diff --git a/BUILD.gn b/BUILD.gn index 3f066a72e79d92d05aa2c71e9613c36ca4ebfbc2..05da0e38aa3c4f2c3db2cc862351c93c34349255 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -20,9 +20,11 @@ group("ark_js_packages") { deps = [] if (host_os != "mac") { deps += [ - "//arkcompiler/ets_runtime:libark_jsruntime", - "//arkcompiler/ets_runtime/ecmascript/js_vm:ark_js_vm", - "//arkcompiler/ets_runtime/ecmascript/quick_fix:quick_fix", + ":libark_jsruntime", + "ecmascript/compiler:stub.an", + "ecmascript/js_vm:ark_js_context", + "ecmascript/js_vm:ark_js_vm", + "ecmascript/quick_fix:quick_fix", ] if (is_clang && clang_version != "9.0.3" && current_cpu == "arm64" && is_ohos) { @@ -38,9 +40,10 @@ group("ark_js_host_windows_tools_packages") { deps = [] if (host_os != "mac" && target_os != "android") { deps += [ + "ecmascript/compiler:ark_aot_compiler($build_root/toolchain/mingw:mingw_x86_64)", + "ecmascript/js_vm:ark_js_context($build_root/toolchain/mingw:mingw_x86_64)", + "ecmascript/js_vm:ark_js_vm($build_root/toolchain/mingw:mingw_x86_64)", "ecmascript/pgo_profiler/prof_dump:profdump($build_root/toolchain/mingw:mingw_x86_64)", - "//arkcompiler/ets_runtime/ecmascript/compiler:ark_aot_compiler($build_root/toolchain/mingw:mingw_x86_64)", - "//arkcompiler/ets_runtime/ecmascript/js_vm:ark_js_vm($build_root/toolchain/mingw:mingw_x86_64)", ] } } @@ -51,12 +54,14 @@ group("ark_js_host_mac_tools_packages") { if (host_cpu == "arm64") { deps += [ "ecmascript/compiler:ark_aot_compiler($build_root/toolchain/mac:clang_arm64)", + "ecmascript/js_vm:ark_js_context($build_root/toolchain/mac:clang_arm64)", "ecmascript/js_vm:ark_js_vm($build_root/toolchain/mac:clang_arm64)", "ecmascript/pgo_profiler/prof_dump:profdump($build_root/toolchain/mac:clang_arm64)", ] } else { deps += [ "ecmascript/compiler:ark_aot_compiler($build_root/toolchain/mac:clang_x64)", + "ecmascript/js_vm:ark_js_context($build_root/toolchain/mac:clang_x64)", "ecmascript/js_vm:ark_js_vm($build_root/toolchain/mac:clang_x64)", "ecmascript/pgo_profiler/prof_dump:profdump($build_root/toolchain/mac:clang_x64)", ] @@ -68,8 +73,9 @@ group("ark_js_host_linux_tools_packages") { deps = [] if (host_os != "mac") { deps += [ - "//arkcompiler/ets_runtime/ecmascript/js_vm:ark_js_vm(${host_toolchain})", - "//arkcompiler/ets_runtime/ecmascript/quick_fix:quick_fix(${host_toolchain})", + "ecmascript/js_vm:ark_js_context(${host_toolchain})", + "ecmascript/js_vm:ark_js_vm(${host_toolchain})", + "ecmascript/quick_fix:quick_fix(${host_toolchain})", ] if (is_standard_system) { deps += [ @@ -370,8 +376,7 @@ config("ark_jsruntime_common_config") { } } - if (!is_mac && target_os != "ios" && !use_libfuzzer && - !ark_standalone_build) { + if (!is_mac && target_os != "ios" && !use_libfuzzer && !enable_lto_O0) { cflags_cc += [ "-flto=thin" ] ldflags += [ "-flto=thin" ] } @@ -448,7 +453,7 @@ config("ark_jsruntime_common_config") { } } - if (enable_leak_check) { + if (enable_leak_check || is_asan) { defines += [ "ECMASCRIPT_ENABLE_HANDLE_LEAK_CHECK" ] } @@ -460,6 +465,11 @@ 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" ] + } } # ecmascript unit testcase config @@ -474,7 +484,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" ] } @@ -504,9 +514,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", @@ -560,6 +573,7 @@ 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/pgo_bc_info.cpp", "ecmascript/containers/containers_arraylist.cpp", "ecmascript/containers/containers_deque.cpp", "ecmascript/containers/containers_errors.cpp", @@ -591,6 +605,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", @@ -707,6 +722,7 @@ ecma_source = [ "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", @@ -718,17 +734,24 @@ 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/stackmap/ark_stackmap_builder.cpp", "ecmascript/stackmap/ark_stackmap_parser.cpp", "ecmascript/stackmap/llvm_stackmap_parser.cpp", @@ -777,6 +800,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", ] @@ -810,7 +834,10 @@ ecma_profiler_source += [ 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) { @@ -908,6 +935,10 @@ 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 @@ -935,6 +966,13 @@ ohos_source_set("libark_jsruntime_set") { deps += [ "$ark_root/libpandafile:arkfile_header_deps" ] } + if (is_ohos && is_standard_system && product_name != "ohos-sdk") { + if (!is_cross_platform_build) { + defines += [ "ENABLE_QOS" ] + external_deps += [ "frame_aware_sched:qos_manager" ] + } + } + if (enable_target_compilation) { external_deps += [ "c_utils:utils" ] } @@ -1018,6 +1056,13 @@ ohos_source_set("libark_jsruntime_test_set") { deps += [ "$ark_root/libpandafile:arkfile_header_deps" ] } + if (is_ohos && is_standard_system && product_name != "ohos-sdk") { + if (!is_cross_platform_build) { + defines += [ "ENABLE_QOS" ] + external_deps += [ "frame_aware_sched:qos_manager" ] + } + } + if (enable_coverage) { ldflags = [ "--coverage" ] cflags_cc = [ "--coverage" ] @@ -1031,7 +1076,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" ] } diff --git a/OAT.xml b/OAT.xml index e43329a83cb94f0407f8bcde3a58db881b779800..b5def44ac6c6c47be5781dcd04038d4cf12b8eb0 100644 --- a/OAT.xml +++ b/OAT.xml @@ -54,8 +54,13 @@ Note:If the text contains special characters, please escape them according to th - + + + + + + diff --git a/bundle.json b/bundle.json index c7af643cffcee1348797ce3be7d19a7394264455..135711abebf03880417b6c7c2165ed7fb4cb8182 100644 --- a/bundle.json +++ b/bundle.json @@ -23,6 +23,7 @@ "deps": { "components": [ "faultloggerd", + "frame_aware_sched", "hitrace", "hilog", "runtime_core", 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 ``` - -

选项

-

缩写

+ + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + + + - - - - - - - - + + + + - + + - - + + - - - + - + + + - - - - - - - - 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 ``` - -

选项

描述

+

描述

取值范围

+

取值范围

默认值

+

默认值

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

-

Option

-

Abbreviation

+If no parameter is specified for **\[options\]**, an ARK binary file is generated by default. + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + + + - - - - - - - - + + + + - + + - - + + - - - + - + + + - - - - - - - - diff --git a/ecmascript/base/array_helper.cpp b/ecmascript/base/array_helper.cpp index 68fe58c692b56d0886fa3f07851d500696037065..152fc47eb3a3cae1bfc1c2cfa1cc96e042cdf82a 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); @@ -111,7 +210,33 @@ double ArrayHelper::SortCompare(JSThread *thread, const JSHandle 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 +312,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..46ec5305f0f38b9f25cd2ee2b595307e92cb9cab 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..489997efc09ccf23448cc1109e252e5b45d20243 100644 --- a/ecmascript/base/error_helper.cpp +++ b/ecmascript/base/error_helper.cpp @@ -128,7 +128,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 +166,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 +192,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 +222,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/fast_json_stringifier.cpp b/ecmascript/base/fast_json_stringifier.cpp new file mode 100644 index 0000000000000000000000000000000000000000..32562748ce8e4838c413a2660ae741ac62494410 --- /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 (!enumCache.IsNull()) { + 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 (!enumCache.IsNull()) { + 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 objHandle(holder); JSHandle val = JSTaggedValue::GetProperty(thread, objHandle, name).GetValue(); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); if (val->IsECMAObject()) { JSHandle obj = JSTaggedValue::ToObject(thread, val); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); bool isArray = val->IsArray(thread); if (isArray) { JSHandle lenResult = JSTaggedValue::GetProperty(thread, val, lengthKey).GetValue(); @@ -38,6 +40,7 @@ JSHandle Internalize::InternalizeJsonProperty(JSThread *thread, c // Let prop be ! ToString((I)). keyUnknow.Update(JSTaggedValue(i)); keyName.Update(JSTaggedValue::ToString(thread, keyUnknow).GetTaggedValue()); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); RecurseAndApply(thread, obj, keyName, receiver); } } else { @@ -54,12 +57,13 @@ JSHandle Internalize::InternalizeJsonProperty(JSThread *thread, c } // Return ? Call(receiver, holder, « name, val »). - const int32_t argsLength = 2; // 2: « name, val » + const uint32_t argsLength = 2; // 2: « name, val » JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, receiver, objHandle, undefined, argsLength); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); info->SetCallArg(name.GetTaggedValue(), val.GetTaggedValue()); JSTaggedValue result = JSFunction::Call(info); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); return JSHandle(thread, result); } diff --git a/ecmascript/base/json_parser.h b/ecmascript/base/json_parser.h index ef23776a6ca2c736e94bd1fe8f9d47bce1ed24fc..720a2af0f7f39e0a8455353905e5a114292e6547 100644 --- a/ecmascript/base/json_parser.h +++ b/ecmascript/base/json_parser.h @@ -16,6 +16,8 @@ #ifndef ECMASCRIPT_BASE_JSON_PARSE_INL_H #define ECMASCRIPT_BASE_JSON_PARSE_INL_H +#include + #include "ecmascript/base/json_parser.h" #include "ecmascript/base/builtins_base.h" #include "ecmascript/base/number_helper.h" @@ -52,16 +54,24 @@ enum class Tokens : uint8_t { template class JsonParser { -public: +protected: using Text = const T *; + // Instantiation of the class is prohibited JsonParser() = default; explicit JsonParser(JSThread *thread) : thread_(thread) {} ~JsonParser() = default; NO_COPY_SEMANTIC(JsonParser); NO_MOVE_SEMANTIC(JsonParser); - JSHandle Parse(Text begin, Text end) + + JSHandle Launch(Text begin, Text end) { - end_ = (end == begin) ? end : end - 1; + // check empty + if (UNLIKELY(begin == end)) { + return JSHandle(thread_, [&]() -> JSTaggedValue { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); + }()); + } + end_ = end - 1; current_ = begin; auto vm = thread_->GetEcmaVM(); @@ -70,62 +80,37 @@ public: SkipEndWhiteSpace(); range_ = end_; - JSTaggedValue result = ParseJSONText(); + JSTaggedValue result = ParseJSONText(); return JSHandle(thread_, result); } - JSHandle ParseUtf8(EcmaString *str) - { - ASSERT(str != nullptr); - isAsciiString_ = true; - uint32_t len = EcmaStringAccessor(str).GetLength(); - ASSERT(len != UINT32_MAX); - CVector buf(len + 1); // 1 means add '\0' in the end of buf - EcmaStringAccessor(str).WriteToFlatUtf8(buf.data(), len + 1); - Text begin = buf.data(); - return Parse(begin, begin + len); - } - - JSHandle ParseUtf16(EcmaString *str) - { - ASSERT(str != nullptr); - uint32_t len = EcmaStringAccessor(str).GetLength(); - CVector buf(len); - EcmaStringAccessor(str).WriteToFlatUtf16(buf.data(), len); - Text begin = buf.data(); - return Parse(begin, begin + len); - } - -private: - template - JSTaggedValue ParseJSONText() + JSTaggedValue ParseJSONText(bool inObjorArr = false) { SkipStartWhiteSpace(); Tokens token = ParseToken(); switch (token) { case Tokens::OBJECT: - return ParseObject(); + return ParseObject(inObjorArr); case Tokens::ARRAY: - return ParseArray(); + return ParseArray(inObjorArr); case Tokens::LITERAL_TRUE: - return ParseLiteral("true", Tokens::LITERAL_TRUE); + return ParseLiteralTrue(); case Tokens::LITERAL_FALSE: - return ParseLiteral("false", Tokens::LITERAL_FALSE); + return ParseLiteralFalse(); case Tokens::LITERAL_NULL: - return ParseLiteral("null", Tokens::LITERAL_NULL); + return ParseLiteralNull(); case Tokens::NUMBER: - return ParseNumber(); + return ParseNumber(inObjorArr); case Tokens::STRING: - return ParseString(); + return ParseString(inObjorArr); default: THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); } } - template - JSTaggedValue ParseNumber() + JSTaggedValue ParseNumber(bool inObjorArr = false) { - if (inObjOrArr) { + if (inObjorArr) { bool isFast = true; bool isNumber = ReadNumberRange(isFast); if (!isNumber) { @@ -134,7 +119,11 @@ private: if (isFast) { std::string strNum(current_, end_ + 1); current_ = end_; - double v = std::stod(strNum); + double v = std::strtod(strNum.c_str(), nullptr); + if (errno == ERANGE) { + errno = 0; + return v > 0 ? JSTaggedValue(base::POSITIVE_INFINITY): JSTaggedValue(-base::POSITIVE_INFINITY); + } return JSTaggedValue::TryCastDoubleToInt32(v); } } @@ -160,66 +149,50 @@ private: std::string strNum(current, end_ + 1); current_ = end_; - double v = std::stod(strNum); - return JSTaggedValue::TryCastDoubleToInt32(v); - } - - bool ReadJsonStringRange(bool &isFastString, bool &isAscii) - { - current_++; - if (isAsciiString_) { - return ReadAsciiStringRange(isFastString); + double v = std::strtod(strNum.c_str(), nullptr); + if (errno == ERANGE) { + errno = 0; + return v > 0 ? JSTaggedValue(base::POSITIVE_INFINITY): JSTaggedValue(-base::POSITIVE_INFINITY); } - return ReadStringRange(isFastString, isAscii); - } - - bool IsFastParseJsonString(bool &isFastString, bool &isAscii) - { - current_++; - if (isAsciiString_) { - return IsFastParseAsciiString(isFastString); - } - return IsFastParseString(isFastString, isAscii); + return JSTaggedValue::TryCastDoubleToInt32(v); } - bool ParseBackslash(CString &res) + bool ParseBackslash(std::string &res) { if (current_ == end_) { return false; } - current_++; + Advance(); switch (*current_) { case '\"': - res += "\""; + res += '\"'; break; case '\\': - res += "\\"; + res += '\\'; break; case '/': - res += "/"; + res += '/'; break; case 'b': - res += "\b"; + res += '\b'; break; case 'f': - res += "\f"; + res += '\f'; break; case 'n': - res += "\n"; + res += '\n'; break; case 'r': - res += "\r"; + res += '\r'; break; case 't': - res += "\t"; + res += '\t'; break; case 'u': { - CVector vec; - if (UNLIKELY(!ConvertStringUnicode(vec))) { + std::u16string u16Str; + if (UNLIKELY(!ConvertStringUnicode(u16Str))) { return false; } - std::u16string u16Str; - u16Str.assign(vec.begin(), vec.end()); res += base::StringHelper::U16stringToString(u16Str); break; } @@ -232,93 +205,40 @@ private: JSTaggedValue SlowParseString() { end_--; - CString res; + std::string res; + res.reserve(end_ - current_); while (current_ <= end_) { if (*current_ == '\\') { bool isLegalChar = ParseBackslash(res); if (!isLegalChar) { THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected string in JSON", JSTaggedValue::Exception()); } - current_++; - } else if (UNLIKELY(*current_ > ASCII_END)) { - if (UNLIKELY(*current_ > utf_helper::DECODE_LEAD_LOW && *current_ < utf_helper::DECODE_LEAD_HIGH && - *(current_ + 1) > utf_helper::DECODE_TRAIL_LOW && - *(current_ + 1) < utf_helper::DECODE_TRAIL_HIGH)) { - std::u16string str(current_, current_ + 2); // 2 means twice as many bytes as normal u16string - res += ConvertToString(StringHelper::U16stringToString(str)); - current_ += 2; // 2 means twice as many bytes as normal u16string - } else { - std::u16string str(current_, current_ + 1); - res += ConvertToString(StringHelper::U16stringToString(str)); - current_++; - } + Advance(); } else { - res += *current_; - current_++; + Text nextCurrent = current_; + while (nextCurrent <= end_ && *nextCurrent != '\\') { + ++nextCurrent; + } + ParticalParseString(res, current_, nextCurrent); + current_ = nextCurrent; } } - ASSERT(res.length() <= static_cast(UINT32_MAX)); - return factory_->NewFromUtf8Literal(reinterpret_cast(res.c_str()), res.length()) + ASSERT(res.size() <= static_cast(UINT32_MAX)); + return factory_->NewFromUtf8Literal(reinterpret_cast(res.c_str()), res.size()) .GetTaggedValue(); } - template - JSTaggedValue ParseString() - { - bool isFastString = true; - bool isAscii = true; - bool isLegal = true; - if (inObjorArr) { - isLegal = ReadJsonStringRange(isFastString, isAscii); - if (!isLegal) { - THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected end Text in JSON", JSTaggedValue::Exception()); - } - if (isFastString) { - if (isAscii) { - CString value(current_, end_); - current_ = end_; - ASSERT(value.length() <= static_cast(UINT32_MAX)); - return factory_->NewFromUtf8LiteralCompress( - reinterpret_cast(value.c_str()), value.length()).GetTaggedValue(); - } - std::u16string value(current_, end_); - current_ = end_; - ASSERT(value.length() <= static_cast(UINT32_MAX)); - return factory_->NewFromUtf16LiteralNotCompress( - reinterpret_cast(value.c_str()), value.length()).GetTaggedValue(); - } - } else { - if (*end_ != '"' || current_ == end_) { - THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected end Text in JSON", JSTaggedValue::Exception()); - } - isLegal = IsFastParseJsonString(isFastString, isAscii); - if (!isLegal) { - THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected end Text in JSON", JSTaggedValue::Exception()); - } - if (LIKELY(isFastString)) { - if (isAscii) { - CString value(current_, end_); - ASSERT(value.length() <= static_cast(UINT32_MAX)); - return factory_->NewFromUtf8LiteralCompress( - reinterpret_cast(value.c_str()), value.length()).GetTaggedValue(); - } - std::u16string value(current_, end_); - ASSERT(value.length() <= static_cast(UINT32_MAX)); - return factory_->NewFromUtf16LiteralNotCompress( - reinterpret_cast(value.c_str()), value.length()).GetTaggedValue(); - } - } - return SlowParseString(); - } + virtual void ParticalParseString(std::string& str, Text current, Text nextCurrent) = 0; - template - JSTaggedValue ParseArray() + virtual JSTaggedValue ParseString(bool inObjorArr = false) = 0; + + JSTaggedValue ParseArray(bool inObjorArr = false) { if (UNLIKELY(*range_ != ']' && !inObjorArr)) { THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Array in JSON", JSTaggedValue::Exception()); } - current_++; + Advance(); SkipStartWhiteSpace(); JSHandle arr = factory_->NewJSArray(); if (*current_ == ']') { @@ -328,12 +248,12 @@ private: JSTaggedValue value; uint32_t index = 0; while (current_ <= range_) { - value = ParseJSONText(); + value = ParseJSONText(true); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); ObjectFastOperator::SetPropertyByIndex(thread_, arr.GetTaggedValue(), index++, value); GetNextNonSpaceChar(); if (*current_ == ',') { - current_++; + Advance(); } else if (*current_ == ']') { if (inObjorArr || current_ == range_) { return arr.GetTaggedValue(); @@ -345,8 +265,7 @@ private: THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Array in JSON", JSTaggedValue::Exception()); } - template - JSTaggedValue ParseObject() + JSTaggedValue ParseObject(bool inObjorArr = false) { if (UNLIKELY(*range_ != '}' && !inObjorArr)) { THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON", JSTaggedValue::Exception()); @@ -354,7 +273,7 @@ private: JSHandle proto(env_->GetObjectFunction()); JSHandle result = factory_->NewJSObjectByConstructor(proto); - current_++; + Advance(); if (*current_ == '}') { return result.GetTaggedValue(); } @@ -364,7 +283,8 @@ private: while (current_ <= range_) { SkipStartWhiteSpace(); if (*current_ == '"') { - keyHandle.Update(ParseString()); + keyHandle.Update(ParseString(true)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); } else { if (*current_ == '}' && (inObjorArr || current_ == range_)) { return result.GetTaggedValue(); @@ -373,11 +293,12 @@ private: } GetNextNonSpaceChar(); if (*current_ == ':') { - current_++; + Advance(); } else { THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Object in JSON", JSTaggedValue::Exception()); } - value = ParseJSONText(); + value = ParseJSONText(true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); // fast path JSTaggedValue res = ObjectFastOperator::SetPropertyByValue(thread_, result.GetTaggedValue(), keyHandle.GetTaggedValue(), value); @@ -385,10 +306,11 @@ private: // slow path JSTaggedValue::SetProperty(thread_, JSHandle(result), keyHandle, JSHandle(thread_, value), true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); } GetNextNonSpaceChar(); if (*current_ == ',') { - current_++; + Advance(); } else if (*current_ == '}') { if (inObjorArr || current_ == range_) { return result.GetTaggedValue(); @@ -415,7 +337,7 @@ private: { while (current_ != end_) { if (*current_ == ' ' || *current_ == '\r' || *current_ == '\n' || *current_ == '\t') { - current_++; + Advance(); } else { break; } @@ -424,7 +346,7 @@ private: void GetNextNonSpaceChar() { - current_++; + Advance(); SkipStartWhiteSpace(); } @@ -460,41 +382,55 @@ private: } } - JSTaggedValue ParseLiteral(CString str, Tokens literalToken) + JSTaggedValue ParseLiteralTrue() { - ASSERT((str.size() - 1) <= static_cast(UINT32_MAX)); - uint32_t strLen = str.size() - 1; + static const char literalTrue[] = "true"; uint32_t remainingLength = range_ - current_; - if (UNLIKELY(remainingLength < strLen)) { + if (UNLIKELY(remainingLength < 3)) { // 3: literalTrue length - 1 THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); } + bool isMatch = MatchText(literalTrue, 3); // 3: literalTrue length - 1 + if (LIKELY(isMatch)) { + return JSTaggedValue::True(); + } + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); + } - bool isMatch = MatchText(str, strLen); + JSTaggedValue ParseLiteralFalse() + { + static const char literalFalse[] = "false"; + uint32_t remainingLength = range_ - current_; + if (UNLIKELY(remainingLength < 4)) { // 4: literalFalse length - 1 + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); + } + bool isMatch = MatchText(literalFalse, 4); // 4: literalFalse length - 1 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: - LOG_ECMA(FATAL) << "this branch is unreachable"; - UNREACHABLE(); - } + return JSTaggedValue::False(); + } + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); + } + + JSTaggedValue ParseLiteralNull() + { + static const char literalNull[] = "null"; + uint32_t remainingLength = range_ - current_; + if (UNLIKELY(remainingLength < 3)) { // 3: literalNull length - 1 + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); + } + bool isMatch = MatchText(literalNull, 3); // 3: literalNull length - 1 + if (LIKELY(isMatch)) { + return JSTaggedValue::Null(); } THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected Text in JSON", JSTaggedValue::Exception()); } - bool MatchText(CString str, uint32_t matchLen) + bool MatchText(const char *str, uint32_t matchLen) { - const char *text = str.c_str(); - uint32_t pos = 1; - while (pos <= matchLen) { - if (current_[pos] != text[pos]) { + // first char is already matched + for (uint32_t pos = 1; pos <= matchLen; ++pos) { + if (current_[pos] != str[pos]) { return false; } - pos++; } current_ += matchLen; return true; @@ -565,7 +501,7 @@ private: if (current_ == end_) { return false; } - current_++; + Advance(); if (IsNumberCharacter(*current_)) { return true; } @@ -580,14 +516,14 @@ private: } while (current_ != end_) { - current_++; + Advance(); if (IsNumberCharacter(*current_)) { continue; } else if (*current_ == 'e' || *current_ == 'E') { if (hasExponent || current_ == end_) { return false; } - current_++; + Advance(); if (!IsExponentNumber()) { return false; } @@ -604,7 +540,7 @@ private: if (hasExponent || current_ == end_) { return false; } - current_++; + Advance(); if (!IsExponentNumber()) { return false; } @@ -612,111 +548,46 @@ private: if (!IsNumberCharacter(*current_)) { return false; } - current_++; + Advance(); } return true; } - bool ReadStringRange(bool &isFast, bool &isAscii) + bool ConvertStringUnicode(std::u16string &u16Str) { - T c = 0; - Text current = current_; - - while (current != range_) { - c = *current; - if (c == '"') { - end_ = current; - return true; - } else if (UNLIKELY(c == '\\')) { - current++; - isFast = false; - } - if (!IsLegalAsciiCharacter(c, isAscii)) { + do { + uint32_t remainingLength = end_ - current_; + if (remainingLength < UNICODE_DIGIT_LENGTH) { return false; } - current++; - } - return false; - } - - bool ReadAsciiStringRange(bool &isFast) - { - T c = 0; - Text current = current_; - - while (current != range_) { - c = *current; - if (c == '"') { - end_ = current; - return true; - } else if (UNLIKELY(c == '\\')) { - current++; - isFast = false; - } else if (UNLIKELY(c < CODE_SPACE)) { - return false; + uint16_t res = 0; + for (uint32_t pos = 0; pos < UNICODE_DIGIT_LENGTH; pos++) { + Advance(); + if (*current_ >= '0' && *current_ <= '9') { + res *= NUMBER_SIXTEEN; + res += (*current_ - '0'); + } else if (*current_ >= 'a' && *current_ <= 'f') { + res *= NUMBER_SIXTEEN; + res += (*current_ - 'a' + NUMBER_TEN); + } else if (*current_ >= 'A' && *current_ <= 'F') { + res *= NUMBER_SIXTEEN; + res += (*current_ - 'A' + NUMBER_TEN); + } else { + return false; + } } - current++; - } - return false; - } - - bool IsFastParseString(bool &isFast, bool &isAscii) - { - Text current = current_; - while (current != end_) { - if (!IsLegalAsciiCharacter(*current, isAscii)) { + u16Str.push_back(res); + } while ([&]() -> bool { + static const int unicodePrefixLen = 2; + if (end_ - current_ < unicodePrefixLen) { return false; } - if (*current == '\\') { - isFast = false; - } - current++; - } - return true; - } - - bool IsFastParseAsciiString(bool &isFast) - { - Text current = current_; - while (current != end_) { - if (*current < CODE_SPACE) { - return false; - } else if (*current == '\\') { - isFast = false; + if (*(current_ + 1) == '\\' && *(current_ + unicodePrefixLen) == 'u') { + AdvanceMultiStep(unicodePrefixLen); + return true; } - current++; - } - return true; - } - - bool ConvertStringUnicode(CVector &vec) - { - uint32_t remainingLength = end_ - current_; - if (remainingLength < 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; - } - } - - 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; } @@ -741,7 +612,7 @@ private: bool CheckNonZeroBeginNumber(bool &hasExponent) { while (current_ != end_) { - current_++; + Advance(); if (IsNumberCharacter(*current_)) { continue; } else if (*current_ == '.') { @@ -759,19 +630,16 @@ private: return true; } - bool IsLegalAsciiCharacter(T c, bool &isAscii) + inline void Advance() { - if (c <= ASCII_END) { - if (c >= CODE_SPACE) { - return true; - } - return false; - } - isAscii = false; - return true; + ++current_; + } + + inline void AdvanceMultiStep(int step) + { + current_ += step; } - bool isAsciiString_ {false}; Text end_ {nullptr}; Text current_ {nullptr}; Text range_ {nullptr}; @@ -780,6 +648,214 @@ private: GlobalEnv *env_ {nullptr}; }; +class Utf8JsonParser : public JsonParser { +public: + Utf8JsonParser() = default; + explicit Utf8JsonParser(JSThread *thread) : JsonParser(thread) {} + ~Utf8JsonParser() = default; + NO_COPY_SEMANTIC(Utf8JsonParser); + NO_MOVE_SEMANTIC(Utf8JsonParser); + + JSHandle Parse(EcmaString *str) + { + ASSERT(str != nullptr); + uint32_t len = EcmaStringAccessor(str).GetLength(); + ASSERT(len != UINT32_MAX); + CVector buf(len); + EcmaStringAccessor(str).WriteToFlatUtf8(buf.data(), len); + Text begin = buf.data(); + return Launch(begin, begin + len); + } + +private: + void ParticalParseString(std::string& str, Text current, Text nextCurrent) override + { + str += std::string_view(reinterpret_cast(current), nextCurrent - current); + } + + JSTaggedValue ParseString(bool inObjorArr = false) override + { + bool isFastString = true; + bool isLegal = true; + if (inObjorArr) { + isLegal = ReadJsonStringRange(isFastString); + if (!isLegal) { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected end Text in JSON", JSTaggedValue::Exception()); + } + if (isFastString) { + std::string_view value(reinterpret_cast(current_), end_ - current_); + current_ = end_; + ASSERT(value.size() <= static_cast(UINT32_MAX)); + return factory_->NewFromUtf8LiteralCompress( + reinterpret_cast(value.data()), value.size()).GetTaggedValue(); + } + } else { + if (*end_ != '"' || current_ == end_) { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected end Text in JSON", JSTaggedValue::Exception()); + } + isLegal = IsFastParseJsonString(isFastString); + if (!isLegal) { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected end Text in JSON", JSTaggedValue::Exception()); + } + if (LIKELY(isFastString)) { + std::string_view value(reinterpret_cast(current_), end_ - current_); + ASSERT(value.size() <= static_cast(UINT32_MAX)); + return factory_->NewFromUtf8LiteralCompress( + reinterpret_cast(value.data()), value.size()).GetTaggedValue(); + } + } + return SlowParseString(); + } + + bool ReadJsonStringRange(bool &isFastString) + { + Advance(); + // chars are within Ascii + for (Text current = current_; current != range_; ++current) { + uint8_t c = *current; + if (c == '"') { + end_ = current; + return true; + } else if (UNLIKELY(c == '\\')) { + current++; + isFastString = false; + } else if (UNLIKELY(c < CODE_SPACE)) { + return false; + } + } + return false; + } + + bool IsFastParseJsonString(bool &isFastString) + { + Advance(); + // chars are within Ascii + for (Text current = current_; current != end_; ++current) { + if (*current < CODE_SPACE) { + return false; + } else if (*current == '\\') { + isFastString = false; + } + } + return true; + } +}; + +class Utf16JsonParser : public JsonParser { +public: + Utf16JsonParser() = default; + explicit Utf16JsonParser(JSThread *thread) : JsonParser(thread) {} + ~Utf16JsonParser() = default; + NO_COPY_SEMANTIC(Utf16JsonParser); + NO_MOVE_SEMANTIC(Utf16JsonParser); + + JSHandle Parse(EcmaString *str) + { + ASSERT(str != nullptr); + uint32_t len = EcmaStringAccessor(str).GetLength(); + CVector buf(len); + EcmaStringAccessor(str).WriteToFlatUtf16(buf.data(), len); + Text begin = buf.data(); + return Launch(begin, begin + len); + } + +private: + void ParticalParseString(std::string& str, Text current, Text nextCurrent) override + { + str += StringHelper::U16stringToString(std::u16string(current, nextCurrent)); + } + + JSTaggedValue ParseString(bool inObjorArr = false) override + { + bool isFastString = true; + bool isAscii = true; + bool isLegal = true; + if (inObjorArr) { + isLegal = ReadJsonStringRange(isFastString, isAscii); + if (!isLegal) { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected end Text in JSON", JSTaggedValue::Exception()); + } + if (isFastString) { + if (isAscii) { + std::string value(current_, end_); // from uint16_t* to std::string, can't use std::string_view + current_ = end_; + ASSERT(value.size() <= static_cast(UINT32_MAX)); + return factory_->NewFromUtf8LiteralCompress( + reinterpret_cast(value.c_str()), value.size()).GetTaggedValue(); + } + std::u16string_view value(reinterpret_cast(current_), end_ - current_); + current_ = end_; + ASSERT(value.size() <= static_cast(UINT32_MAX)); + return factory_->NewFromUtf16LiteralNotCompress( + reinterpret_cast(value.data()), value.size()).GetTaggedValue(); + } + } else { + if (*end_ != '"' || current_ == end_) { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected end Text in JSON", JSTaggedValue::Exception()); + } + isLegal = IsFastParseJsonString(isFastString, isAscii); + if (!isLegal) { + THROW_SYNTAX_ERROR_AND_RETURN(thread_, "Unexpected end Text in JSON", JSTaggedValue::Exception()); + } + if (LIKELY(isFastString)) { + if (isAscii) { + std::string value(current_, end_); // from uint16_t* to std::string, can't use std::string_view + ASSERT(value.size() <= static_cast(UINT32_MAX)); + return factory_->NewFromUtf8LiteralCompress( + reinterpret_cast(value.c_str()), value.size()).GetTaggedValue(); + } + std::u16string_view value(reinterpret_cast(current_), end_ - current_); + ASSERT(value.size() <= static_cast(UINT32_MAX)); + return factory_->NewFromUtf16LiteralNotCompress( + reinterpret_cast(value.data()), value.size()).GetTaggedValue(); + } + } + return SlowParseString(); + } + + bool ReadJsonStringRange(bool &isFastString, bool &isAscii) + { + Advance(); + for (Text current = current_; current != range_; ++current) { + uint16_t c = *current; + if (c == '"') { + end_ = current; + return true; + } else if (UNLIKELY(c == '\\')) { + ++current; + isFastString = false; + } + if (!IsLegalAsciiCharacter(c, isAscii)) { + return false; + } + } + return false; + } + + bool IsFastParseJsonString(bool &isFastString, bool &isAscii) + { + Advance(); + for (Text current = current_; current != end_; ++current) { + if (!IsLegalAsciiCharacter(*current, isAscii)) { + return false; + } + if (*current == '\\') { + isFastString = false; + } + } + return true; + } + + bool IsLegalAsciiCharacter(uint16_t c, bool &isAscii) + { + if (c <= ASCII_END) { + return c >= CODE_SPACE ? true : false; + } + isAscii = false; + return true; + } +}; + class Internalize { public: static JSHandle InternalizeJsonProperty(JSThread *thread, const JSHandle &holder, diff --git a/ecmascript/base/json_stringifier.cpp b/ecmascript/base/json_stringifier.cpp index 133f91feaa8b9258f5ec7fcd66445138f642424e..1b16d9aa94a0c07ff4a56a4d8c17944fed5f8973 100644 --- a/ecmascript/base/json_stringifier.cpp +++ b/ecmascript/base/json_stringifier.cpp @@ -15,11 +15,8 @@ #include "ecmascript/base/json_stringifier.h" -#include -#include -#include - #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" @@ -36,109 +33,7 @@ #include "ecmascript/object_fast_operator-inl.h" namespace panda::ecmascript::base { -constexpr unsigned char CODE_SPACE = 0x20; constexpr int GAP_MAX_LEN = 10; -constexpr int FOUR_HEX = 4; -constexpr char ZERO_FIRST = static_cast(0xc0); // \u0000 => c0 80 - -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) || *value == ZERO_FIRST) { - 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; - 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; -} JSHandle JsonStringifier::Stringify(const JSHandle &value, const JSHandle &replacer, @@ -313,7 +208,7 @@ JSTaggedValue JsonStringifier::GetSerializeValue(const JSHandle & if (UNLIKELY(replacer->IsCallable())) { handleValue_.Update(tagValue); // a. Let value be Call(ReplacerFunction, holder, «key, value»). - const int32_t argsLength = 2; // 2: «key, value» + const uint32_t argsLength = 2; // 2: «key, value» EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread_, replacer, object, undefined, argsLength); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); @@ -369,12 +264,13 @@ JSTaggedValue JsonStringifier::SerializeJSONProperty(const JSHandle strHandle = JSHandle(valHandle); auto string = JSHandle(thread_, EcmaStringAccessor::Flatten(thread_->GetEcmaVM(), strHandle)); CString str = ConvertToString(*string, StringConvertedUsage::LOGICOPERATION); - str = ValueToQuotedString(str); + str = JsonHelper::ValueToQuotedString(str); result_ += str; return tagValue; } @@ -428,7 +324,7 @@ void JsonStringifier::SerializeObjectKey(const JSHandle &key, boo str = ConvertToString(*JSTaggedValue::ToString(thread_, key), StringConvertedUsage::LOGICOPERATION); } result_ += stepBegin; - str = ValueToQuotedString(str); + str = JsonHelper::ValueToQuotedString(str); result_ += str; result_ += ":"; result_ += stepEnd; @@ -477,6 +373,7 @@ bool JsonStringifier::SerializeJSONObject(const JSHandle &value, 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); JSTaggedValue serializeValue = GetSerializeValue(value, handleKey_, valueHandle, replacer); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); if (UNLIKELY(serializeValue.IsUndefined() || serializeValue.IsSymbol() || @@ -608,6 +505,7 @@ bool JsonStringifier::SerializeJSArray(const JSHandle &value, con 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); } @@ -647,7 +545,7 @@ void JsonStringifier::SerializePrimitiveRef(const JSHandle &primi auto priStr = JSTaggedValue::ToString(thread_, primitiveRef); RETURN_IF_ABRUPT_COMPLETION(thread_); CString str = ConvertToString(*priStr, StringConvertedUsage::LOGICOPERATION); - str = ValueToQuotedString(str); + str = JsonHelper::ValueToQuotedString(str); result_ += str; } else if (primitive.IsNumber()) { auto priNum = JSTaggedValue::ToNumber(thread_, primitiveRef); @@ -692,7 +590,7 @@ bool JsonStringifier::SerializeElements(const JSHandle &obj, const JSH } } } - std::sort(sortArr.begin(), sortArr.end(), CompareNumber); + std::sort(sortArr.begin(), sortArr.end(), JsonHelper::CompareNumber); for (const auto &entry : sortArr) { JSTaggedValue entryKey = entry.GetTaggedValue(); handleKey_.Update(entryKey); @@ -715,6 +613,7 @@ bool JsonStringifier::SerializeKeys(const JSHandle &obj, const JSHandl { JSHandle propertiesArr(thread_, obj->GetProperties()); if (!propertiesArr->IsDictionaryMode()) { + bool hasChangedToDictionaryMode = false; JSHandle jsHclass(thread_, obj->GetJSHClass()); JSTaggedValue enumCache = jsHclass->GetEnumCache(); if (!enumCache.IsNull()) { @@ -732,7 +631,7 @@ bool JsonStringifier::SerializeKeys(const JSHandle &obj, const JSHandl PropertyAttributes attr(layoutInfo->GetAttr(index)); ASSERT(static_cast(attr.GetOffset()) == index); value = attr.IsInlinedProps() - ? obj->GetPropertyInlinedProps(static_cast(index)) + ? obj->GetPropertyInlinedPropsWithRep(static_cast(index), attr) : propertiesArr->Get(static_cast(index) - jsHclass->GetInlinedProperties()); if (attr.IsInlinedProps() && value.IsHole()) { continue; @@ -754,25 +653,50 @@ bool JsonStringifier::SerializeKeys(const JSHandle &obj, const JSHandl 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->GetPropertyInlinedProps(static_cast(index)) - : 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 = JsonStringifier::AppendJsonString(obj, replacer, hasContent); - RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + if (!hasChangedToDictionaryMode) { + 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)); + if (obj->GetProperties().IsDictionary()) { + hasChangedToDictionaryMode = true; + propertiesArr = JSHandle(thread_, obj->GetProperties()); + } + } + handleValue_.Update(value); + hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); + } + } else { + JSHandle nameDic(propertiesArr); + int index = nameDic->FindEntry(key); + if (!key.IsString()) { + continue; + } + PropertyAttributes attr = nameDic->GetAttributes(index); + if (!attr.IsEnumerable()) { + continue; + } + JSTaggedValue value = nameDic->GetValue(index); + handleKey_.Update(key); + if (UNLIKELY(value.IsAccessor())) { + value = JSObject::CallGetter(thread_, AccessorData::Cast(value.GetTaggedObject()), + JSHandle(obj)); + } + handleValue_.Update(value); + hasContent = JsonStringifier::AppendJsonString(obj, replacer, hasContent); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false); } } return hasContent; @@ -793,7 +717,7 @@ bool JsonStringifier::SerializeKeys(const JSHandle &obj, const JSHandl std::pair, PropertyAttributes> pair(JSHandle(thread_, key), attr); sortArr.emplace_back(pair); } - std::sort(sortArr.begin(), sortArr.end(), CompareKey); + std::sort(sortArr.begin(), sortArr.end(), JsonHelper::CompareKey); for (const auto &entry : sortArr) { JSTaggedValue entryKey = entry.first.GetTaggedValue(); handleKey_.Update(entryKey); @@ -824,7 +748,7 @@ bool JsonStringifier::SerializeKeys(const JSHandle &obj, const JSHandl std::pair, PropertyAttributes> pair(JSHandle(thread_, key), attr); sortArr.emplace_back(pair); } - std::sort(sortArr.begin(), sortArr.end(), CompareKey); + std::sort(sortArr.begin(), sortArr.end(), JsonHelper::CompareKey); for (const auto &entry : sortArr) { JSTaggedValue entryKey = entry.first.GetTaggedValue(); handleKey_.Update(entryKey); diff --git a/ecmascript/base/json_stringifier.h b/ecmascript/base/json_stringifier.h index eba164e0b519820d23542532ebaff29728d3840e..d90189f4d7aa171549dc1b044cb003df7774cbd9 100644 --- a/ecmascript/base/json_stringifier.h +++ b/ecmascript/base/json_stringifier.h @@ -66,17 +66,6 @@ private: bool SerializeElements(const JSHandle &obj, const JSHandle &replacer, bool hasContent); bool SerializeKeys(const JSHandle &obj, const JSHandle &replacer, bool hasContent); - 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(); - } - CString gap_; CString result_; CString indent_; diff --git a/ecmascript/base/number_helper.cpp b/ecmascript/base/number_helper.cpp index e3c3a11034d866885cb0922401b6f7042070105e..38c421fd11048497511753683afef84d136c3c6b 100644 --- a/ecmascript/base/number_helper.cpp +++ b/ecmascript/base/number_helper.cpp @@ -90,32 +90,79 @@ bool NumberHelper::IsEmptyString(const uint8_t *start, const uint8_t *end) JSTaggedValue NumberHelper::DoubleToString(JSThread *thread, double number, int radix) { - bool negative = false; - if (number < 0.0) { - negative = true; + static constexpr int BUFFER_SIZE = 2240; // 2240: The size of the character array buffer + static constexpr int HALF_BUFFER_SIZE = BUFFER_SIZE >> 1; + char buffer[BUFFER_SIZE]; + size_t integerCursor = HALF_BUFFER_SIZE; + size_t fractionCursor = integerCursor; + + bool negative = number < 0.0; + if (negative) { number = -number; } - double numberInteger = std::floor(number); - double numberFraction = number - numberInteger; + double integer = std::floor(number); + double fraction = number - integer; auto value = bit_cast(number); value += 1; double delta = HALF * (bit_cast(value) - number); - - CString result; - if (numberFraction != 0 && numberFraction >= delta) { - result += "."; - result += DecimalsToString(&numberInteger, numberFraction, radix, delta); + if (fraction != 0 && fraction >= delta) { + buffer[fractionCursor++] = '.'; + while (fraction >= delta) { + fraction *= radix; + delta *= radix; + int64_t digit = std::floor(fraction); + fraction -= digit; + buffer[fractionCursor++] = CHARS[digit]; + bool needCarry = (fraction > HALF) && (fraction + delta > 1); + if (needCarry) { + size_t fractionEnd = fractionCursor - 1; + buffer[fractionEnd] = Carry(buffer[fractionEnd], radix); + for (; fractionEnd > HALF_BUFFER_SIZE; fractionEnd--) { + if (buffer[fractionEnd] == '0') { + buffer[fractionEnd - 1] = Carry(buffer[fractionEnd - 1], radix); + } else { + break; + } + } + if (fractionEnd == HALF_BUFFER_SIZE) { + ++integer; + } + break; + } + } + // delete 0 in the end + size_t fractionEnd = fractionCursor - 1; + while (buffer[fractionEnd] == '0') { + --fractionEnd; + } + fractionCursor = fractionEnd + 1; } - result = IntegerToString(numberInteger, radix) + result; + ASSERT(radix >= MIN_RADIX && radix <= MAX_RADIX); + while (integer / radix > MAX_MANTISSA) { + integer /= radix; + buffer[--integerCursor] = '0'; + } + do { + double remainder = std::fmod(integer, radix); + buffer[--integerCursor] = CHARS[static_cast(remainder)]; + integer = (integer - remainder) / radix; + } while (integer > 0); if (negative) { - result = "-" + result; + buffer[--integerCursor] = '-'; } + buffer[fractionCursor++] = '\0'; - return BuiltinsBase::GetTaggedString(thread, result.c_str()); + size_t size = fractionCursor - integerCursor; + std::unique_ptr result = std::make_unique(size); + if (memcpy_s(result.get(), size, buffer + integerCursor, size) != EOK) { + LOG_FULL(FATAL) << "memcpy_s failed"; + UNREACHABLE(); + } + return BuiltinsBase::GetTaggedString(thread, result.get()); } JSTaggedValue NumberHelper::DoubleToExponential(JSThread *thread, double number, int digit) @@ -275,43 +322,15 @@ CString NumberHelper::IntegerToString(double number, int radix) return result; } -CString NumberHelper::DecimalsToString(double *numberInteger, double fraction, int radix, double delta) +CString NumberHelper::IntToString(int number) { - 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; + return ToCString(number); } -CString NumberHelper::IntToString(int number) +JSHandle NumberHelper::IntToEcmaString(const JSThread *thread, int number) { - return ToCString(number); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + return factory->NewFromASCII(ToCString(number)); } // 7.1.12.1 ToString Applied to the Number Type @@ -769,6 +788,9 @@ JSTaggedValue NumberHelper::StringToBigInt(JSThread *thread, JSHandle::max(); static constexpr double MIN_VALUE = std::numeric_limits::min(); static constexpr double POSITIVE_INFINITY = std::numeric_limits::infinity(); static constexpr double NAN_VALUE = std::numeric_limits::quiet_NaN(); +static constexpr uint64_t MAX_UINT64_VALUE = std::numeric_limits::max(); // Helper defines for double static constexpr int DOUBLE_MAX_PRECISION = 17; @@ -65,6 +66,12 @@ static constexpr size_t INT16_BITS = 16; static constexpr size_t INT8_BITS = 8; static constexpr size_t JS_DTOA_BUF_SIZE = 128; +// Max number of hexadecimal digits to display an integer +static constexpr size_t INT64_HEX_DIGITS = INT64_BITS / 4; +static constexpr size_t INT32_HEX_DIGITS = INT32_BITS / 4; +static constexpr size_t INT16_HEX_DIGITS = INT16_BITS / 4; +static constexpr size_t INT8_HEX_DIGITS = INT8_BITS / 4; + // help defines for random static constexpr int RIGHT12 = 12; static constexpr int SECONDS_TO_SUBTLE = 1000000; @@ -96,6 +103,7 @@ public: } static JSTaggedValue DoubleToString(JSThread *thread, double number, int radix); static bool IsEmptyString(const uint8_t *start, const uint8_t *end); + static JSHandle IntToEcmaString(const JSThread *thread, int number); static JSHandle NumberToString(const JSThread *thread, JSTaggedValue number); static double TruncateDouble(double d); static int64_t DoubleToInt64(double d); @@ -113,7 +121,6 @@ public: private: static char Carry(char current, int radix); static double Strtod(const char *str, int exponent, uint8_t radix); - static CString DecimalsToString(double *numberInteger, double fraction, int radix, double delta); static bool GotoNonspace(uint8_t **ptr, const uint8_t *end); static void GetBase(double d, int digits, int *decpt, char *buf, char *bufTmp, int size); static int GetMinmumDigits(double d, int *decpt, char *buf); diff --git a/ecmascript/base/path_helper.cpp b/ecmascript/base/path_helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2bc6d00639a870878078f2df7e590d229fbadfc7 --- /dev/null +++ b/ecmascript/base/path_helper.cpp @@ -0,0 +1,74 @@ +/* + * 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/path_helper.h" + +namespace panda::ecmascript::base { +/* + * Before: ./xxx/../xxx/xxx/ + * After: xxx/xxx + */ +CString PathHelper::NormalizePath(const CString &fileName) +{ + if (fileName.find(DOUBLE_SLASH_TAG) == CString::npos && + fileName.find(CURRENT_DIREATORY_TAG) == CString::npos && + fileName[fileName.size() - 1] != SLASH_TAG) { + return fileName; + } + CString res = ""; + size_t prev = 0; + size_t curr = fileName.find(SLASH_TAG); + CVector elems; + // eliminate parent directory path + while (curr != CString::npos) { + if (curr > prev) { + CString elem = fileName.substr(prev, curr - prev); + if (elem == DOUBLE_POINT_TAG && !elems.empty()) { // looking for xxx/../ + elems.pop_back(); + } else if (elem != POINT_STRING_TAG && elem != DOUBLE_POINT_TAG) { // remove ./ ../ + elems.push_back(elem); + } + } + prev = curr + 1; + curr = fileName.find(SLASH_TAG, prev); + } + if (prev != fileName.size()) { + elems.push_back(fileName.substr(prev)); + } + for (auto e : elems) { + if (res.size() == 0 && fileName.at(0) != SLASH_TAG) { + res.append(e); + continue; + } + res.append(1, SLASH_TAG).append(e); + } + return res; +} + +/* + * Before: xxx/xxx + * After: xxx/ + */ +JSHandle PathHelper::ResolveDirPath(JSThread *thread, CString fileName) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // find last '/', '\\' + int foundPos = static_cast(fileName.find_last_of("/\\")); + if (foundPos == -1) { + return factory->NewFromUtf8(""); + } + CString dirPathStr = fileName.substr(0, foundPos + 1); + return factory->NewFromUtf8(dirPathStr); +} +} // namespace panda::ecmascript::base \ No newline at end of file diff --git a/ecmascript/base/path_helper.h b/ecmascript/base/path_helper.h index 281223e5d9a6a5d8a9a302c678ef56d2463c7d38..5ff0fb9bb965c8d78261b986a76a889c3f4eb049 100644 --- a/ecmascript/base/path_helper.h +++ b/ecmascript/base/path_helper.h @@ -27,157 +27,23 @@ namespace panda::ecmascript::base { class PathHelper { public: - static constexpr char EXT_NAME_ABC[] = ".abc"; - static constexpr char EXT_NAME_ETS[] = ".ets"; - static constexpr char EXT_NAME_TS[] = ".ts"; - static constexpr char EXT_NAME_JS[] = ".js"; - static constexpr char EXT_NAME_JSON[] = ".json"; - static constexpr char PREFIX_BUNDLE[] = "@bundle:"; - static constexpr char PREFIX_MODULE[] = "@module:"; - static constexpr char PREFIX_PACKAGE[] = "@package:"; - static constexpr char REQUIRE_NAITVE_MODULE_PREFIX[] = "@native:"; - static constexpr char REQUIRE_NAPI_OHOS_PREFIX[] = "@ohos:"; - static constexpr char REQUIRE_NAPI_APP_PREFIX[] = "@app:"; - static constexpr char NPM_PATH_SEGMENT[] = "node_modules"; - static constexpr char PACKAGE_PATH_SEGMENT[] = "pkg_modules"; - static constexpr char PACKAGE_ENTRY_FILE[] = "/index"; - static constexpr char BUNDLE_INSTALL_PATH[] = "/data/storage/el1/bundle/"; - static constexpr char MERGE_ABC_ETS_MODULES[] = "/ets/modules.abc"; - static constexpr char MODULE_DEFAULE_ETS[] = "/ets/"; - static constexpr char BUNDLE_SUB_INSTALL_PATH[] = "/data/storage/el1/"; - static constexpr char PREVIEW_OF_ACROSS_HAP_FLAG[] = "[preview]"; - static constexpr char NAME_SPACE_TAG[] = "@"; - static constexpr char PREVIER_TEST_DIR[] = ".test"; - - static constexpr size_t MAX_PACKAGE_LEVEL = 1; - static constexpr size_t SEGMENTS_LIMIT_TWO = 2; - static constexpr size_t EXT_NAME_ABC_LEN = 4; - static constexpr size_t EXT_NAME_ETS_LEN = 4; - static constexpr size_t EXT_NAME_TS_LEN = 3; - static constexpr size_t EXT_NAME_JS_LEN = 3; - static constexpr size_t EXT_NAME_JSON_LEN = 5; - static constexpr size_t PREFIX_BUNDLE_LEN = 8; - static constexpr size_t PREFIX_MODULE_LEN = 8; - static constexpr size_t PREFIX_PACKAGE_LEN = 9; - static constexpr size_t NATIVE_PREFIX_SIZE = 8; - static constexpr size_t OHOS_PREFIX_SIZE = 6; - static constexpr size_t APP_PREFIX_SIZE = 5; - - static void ResolveCurrentPath(JSThread *thread, - JSMutableHandle &dirPath, - JSMutableHandle &fileName, - const JSPandaFile *jsPandaFile) - { - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - CString fullName = jsPandaFile->GetJSPandaFileDesc(); - // find last '/' - int foundPos = static_cast(fullName.find_last_of("/\\")); - if (foundPos == -1) { - RETURN_IF_ABRUPT_COMPLETION(thread); - } - CString dirPathStr = fullName.substr(0, foundPos + 1); - JSHandle dirPathName = factory->NewFromUtf8(dirPathStr); - dirPath.Update(dirPathName.GetTaggedValue()); - - // Get filename from JSPandaFile - JSHandle cbFileName = factory->NewFromUtf8(fullName); - fileName.Update(cbFileName.GetTaggedValue()); - } - - static JSHandle ResolveDirPath(JSThread *thread, - JSHandle fileName) - { - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - CString fullName = ConvertToString(fileName.GetTaggedValue()); - // find last '/' - int foundPos = static_cast(fullName.find_last_of("/\\")); - if (foundPos == -1) { - RETURN_HANDLE_IF_ABRUPT_COMPLETION(EcmaString, thread); - } - CString dirPathStr = fullName.substr(0, foundPos + 1); - return factory->NewFromUtf8(dirPathStr); - } - - static CString NormalizePath(const CString &fileName) - { - if (fileName.find("//") == CString::npos && fileName.find("./") == CString::npos && - fileName[fileName.size() - 1] != '/') { - return fileName; - } - const char delim = '/'; - CString res = ""; - size_t prev = 0; - size_t curr = fileName.find(delim); - CVector elems; - while (curr != CString::npos) { - if (curr > prev) { - CString elem = fileName.substr(prev, curr - prev); - if (elem == ".." && !elems.empty()) { - elems.pop_back(); - } else if (elem != "." && elem != "..") { - elems.push_back(elem); - } - } - prev = curr + 1; - curr = fileName.find(delim, prev); - } - if (prev != fileName.size()) { - elems.push_back(fileName.substr(prev)); - } - for (auto e : elems) { - if (res.size() == 0 && fileName.at(0) != delim) { - res.append(e); - continue; - } - res.append(1, delim).append(e); - } - return res; - } - - static CString ParseOhmUrl(EcmaVM *vm, const CString &inputFileName, CString &outFileName) - { - CString bundleInstallName(BUNDLE_INSTALL_PATH); - size_t startStrLen = bundleInstallName.length(); - size_t pos = CString::npos; - - if (inputFileName.length() > startStrLen && inputFileName.compare(0, startStrLen, bundleInstallName) == 0) { - pos = startStrLen; - } - CString entryPoint; - if (pos != CString::npos) { - pos = inputFileName.find('/', startStrLen); - if (pos == CString::npos) { - LOG_FULL(FATAL) << "Invalid Ohm url, please check."; - } - CString moduleName = inputFileName.substr(startStrLen, pos - startStrLen); - outFileName = BUNDLE_INSTALL_PATH + moduleName + MERGE_ABC_ETS_MODULES; - entryPoint = vm->GetBundleName() + "/" + inputFileName.substr(startStrLen); - } else { - // Temporarily handle the relative path sent by arkui - if (StringHelper::StringStartWith(inputFileName, PREFIX_BUNDLE)) { - entryPoint = inputFileName.substr(PREFIX_BUNDLE_LEN); - outFileName = ParseUrl(vm, entryPoint); - } else { -#if !defined(PANDA_TARGET_WINDOWS) && !defined(PANDA_TARGET_MACOS) - entryPoint = vm->GetBundleName() + "/" + inputFileName; -#else - // if the inputFileName starts with '.test', the preview test page is started. - // in this case, the path ets does not need to be combined. - if (StringHelper::StringStartWith(inputFileName, PREVIER_TEST_DIR)) { - entryPoint = vm->GetBundleName() + "/" + vm->GetModuleName() + "/" + inputFileName; - } else { - entryPoint = vm->GetBundleName() + "/" + vm->GetModuleName() + MODULE_DEFAULE_ETS + inputFileName; - } -#endif - } - } - if (StringHelper::StringEndWith(entryPoint, EXT_NAME_ABC)) { - entryPoint.erase(entryPoint.length() - EXT_NAME_ABC_LEN, EXT_NAME_ABC_LEN); - } - return entryPoint; - } - - static void CropNamespaceIfAbsent(CString &moduleName) + static constexpr char COLON_TAG = ':'; + static constexpr char CURRENT_DIREATORY_TAG[] = "./"; + static constexpr char DOUBLE_POINT_TAG[] = ".."; + static constexpr char DOUBLE_SLASH_TAG[] = "//"; + static constexpr char NAME_SPACE_TAG = '@'; + static constexpr char POINT_STRING_TAG[] = "."; + static constexpr char POINT_TAG = '.'; + static constexpr char SLASH_TAG = '/'; + + static CString NormalizePath(const CString &fileName); + static JSHandle ResolveDirPath(JSThread *thread, CString fileName); + + /* + * Before: moduleName@nameSpace + * After: moduleName + */ + inline static void DeleteNamespace(CString &moduleName) { size_t pos = moduleName.find(NAME_SPACE_TAG); if (pos == CString::npos) { @@ -186,400 +52,41 @@ public: moduleName.erase(pos, moduleName.size() - pos); } - // current ohmUrl format : @bundle:bundlename/modulename@namespace/entry/src/index - static CString ParseUrl(EcmaVM *vm, const CString &entryPoint) + /* + * Before: bundleName/moduleName@namespace/moduleName/xxx/xxx + * After: moduleName/xxx/xxx + */ + inline static void AdaptOldIsaRecord(CString &recordName) { - CVector vec; - StringHelper::SplitString(entryPoint, vec, 0, SEGMENTS_LIMIT_TWO); - if (vec.size() < SEGMENTS_LIMIT_TWO) { - LOG_ECMA(DEBUG) << "ParseUrl SplitString filed, please check Url" << entryPoint; - return CString(); - } - CString bundleName = vec[0]; - CString moduleName = vec[1]; - CropNamespaceIfAbsent(moduleName); - - CString baseFileName; - if (bundleName != vm->GetBundleName()) { - // Cross-application - baseFileName = - BUNDLE_INSTALL_PATH + bundleName + "/" + moduleName + "/" + moduleName + MERGE_ABC_ETS_MODULES; - } else { - // Intra-application cross hap - baseFileName = BUNDLE_INSTALL_PATH + moduleName + MERGE_ABC_ETS_MODULES; - } - return baseFileName; - } - - static std::string ParseHapPath(const CString &fileName) - { - CString bundleSubInstallName(BUNDLE_SUB_INSTALL_PATH); - size_t startStrLen = bundleSubInstallName.length(); - if (fileName.length() > startStrLen && fileName.compare(0, startStrLen, bundleSubInstallName) == 0) { - CString hapPath = fileName.substr(startStrLen); - size_t pos = hapPath.find(MERGE_ABC_ETS_MODULES); - if (pos != CString::npos) { - return hapPath.substr(0, pos).c_str(); - } - } - return std::string(); - } - - static void CroppingRecord(CString &recordName) - { - size_t pos = recordName.find('/'); + size_t pos = recordName.find(SLASH_TAG); if (pos != CString::npos) { - pos = recordName.find('/', pos + 1); + pos = recordName.find(SLASH_TAG, pos + 1); if (pos != CString::npos) { recordName = recordName.substr(pos + 1); } } } - static CString ParsePrefixBundle(JSThread *thread, const JSPandaFile *jsPandaFile, - [[maybe_unused]] CString &baseFileName, CString moduleRequestName, [[maybe_unused]] CString recordName) - { - EcmaVM *vm = thread->GetEcmaVM(); - moduleRequestName = moduleRequestName.substr(PREFIX_BUNDLE_LEN); - CString entryPoint = moduleRequestName; - if (jsPandaFile->IsRecordWithBundleName()) { - CVector vec; - StringHelper::SplitString(moduleRequestName, vec, 0, SEGMENTS_LIMIT_TWO); - if (vec.size() < SEGMENTS_LIMIT_TWO) { - LOG_ECMA(INFO) << "SplitString filed, please check moduleRequestName"; - return CString(); - } - CString bundleName = vec[0]; - CString moduleName = vec[1]; - CropNamespaceIfAbsent(moduleName); - -#if !defined(PANDA_TARGET_WINDOWS) && !defined(PANDA_TARGET_MACOS) - if (bundleName != vm->GetBundleName()) { - baseFileName = - BUNDLE_INSTALL_PATH + bundleName + '/' + moduleName + '/' + moduleName + MERGE_ABC_ETS_MODULES; - } else { - baseFileName = BUNDLE_INSTALL_PATH + moduleName + MERGE_ABC_ETS_MODULES; - } -#else - CVector currentVec; - StringHelper::SplitString(recordName, currentVec, 0, SEGMENTS_LIMIT_TWO); - if (vec.size() < SEGMENTS_LIMIT_TWO) { - LOG_ECMA(INFO) << "SplitString filed, please check moduleRequestName"; - return CString(); - } - CString currentModuleName = currentVec[1]; - CropNamespaceIfAbsent(currentModuleName); - if (bundleName != vm->GetBundleName() || moduleName != currentModuleName) { - entryPoint = PREVIEW_OF_ACROSS_HAP_FLAG; - if (vm->EnableReportModuleResolvingFailure()) { - LOG_NO_TAG(ERROR) << "[ArkRuntime Log] Importing shared package is not supported in the Previewer."; - } - } -#endif - } else { - CroppingRecord(entryPoint); - } - return entryPoint; - } - - static CString MakeNewRecord(const JSPandaFile *jsPandaFile, CString &baseFileName, const CString &recordName, - const CString &requestName) - { - CString entryPoint; - CString moduleRequestName = RemoveSuffix(requestName); - size_t pos = moduleRequestName.find("./"); - if (pos == 0) { - moduleRequestName = moduleRequestName.substr(2); // 2 means jump "./" - } - pos = recordName.rfind('/'); - if (pos != CString::npos) { - entryPoint = recordName.substr(0, pos + 1) + moduleRequestName; - } else { - entryPoint = moduleRequestName; - } - entryPoint = NormalizePath(entryPoint); - entryPoint = ConfirmLoadingIndexOrNot(jsPandaFile, entryPoint); - if (!entryPoint.empty()) { - return entryPoint; - } - // the package name may have a '.js' suffix, try to parseThirdPartyPackage - entryPoint = ParseThirdPartyPackage(jsPandaFile, recordName, requestName); - if (!entryPoint.empty()) { - return entryPoint; - } - // Execute abc locally - pos = baseFileName.rfind('/'); - if (pos != CString::npos) { - baseFileName = baseFileName.substr(0, pos + 1) + moduleRequestName + EXT_NAME_ABC; - } else { - baseFileName = moduleRequestName + EXT_NAME_ABC; - } - pos = moduleRequestName.rfind('/'); - if (pos != CString::npos) { - entryPoint = moduleRequestName.substr(pos + 1); - } else { - entryPoint = moduleRequestName; - } - return entryPoint; - } - - static CString ConfirmLoadingIndexOrNot(const JSPandaFile *jsPandaFile, const CString &packageEntryPoint) - { - CString entryPoint = packageEntryPoint; - if (jsPandaFile->HasRecord(entryPoint)) { - return entryPoint; - } - // Possible import directory - entryPoint += PACKAGE_ENTRY_FILE; - if (jsPandaFile->HasRecord(entryPoint)) { - return entryPoint; - } - return CString(); - } - - static CString FindNpmEntryPoint(const JSPandaFile *jsPandaFile, const CString &packageEntryPoint) - { - // if we are currently importing a specific file or directory, we will get the entryPoint here - CString entryPoint = ConfirmLoadingIndexOrNot(jsPandaFile, packageEntryPoint); - if (!entryPoint.empty()) { - return entryPoint; - } - // When you come here, must import a packageName - return jsPandaFile->GetEntryPoint(packageEntryPoint); - } - - static CString FindPackageInTopLevel(const JSPandaFile *jsPandaFile, const CString& requestName, - const CString &packagePath) - { - // we find node_modules/0/xxx or node_modules/1/xxx - CString entryPoint; - for (size_t level = 0; level <= MAX_PACKAGE_LEVEL; ++level) { - CString levelStr = std::to_string(level).c_str(); - CString key = packagePath + "/" + levelStr + '/' + requestName; - entryPoint = FindNpmEntryPoint(jsPandaFile, key); - if (!entryPoint.empty()) { - return entryPoint; - } - } - return CString(); - } - - static CString FindOhpmEntryPoint(const JSPandaFile *jsPandaFile, const CString& ohpmPath, - const CString& requestName) - { - CVector vec; - StringHelper::SplitString(requestName, vec, 0); - size_t maxIndex = vec.size() - 1; - CString ohpmKey; - size_t index = 0; - // first we find the ohpmKey by splicing the requestName - while (index <= maxIndex) { - CString maybeKey = ohpmPath + "/" + StringHelper::JoinString(vec, 0, index); - ohpmKey = jsPandaFile->GetNpmEntries(maybeKey); - if (!ohpmKey.empty()) { - break; - } - ++index; - } - if (ohpmKey.empty()) { - return CString(); - } - // second If the ohpmKey is not empty, we will use it to obtain the real entrypoint - CString entryPoint; - if (index == maxIndex) { - // requestName is a packageName - entryPoint = jsPandaFile->GetEntryPoint(ohpmKey); - } else { - // import a specific file or directory - ohpmKey = ohpmKey + "/" + StringHelper::JoinString(vec, index + 1, maxIndex); - entryPoint = ConfirmLoadingIndexOrNot(jsPandaFile, ohpmKey); - } - return entryPoint; - } - - static CString FindPackageInTopLevelWithNamespace(const JSPandaFile *jsPandaFile, const CString& requestName, - const CString &recordName) - { - // find in current module @[moduleName|namespace]/ - CString entryPoint; - CString ohpmPath; - if (StringHelper::StringStartWith(recordName, PACKAGE_PATH_SEGMENT)) { - size_t pos = recordName.find('/'); - if (pos == CString::npos) { - LOG_ECMA(DEBUG) << "wrong recordname : " << recordName; - return CString(); - } - ohpmPath = recordName.substr(0, pos); - entryPoint = FindOhpmEntryPoint(jsPandaFile, recordName.substr(0, pos), requestName); - } else { - CVector vec; - StringHelper::SplitString(recordName, vec, 0, SEGMENTS_LIMIT_TWO); - if (vec.size() < SEGMENTS_LIMIT_TWO) { - LOG_ECMA(DEBUG) << "SplitString filed, please check moduleRequestName"; - return CString(); - } - CString moduleName = vec[1]; - // If namespace exists, use namespace as moduleName - size_t pos = moduleName.find(NAME_SPACE_TAG); - if (pos != CString::npos) { - moduleName = moduleName.substr(pos + 1); - } - ohpmPath = CString(PACKAGE_PATH_SEGMENT) + NAME_SPACE_TAG + moduleName; - entryPoint = FindOhpmEntryPoint(jsPandaFile, ohpmPath, requestName); - } - if (!entryPoint.empty()) { - return entryPoint; - } - // find in project directory / - return FindOhpmEntryPoint(jsPandaFile, PACKAGE_PATH_SEGMENT, requestName); - } - - static CString ParseOhpmPackage(const JSPandaFile *jsPandaFile, const CString &recordName, - const CString &requestName) - { - CString entryPoint; - if (StringHelper::StringStartWith(recordName, PACKAGE_PATH_SEGMENT)) { - //this way is thirdPartyPackage import ThirdPartyPackage - auto info = const_cast(jsPandaFile)->FindRecordInfo(recordName); - CString packageName = info.npmPackageName; - size_t pos = packageName.rfind(PACKAGE_PATH_SEGMENT); - if (pos != CString::npos) { - packageName.erase(pos, packageName.size() - pos); - CString ohpmPath = packageName + PACKAGE_PATH_SEGMENT; - entryPoint = FindOhpmEntryPoint(jsPandaFile, ohpmPath, requestName); - if (!entryPoint.empty()) { - return entryPoint; - } - } - } - // Import packages under the current module or project directory - return FindPackageInTopLevelWithNamespace(jsPandaFile, requestName, recordName); - } - - static CString ParseThirdPartyPackage(const JSPandaFile *jsPandaFile, const CString &recordName, - const CString &requestName, const CString &packagePath) - { - CString entryPoint; - if (StringHelper::StringStartWith(recordName, packagePath)) { - auto info = const_cast(jsPandaFile)->FindRecordInfo(recordName); - CString packageName = info.npmPackageName; - size_t pos = 0; - while (true) { - CString key = packageName + '/' + packagePath + "/" + requestName; - entryPoint = FindNpmEntryPoint(jsPandaFile, key); - if (!entryPoint.empty()) { - return entryPoint; - } - pos = packageName.rfind(packagePath) - 1; - if (pos == CString::npos || pos < 0) { - break; - } - packageName.erase(pos, packageName.size() - pos); - } - } - return FindPackageInTopLevel(jsPandaFile, requestName, packagePath); - } - - static CString ParseThirdPartyPackage(const JSPandaFile *jsPandaFile, const CString &recordName, - const CString &requestName) - { - static CVector packagePaths = {CString(PACKAGE_PATH_SEGMENT), CString(NPM_PATH_SEGMENT)}; - // We need to deal with scenarios like this 'json5/' -> 'json5' - CString normalizeRequestName = NormalizePath(requestName); - CString entryPoint = ParseOhpmPackage(jsPandaFile, recordName, normalizeRequestName); - if (!entryPoint.empty()) { - return entryPoint; - } - // Package compatible with old soft link format - for (size_t i = 0; i < packagePaths.size(); ++i) { - entryPoint = ParseThirdPartyPackage(jsPandaFile, recordName, normalizeRequestName, packagePaths[i]); - if (!entryPoint.empty()) { - return entryPoint; - } - } - return CString(); - } - - static bool IsImportFile(const CString &moduleRequestName) - { - if (moduleRequestName[0] == '.') { - return true; - } - size_t pos = moduleRequestName.rfind('.'); - if (pos != CString::npos) { - CString suffix = moduleRequestName.substr(pos); - if (suffix == EXT_NAME_JS || suffix == EXT_NAME_TS || suffix == EXT_NAME_ETS || suffix == EXT_NAME_JSON) { - return true; - } - } - return false; - } - - static CString RemoveSuffix(const CString &requestName) - { - CString res = requestName; - size_t pos = res.rfind('.'); - if (pos != CString::npos) { - CString suffix = res.substr(pos); - if (suffix == EXT_NAME_JS || suffix == EXT_NAME_TS || suffix == EXT_NAME_ETS || suffix == EXT_NAME_JSON) { - res.erase(pos, suffix.length()); - } - } - return res; - } - - inline static bool IsNativeModuleRequest(const CString &requestName) - { - if (requestName[0] != '@') { - return false; - } - if (StringHelper::StringStartWith(requestName, PathHelper::REQUIRE_NAPI_OHOS_PREFIX) || - StringHelper::StringStartWith(requestName, PathHelper::REQUIRE_NAPI_APP_PREFIX) || - StringHelper::StringStartWith(requestName, PathHelper::REQUIRE_NAITVE_MODULE_PREFIX)) { - return true; - } - return false; - } - - static CString ConcatFileNameWithMerge(JSThread *thread, const JSPandaFile *jsPandaFile, CString &baseFileName, - CString recordName, CString requestName) - { - CString entryPoint; - if (StringHelper::StringStartWith(requestName, PREFIX_BUNDLE)) { - entryPoint = ParsePrefixBundle(thread, jsPandaFile, baseFileName, requestName, recordName); - } else if (StringHelper::StringStartWith(requestName, PREFIX_PACKAGE)) { - entryPoint = requestName.substr(PREFIX_PACKAGE_LEN); - } else if (IsImportFile(requestName)) { // load a relative pathName. - entryPoint = MakeNewRecord(jsPandaFile, baseFileName, recordName, requestName); - } else { - entryPoint = ParseThirdPartyPackage(jsPandaFile, recordName, requestName); - } - if (entryPoint.empty() && thread->GetEcmaVM()->EnableReportModuleResolvingFailure()) { - LOG_ECMA(ERROR) << "Failed to resolve the requested entryPoint. baseFileName : '" << baseFileName << - "'. RecordName : '" << recordName << "'. RequestName : '" << requestName << "'."; - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - CString msg = "failed to load module'" + requestName + "' which imported by '" + - recordName + "'. Please check the target path."; - JSTaggedValue error = factory->GetJSError(ErrorType::REFERENCE_ERROR, msg.c_str()).GetTaggedValue(); - THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, entryPoint); - } - return entryPoint; - } - - static CString GetStrippedModuleName(const CString &moduleRequestName) + /* + * Before: @***:xxxx + * After: xxxx + */ + inline static CString GetStrippedModuleName(const CString &moduleRequestName) { - // @xxx:**** -> **** - size_t pos = moduleRequestName.find(':'); + size_t pos = moduleRequestName.find(COLON_TAG); if (pos == CString::npos) { LOG_FULL(FATAL) << "Unknown format " << moduleRequestName; } return moduleRequestName.substr(pos + 1); } - static CString GetInternalModulePrefix(const CString &moduleRequestName) + /* + * Before: @xxx:**** + * After: xxx + */ + inline static CString GetInternalModulePrefix(const CString &moduleRequestName) { - // @xxx:* -> xxx - size_t pos = moduleRequestName.find(':'); + size_t pos = moduleRequestName.find(COLON_TAG); if (pos == CString::npos) { LOG_FULL(FATAL) << "Unknown format " << moduleRequestName; } diff --git a/ecmascript/base/string_helper.h b/ecmascript/base/string_helper.h index e15b3d2c2edb543ff7ff56ed08aef2b82217db26..43c049c54f1786015bdc58aa8193be229a0788f2 100644 --- a/ecmascript/base/string_helper.h +++ b/ecmascript/base/string_helper.h @@ -229,6 +229,11 @@ public: return c; } + static inline void InplaceAppend(std::u16string &str1, const std::u16string &str2) + { + str1.append(str2); + } + static inline std::u16string Append(const std::u16string &str1, const std::u16string &str2) { std::u16string tmpStr = str1; diff --git a/ecmascript/base/tests/array_helper_test.cpp b/ecmascript/base/tests/array_helper_test.cpp index 720d8064fcaf6c4a536c285c8cf8490c72435c08..fb0ae0cc237c01d3965b44627c0545c34f5feff3 100644 --- a/ecmascript/base/tests/array_helper_test.cpp +++ b/ecmascript/base/tests/array_helper_test.cpp @@ -104,7 +104,7 @@ HWTEST_F_L0(ArrayHelperTest, SortCompare) EXPECT_EQ(resultValue3, -1); // Y is Undefined EXPECT_EQ(resultValue4, 1); // X > Y EXPECT_EQ(resultValue5, 0); // X = Y - EXPECT_EQ(resultValue6, 0); // X < Y + EXPECT_EQ(resultValue6, -1); // X < Y } /** diff --git a/ecmascript/base/tests/error_helper_test.cpp b/ecmascript/base/tests/error_helper_test.cpp index 3cad7ef139e6cbee694e73a53c8624c3462112b0..c006c0dbc28c104affa821ca9b047034e40812e1 100644 --- a/ecmascript/base/tests/error_helper_test.cpp +++ b/ecmascript/base/tests/error_helper_test.cpp @@ -324,4 +324,56 @@ HWTEST_F_L0(ErrorHelperTest, ErrorCommonConstructor_003) EXPECT_STREQ(EcmaStringAccessor(JSHandle::Cast(aggregateNameValue)).ToCString().c_str(), "AggregateError"); } + +HWTEST_F_L0(ErrorHelperTest, ErrorCommonConstructor_004) +{ + auto factory = instance->GetFactory(); + auto env = instance->GetGlobalEnv(); + JSHandle msgKey = thread->GlobalConstants()->GetHandledMessageString(); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + JSHandle causeKey = thread->GlobalConstants()->GetHandledCauseString(); + + JSHandle error(env->GetErrorFunction()); + JSHandle typeError(env->GetTypeErrorFunction()); + JSHandle objFun = env->GetObjectFunction(); + JSHandle optionsObj = factory->NewJSObjectByConstructor(JSHandle(objFun), objFun); + JSHandle causeValue(factory->NewFromASCII("error cause")); // test error cause + JSObject::SetProperty(thread, optionsObj, causeKey, causeValue); + + JSHandle errorMsg(factory->NewFromASCII("You have an Error!")); + EcmaRuntimeCallInfo *argv1 = + TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*error), 8); // 8 means 2 call args + argv1->SetFunction(error.GetTaggedValue()); + argv1->SetThis(JSTaggedValue(*error)); + argv1->SetCallArg(0, errorMsg.GetTaggedValue()); + argv1->SetCallArg(1, optionsObj.GetTaggedValue()); + auto prev1 = TestHelper::SetupFrame(thread, argv1); + JSHandle errorResult(thread, ErrorHelper::ErrorCommonConstructor(argv1, ErrorType::ERROR)); + TestHelper::TearDownFrame(thread, prev1); + JSHandle errorMsgValue(JSObject::GetProperty(thread, errorResult, msgKey).GetValue()); + JSHandle errorNameValue(JSObject::GetProperty(thread, errorResult, nameKey).GetValue()); + JSHandle errorCauseValue(JSObject::GetProperty(thread, errorResult, causeKey).GetValue()); + EXPECT_STREQ(EcmaStringAccessor(JSHandle::Cast(errorMsgValue)).ToCString().c_str(), + "You have an Error!"); + EXPECT_STREQ(EcmaStringAccessor(JSHandle::Cast(errorNameValue)).ToCString().c_str(), "Error"); + EXPECT_STREQ(EcmaStringAccessor(JSHandle::Cast(errorCauseValue)).ToCString().c_str(), "error cause"); + + JSHandle typeErrorMsg(factory->NewFromASCII("You have a type error!")); + EcmaRuntimeCallInfo *argv2 = + TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*typeError), 8); // 8 means 2 call args + argv2->SetFunction(typeError.GetTaggedValue()); + argv2->SetThis(JSTaggedValue(*typeError)); + argv2->SetCallArg(0, typeErrorMsg.GetTaggedValue()); + argv2->SetCallArg(1, optionsObj.GetTaggedValue()); + auto prev2 = TestHelper::SetupFrame(thread, argv2); + JSHandle typeErrorResult(thread, ErrorHelper::ErrorCommonConstructor(argv2, ErrorType::TYPE_ERROR)); + TestHelper::TearDownFrame(thread, prev2); + JSHandle typeMsgValue(JSObject::GetProperty(thread, typeErrorResult, msgKey).GetValue()); + JSHandle typeNameValue(JSObject::GetProperty(thread, typeErrorResult, nameKey).GetValue()); + JSHandle typeCauseValue(JSObject::GetProperty(thread, typeErrorResult, causeKey).GetValue()); + EXPECT_STREQ(EcmaStringAccessor(JSHandle::Cast(typeMsgValue)).ToCString().c_str(), + "You have a type error!"); + EXPECT_STREQ(EcmaStringAccessor(JSHandle::Cast(typeNameValue)).ToCString().c_str(), "TypeError"); + EXPECT_STREQ(EcmaStringAccessor(JSHandle::Cast(typeCauseValue)).ToCString().c_str(), "error cause"); +} } // namespace panda::test diff --git a/ecmascript/base/tests/json_parser_test.cpp b/ecmascript/base/tests/json_parser_test.cpp index 36f44df820cef4bf4d8ff65238fa3728c032f9f3..6a0a03f8373e3b82b249d51448978589c05668da 100644 --- a/ecmascript/base/tests/json_parser_test.cpp +++ b/ecmascript/base/tests/json_parser_test.cpp @@ -57,21 +57,21 @@ public: HWTEST_F_L0(JsonParserTest, Parser_001) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - JsonParser parser(thread); + Utf8JsonParser parser(thread); // JSON Number JSHandle handleMsg2(factory->NewFromASCII("1234")); JSHandle handleStr2(JSTaggedValue::ToString(thread, handleMsg2)); - JSHandle result2 = parser.ParseUtf8(*handleStr2); + JSHandle result2 = parser.Parse(*handleStr2); EXPECT_EQ(result2->GetNumber(), 1234); // JSON Literal JSHandle handleMsg3(factory->NewFromASCII("true")); JSHandle handleStr3(JSTaggedValue::ToString(thread, handleMsg3)); - JSHandle result3 = parser.ParseUtf8(*handleStr3); + JSHandle result3 = parser.Parse(*handleStr3); EXPECT_EQ(result3.GetTaggedValue(), JSTaggedValue::True()); // JSON Unexpected JSHandle handleMsg4(factory->NewFromASCII("trus")); JSHandle handleStr4(JSTaggedValue::ToString(thread, handleMsg4)); - JSHandle result4 = parser.ParseUtf8(*handleStr4); + JSHandle result4 = parser.Parse(*handleStr4); EXPECT_EQ(result4.GetTaggedValue(), JSTaggedValue::Exception()); } @@ -85,28 +85,28 @@ HWTEST_F_L0(JsonParserTest, Parser_001) HWTEST_F_L0(JsonParserTest, Parser_002) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - JsonParser parser(thread); + Utf16JsonParser parser(thread); // JSON Number uint16_t array1Utf16[] = {0x31, 0x32, 0x33, 0x34}; // "1234" uint32_t array1Utf16Len = sizeof(array1Utf16) / sizeof(array1Utf16[0]); JSHandle handleMsg2(factory->NewFromUtf16(&array1Utf16[0], array1Utf16Len)); JSHandle handleStr2(JSTaggedValue::ToString(thread, handleMsg2)); - JSHandle result2 = parser.ParseUtf16(*handleStr2); + JSHandle result2 = parser.Parse(*handleStr2); EXPECT_EQ(result2->GetNumber(), 1234); // JSON Literal uint16_t array2Utf16[] = {0x74, 0x72, 0x75, 0x65}; // "true" uint32_t array2Utf16Len = sizeof(array2Utf16) / sizeof(array2Utf16[0]); JSHandle handleMsg3(factory->NewFromUtf16(&array2Utf16[0], array2Utf16Len)); JSHandle handleStr3(JSTaggedValue::ToString(thread, handleMsg3)); - JSHandle result3 = parser.ParseUtf16(*handleStr3); + JSHandle result3 = parser.Parse(*handleStr3); EXPECT_EQ(result3.GetTaggedValue(), JSTaggedValue::True()); // JSON String uint16_t array3Utf16[] = {0x22, 0x73, 0x74, 0x72, 0x69, 0x6E, 0X67, 0x22}; // "string" uint32_t array3Utf16Len = sizeof(array3Utf16) / sizeof(array3Utf16[0]); JSHandle handleMsg4(factory->NewFromUtf16(&array3Utf16[0], array3Utf16Len)); JSHandle handleStr4(JSTaggedValue::ToString(thread, handleMsg4)); - JSHandle result4 = parser.ParseUtf16(*handleStr4); + JSHandle result4 = parser.Parse(*handleStr4); JSHandle handleEcmaStr(result4); EXPECT_STREQ("string", EcmaStringAccessor(handleEcmaStr).ToCString().c_str()); } @@ -121,13 +121,13 @@ HWTEST_F_L0(JsonParserTest, Parser_002) HWTEST_F_L0(JsonParserTest, Parser_003) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - JsonParser parser(thread); + Utf8JsonParser parser(thread); JSHandle handleMsg(factory->NewFromASCII( "\t\r \n{\t\r \n \"json\"\t\r\n:\t\r \n{\t\r \n}\t\r \n,\t\r \n \"prop2\"\t\r \n:\t\r \n [\t\r \nfalse\t\r" "\n,\t\r \nnull\t\r \ntrue\t\r,123.456\t\r \n]\t\r \n}\t\r \n")); JSHandle handleStr(JSTaggedValue::ToString(thread, handleMsg)); // JSON Object - JSHandle result = parser.ParseUtf8(*handleStr); + JSHandle result = parser.Parse(*handleStr); EXPECT_TRUE(result->IsECMAObject()); } @@ -142,11 +142,11 @@ HWTEST_F_L0(JsonParserTest, Parser_004) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); - JsonParser parser(thread); + Utf8JsonParser parser(thread); JSHandle handleMsg(factory->NewFromASCII("[100,2.5,\"abc\"]")); JSHandle handleStr(JSTaggedValue::ToString(thread, handleMsg)); // JSON Array - JSHandle result = parser.ParseUtf8(*handleStr); + JSHandle result = parser.Parse(*handleStr); JSTaggedValue resultValue(static_cast(result->GetRawData())); EXPECT_TRUE(resultValue.IsECMAObject()); @@ -168,12 +168,12 @@ HWTEST_F_L0(JsonParserTest, Parser_004) HWTEST_F_L0(JsonParserTest, Parser_005) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - JsonParser parser(thread); + Utf8JsonParser parser(thread); JSHandle handleMsg(factory->NewFromASCII("{\"epf\":100,\"key1\":400}")); JSHandle handleStr(JSTaggedValue::ToString(thread, handleMsg)); // JSON Object - JSHandle result = parser.ParseUtf8(*handleStr); + JSHandle result = parser.Parse(*handleStr); JSTaggedValue resultValue(static_cast(result->GetRawData())); EXPECT_TRUE(resultValue.IsECMAObject()); @@ -196,9 +196,9 @@ HWTEST_F_L0(JsonParserTest, Parser_005) */ HWTEST_F_L0(JsonParserTest, Parser_006) { - JsonParser parser(thread); + Utf8JsonParser parser(thread); JSHandle emptyString(thread->GlobalConstants()->GetHandledEmptyString()); - JSHandle result = parser.ParseUtf8(*emptyString); + JSHandle result = parser.Parse(*emptyString); EXPECT_TRUE(result->IsException()); } } // namespace panda::test diff --git a/ecmascript/base/tests/number_helper_test.cpp b/ecmascript/base/tests/number_helper_test.cpp index 5a545510def955c476a1e0d08d9fe851b2e31e47..3b487092a39ad93130d86e22037214d443a66d6a 100644 --- a/ecmascript/base/tests/number_helper_test.cpp +++ b/ecmascript/base/tests/number_helper_test.cpp @@ -105,7 +105,8 @@ HWTEST_F_L0(NumberHelperTest, DoubleToString_001) radix = 5; resultStr = factory->NewFromASCII("-1104332401304422434310320000"); - JSHandle handleEcmaStr5(thread, NumberHelper::DoubleToString(thread, static_cast(-9223372036854775807), radix)); + JSHandle handleEcmaStr5(thread, + NumberHelper::DoubleToString(thread, static_cast(-9223372036854775807), radix)); EXPECT_EQ(EcmaStringAccessor::Compare(instance, handleEcmaStr5, resultStr), 0); radix = 6; diff --git a/ecmascript/base/tests/typed_array_helper_test.cpp b/ecmascript/base/tests/typed_array_helper_test.cpp index c502f608d8778df1fd52d8e948dad6e05818de24..dce9b45f2b30c58f3777b48bb7ce1e29b49144ea 100755 --- a/ecmascript/base/tests/typed_array_helper_test.cpp +++ b/ecmascript/base/tests/typed_array_helper_test.cpp @@ -146,7 +146,7 @@ HWTEST_F_L0(TypedArrayHelperTest, AllocateTypedArray_001) auto prev = TestHelper::SetupFrame(thread, argv); JSHandle newTarget = BuiltinsBase::GetNewTarget(argv); JSHandle arrayObj = - TypedArrayHelper::AllocateTypedArray(factory, ecmaVm, constructorName, newTarget, DataViewType::UINT8); + TypedArrayHelper::AllocateTypedArray(thread, constructorName, newTarget, DataViewType::UINT8); TestHelper::TearDownFrame(thread, prev); JSTypedArray *jsTypedArray = JSTypedArray::Cast(*arrayObj); EXPECT_EQ(jsTypedArray->GetContentType(), ContentType::Number); @@ -167,7 +167,7 @@ HWTEST_F_L0(TypedArrayHelperTest, AllocateTypedArray_002) auto prev = TestHelper::SetupFrame(thread, argv); JSHandle newTarget = BuiltinsBase::GetNewTarget(argv); JSHandle arrayObj = - TypedArrayHelper::AllocateTypedArray(factory, ecmaVm, constructorName, newTarget, length, DataViewType::UINT8); + TypedArrayHelper::AllocateTypedArray(thread, constructorName, newTarget, length, DataViewType::UINT8); TestHelper::TearDownFrame(thread, prev); JSTypedArray *jsTypedArray = JSTypedArray::Cast(*arrayObj); EXPECT_EQ(jsTypedArray->GetContentType(), ContentType::Number); diff --git a/ecmascript/base/typed_array_helper-inl.h b/ecmascript/base/typed_array_helper-inl.h index e348f809f73eed7eebe650dd8d75daaa40d37df7..eecb5cc730ee8aae430aa61157f2beda5725b6ff 100644 --- a/ecmascript/base/typed_array_helper-inl.h +++ b/ecmascript/base/typed_array_helper-inl.h @@ -126,50 +126,52 @@ JSHandle TypedArrayHelper::GetConstructorFromType(JSThread *thread, { JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); switch (arrayType) { - case DataViewType::INT8: - return JSHandle(env->GetInt8ArrayFunction()); - case DataViewType::UINT8: - return JSHandle(env->GetUint8ArrayFunction()); - case DataViewType::UINT8_CLAMPED: - return JSHandle(env->GetUint8ClampedArrayFunction()); - case DataViewType::INT16: - return JSHandle(env->GetInt16ArrayFunction()); - case DataViewType::UINT16: - return JSHandle(env->GetUint16ArrayFunction()); - case DataViewType::INT32: - return JSHandle(env->GetInt32ArrayFunction()); - case DataViewType::UINT32: - return JSHandle(env->GetUint32ArrayFunction()); - case DataViewType::FLOAT32: - return JSHandle(env->GetFloat32ArrayFunction()); - case DataViewType::FLOAT64: - return JSHandle(env->GetFloat64ArrayFunction()); - case DataViewType::BIGINT64: - return JSHandle(env->GetBigInt64ArrayFunction()); - default: - break; + case DataViewType::INT8: + return JSHandle(env->GetInt8ArrayFunction()); + case DataViewType::UINT8: + return JSHandle(env->GetUint8ArrayFunction()); + case DataViewType::UINT8_CLAMPED: + return JSHandle(env->GetUint8ClampedArrayFunction()); + case DataViewType::INT16: + return JSHandle(env->GetInt16ArrayFunction()); + case DataViewType::UINT16: + return JSHandle(env->GetUint16ArrayFunction()); + case DataViewType::INT32: + return JSHandle(env->GetInt32ArrayFunction()); + case DataViewType::UINT32: + return JSHandle(env->GetUint32ArrayFunction()); + case DataViewType::FLOAT32: + return JSHandle(env->GetFloat32ArrayFunction()); + case DataViewType::FLOAT64: + return JSHandle(env->GetFloat64ArrayFunction()); + case DataViewType::BIGINT64: + return JSHandle(env->GetBigInt64ArrayFunction()); + default: + break; } return JSHandle(env->GetBigUint64ArrayFunction()); } uint32_t TypedArrayHelper::GetSizeFromType(const DataViewType arrayType) { - uint32_t elementSize; if (arrayType == DataViewType::INT8 || arrayType == DataViewType::UINT8 || arrayType == DataViewType::UINT8_CLAMPED) { - elementSize = ElementSize::ONE; - } else if (arrayType == DataViewType::INT16 || - arrayType == DataViewType::UINT16) { - elementSize = ElementSize::TWO; - } else if (arrayType == DataViewType::FLOAT32 || - arrayType == DataViewType::UINT32 || - arrayType == DataViewType::INT32) { - elementSize = ElementSize::FOUR; - } else { - elementSize = ElementSize::EIGHT; + return ElementSize::ONE; + } + + if (arrayType == DataViewType::INT16 || + arrayType == DataViewType::UINT16) { + return ElementSize::TWO; } - return elementSize; + + if (arrayType == DataViewType::FLOAT32 || + arrayType == DataViewType::UINT32 || + arrayType == DataViewType::INT32) { + return ElementSize::FOUR; + } + + return ElementSize::EIGHT; } } // namespace panda::ecmascript::base #endif // ECMASCRIPT_BASE_TYPED_ARRAY_HELPER_INL_H diff --git a/ecmascript/base/typed_array_helper.cpp b/ecmascript/base/typed_array_helper.cpp index 2cfe6b939340a642171b1f541a3d80f9555e11d6..89ffed8b3ff112a271c8a074967b7b5e8ebe2f6f 100644 --- a/ecmascript/base/typed_array_helper.cpp +++ b/ecmascript/base/typed_array_helper.cpp @@ -45,7 +45,6 @@ JSTaggedValue TypedArrayHelper::TypedArrayConstructor(EcmaRuntimeCallInfo *argv, ASSERT(argv); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); - EcmaVM *ecmaVm = thread->GetEcmaVM(); JSHandle newTarget = BuiltinsBase::GetNewTarget(argv); // 2. If NewTarget is undefined, throw a TypeError exception. if (newTarget->IsUndefined()) { @@ -54,24 +53,23 @@ JSTaggedValue TypedArrayHelper::TypedArrayConstructor(EcmaRuntimeCallInfo *argv, // 3. Let constructorName be the String value of the Constructor Name value specified in Table 61 for this // TypedArray constructor. // 4. Let O be ? AllocateTypedArray(constructorName, NewTarget, "%TypedArray.prototype%"). - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle firstArg = BuiltinsBase::GetCallArg(argv, 0); if (!firstArg->IsECMAObject()) { // es11 22.2.4.1 TypedArray ( ) - int32_t elementLength = 0; + uint32_t elementLength = 0; // es11 22.2.4.2 TypedArray ( length ) if (!firstArg->IsUndefined()) { JSTaggedNumber index = JSTaggedValue::ToIndex(thread, firstArg); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - elementLength = static_cast(index.GetNumber()); + elementLength = static_cast(index.GetNumber()); } - JSHandle obj = TypedArrayHelper::AllocateTypedArray(factory, ecmaVm, constructorName, newTarget, + JSHandle obj = TypedArrayHelper::AllocateTypedArray(thread, constructorName, newTarget, elementLength, arrayType); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return obj.GetTaggedValue(); } - JSHandle obj = - TypedArrayHelper::AllocateTypedArray(factory, ecmaVm, constructorName, newTarget, arrayType); + + JSHandle obj = TypedArrayHelper::AllocateTypedArray(thread, constructorName, newTarget, arrayType); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); if (firstArg->IsTypedArray()) { return TypedArrayHelper::CreateFromTypedArray(argv, obj, arrayType); @@ -98,11 +96,13 @@ JSTaggedValue TypedArrayHelper::FastCopyElementFromArray(EcmaRuntimeCallInfo *ar if (elements->GetLength() < len) { TypedArrayHelper::CreateFromOrdinaryObject(argv, obj, arrayType); } - EcmaVM *ecmaVm = thread->GetEcmaVM(); - TypedArrayHelper::AllocateTypedArrayBuffer(thread, ecmaVm, obj, len, arrayType); + + TypedArrayHelper::AllocateTypedArrayBuffer(thread, obj, len, arrayType); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle targetObj = JSHandle::Cast(obj); - + JSStableArray::FastCopyFromArrayToTypedArray(thread, targetObj, arrayType, 0, len, elements); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return JSHandle::Cast(targetObj).GetTaggedValue(); } @@ -141,7 +141,7 @@ JSTaggedValue TypedArrayHelper::CreateFromOrdinaryObject(EcmaRuntimeCallInfo *ar } } uint32_t len = static_cast(vec.size()); - TypedArrayHelper::AllocateTypedArrayBuffer(thread, ecmaVm, obj, len, arrayType); + TypedArrayHelper::AllocateTypedArrayBuffer(thread, obj, len, arrayType); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // d. Let k be 0. // e. Repeat, while k < len @@ -154,6 +154,7 @@ JSTaggedValue TypedArrayHelper::CreateFromOrdinaryObject(EcmaRuntimeCallInfo *ar while (k < len) { tKey.Update(JSTaggedValue(k)); JSHandle kKey(JSTaggedValue::ToString(thread, tKey)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle kValue = vec[k]; JSTaggedValue::SetProperty(thread, JSHandle::Cast(obj), kKey, kValue, true); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -173,7 +174,7 @@ JSTaggedValue TypedArrayHelper::CreateFromOrdinaryObject(EcmaRuntimeCallInfo *ar RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); uint64_t rawLen = lenTemp.GetNumber(); // 10. Perform ? AllocateTypedArrayBuffer(O, len). - TypedArrayHelper::AllocateTypedArrayBuffer(thread, ecmaVm, obj, rawLen, arrayType); + TypedArrayHelper::AllocateTypedArrayBuffer(thread, obj, rawLen, arrayType); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 11. Let k be 0. // 12. Repeat, while k < len @@ -187,6 +188,7 @@ JSTaggedValue TypedArrayHelper::CreateFromOrdinaryObject(EcmaRuntimeCallInfo *ar while (k < len) { tKey.Update(JSTaggedValue(k)); JSHandle kKey(JSTaggedValue::ToString(thread, tKey)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle kValue = JSObject::GetProperty(thread, objectArg, kKey).GetValue(); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSTaggedValue::SetProperty(thread, JSHandle::Cast(obj), kKey, kValue, true); @@ -230,7 +232,9 @@ JSTaggedValue TypedArrayHelper::CreateFromTypedArray(EcmaRuntimeCallInfo *argv, // 15. Let byteLength be elementSize × elementLength. uint32_t srcByteOffset = srcObj->GetByteOffset(); uint32_t elementSize = TypedArrayHelper::GetSizeFromType(arrayType); - uint32_t byteLength = elementSize * elementLength; + // If elementLength is a large number, the multiplication of elementSize and elementLength may exceed + // the maximum value of uint32, resulting in data overflow. Therefore, the type of byteLength is uint64_t. + uint64_t byteLength = elementSize * static_cast(elementLength); // 16. If IsSharedArrayBuffer(srcData) is false, then // a. Let bufferConstructor be ? SpeciesConstructor(srcData, %ArrayBuffer%). @@ -307,7 +311,7 @@ JSTaggedValue TypedArrayHelper::CreateFromArrayBuffer(EcmaRuntimeCallInfo *argv, [[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). - uint32_t elementSize = static_cast(TypedArrayHelper::GetSizeFromType(arrayType)); + uint32_t elementSize = TypedArrayHelper::GetSizeFromType(arrayType); JSHandle byteOffset = BuiltinsBase::GetCallArg(argv, 1); JSTaggedNumber index = JSTaggedValue::ToIndex(thread, byteOffset); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -320,11 +324,11 @@ JSTaggedValue TypedArrayHelper::CreateFromArrayBuffer(EcmaRuntimeCallInfo *argv, // 8. If length is not undefined, then // a. Let newLength be ? ToIndex(length). JSHandle length = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); - int32_t newLength = 0; + uint64_t newLength = 0; if (!length->IsUndefined()) { index = JSTaggedValue::ToIndex(thread, length); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - newLength = static_cast(index.GetNumber()); + newLength = static_cast(index.GetNumber()); } // 9. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. JSHandle buffer = BuiltinsBase::GetCallArg(argv, 0); @@ -337,7 +341,7 @@ JSTaggedValue TypedArrayHelper::CreateFromArrayBuffer(EcmaRuntimeCallInfo *argv, // a. If bufferByteLength modulo elementSize ≠ 0, throw a RangeError exception. // b. Let newByteLength be bufferByteLength - offset. // c. If newByteLength < 0, throw a RangeError exception. - uint32_t newByteLength = 0; + uint64_t newByteLength = 0; if (length->IsUndefined()) { if (bufferByteLength % elementSize != 0) { THROW_RANGE_ERROR_AND_RETURN(thread, "The bufferByteLength cannot be an integral multiple of elementSize.", @@ -351,9 +355,7 @@ JSTaggedValue TypedArrayHelper::CreateFromArrayBuffer(EcmaRuntimeCallInfo *argv, // 12. Else, // a. Let newByteLength be newLength × elementSize. // b. If offset + newByteLength > bufferByteLength, throw a RangeError exception. - ASSERT((static_cast(newLength) * static_cast(elementSize)) <= - static_cast(INT32_MAX)); - newByteLength = static_cast(newLength) * elementSize; + newByteLength = newLength * elementSize; if (offset + newByteLength > bufferByteLength) { THROW_RANGE_ERROR_AND_RETURN(thread, "The newByteLength is out of range.", JSTaggedValue::Exception()); } @@ -364,23 +366,22 @@ JSTaggedValue TypedArrayHelper::CreateFromArrayBuffer(EcmaRuntimeCallInfo *argv, // 16. Set O.[[ArrayLength]] to newByteLength / elementSize. JSTypedArray *jsTypedArray = JSTypedArray::Cast(*obj); jsTypedArray->SetViewedArrayBufferOrByteArray(thread, buffer); - jsTypedArray->SetByteLength(static_cast(newByteLength)); + jsTypedArray->SetByteLength(newByteLength); jsTypedArray->SetByteOffset(offset); - jsTypedArray->SetArrayLength(static_cast(newByteLength / elementSize)); + jsTypedArray->SetArrayLength(newByteLength / elementSize); // 17. Return O. return obj.GetTaggedValue(); } // es11 22.2.4.2.1 Runtime Semantics: AllocateTypedArray ( constructorName, newTarget, defaultProto ) -JSHandle TypedArrayHelper::AllocateTypedArray(ObjectFactory *factory, EcmaVM *ecmaVm, +JSHandle TypedArrayHelper::AllocateTypedArray(JSThread *thread, const JSHandle &constructorName, const JSHandle &newTarget, const DataViewType arrayType) { - JSThread *thread = ecmaVm->GetJSThread(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); // 1. Let proto be ? GetPrototypeFromConstructor(newTarget, defaultProto). // 2. Let obj be ! IntegerIndexedObjectCreate(proto). - RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle(thread, JSTaggedValue::Exception())); JSHandle typedArrayFunc = TypedArrayHelper::GetConstructorFromType(thread, arrayType); JSHandle obj = factory->NewJSObjectByConstructor(typedArrayFunc, newTarget); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle(thread, JSTaggedValue::Exception())); @@ -407,18 +408,17 @@ JSHandle TypedArrayHelper::AllocateTypedArray(ObjectFactory *factory, jsTypedArray->SetIsOnHeap(false); // 9. Return obj. return obj; -} // namespace panda::ecmascript::base +} // es11 22.2.4.2.1 Runtime Semantics: AllocateTypedArray ( constructorName, newTarget, defaultProto, length ) -JSHandle TypedArrayHelper::AllocateTypedArray(ObjectFactory *factory, EcmaVM *ecmaVm, +JSHandle TypedArrayHelper::AllocateTypedArray(JSThread *thread, const JSHandle &constructorName, - const JSHandle &newTarget, int32_t length, + const JSHandle &newTarget, uint32_t length, const DataViewType arrayType) { - JSThread *thread = ecmaVm->GetJSThread(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); // 1. Let proto be ? GetPrototypeFromConstructor(newTarget, defaultProto). // 2. Let obj be ! IntegerIndexedObjectCreate(proto). - RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle(thread, JSTaggedValue::Exception())); JSHandle typedArrayFunc = TypedArrayHelper::GetConstructorFromType(thread, arrayType); JSHandle obj = factory->NewJSObjectByConstructor(typedArrayFunc, newTarget); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle(thread, JSTaggedValue::Exception())); @@ -428,14 +428,14 @@ JSHandle TypedArrayHelper::AllocateTypedArray(ObjectFactory *factory, // 7. If length is not present, then // 8. Else, // a. Perform ? AllocateTypedArrayBuffer(obj, length). - TypedArrayHelper::AllocateTypedArrayBuffer(thread, ecmaVm, obj, length, arrayType); + TypedArrayHelper::AllocateTypedArrayBuffer(thread, obj, length, arrayType); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle(thread, JSTaggedValue::Exception())); // 9. Return obj. return obj; } // es11 22.2.4.2.2 Runtime Semantics: AllocateTypedArrayBuffer ( O, length ) -JSHandle TypedArrayHelper::AllocateTypedArrayBuffer(JSThread *thread, EcmaVM *ecmaVm, +JSHandle TypedArrayHelper::AllocateTypedArrayBuffer(JSThread *thread, const JSHandle &obj, uint64_t length, const DataViewType arrayType) { @@ -456,7 +456,7 @@ JSHandle TypedArrayHelper::AllocateTypedArrayBuffer(JSThread *thread, // 7. Let data be ? AllocateArrayBuffer(%ArrayBuffer%, byteLength). JSTaggedValue data; if (byteLength > JSTypedArray::MAX_ONHEAP_LENGTH) { - JSHandle constructor = ecmaVm->GetGlobalEnv()->GetArrayBufferFunction(); + JSHandle constructor = thread->GetEcmaVM()->GetGlobalEnv()->GetArrayBufferFunction(); data = BuiltinsArrayBuffer::AllocateArrayBuffer(thread, constructor, byteLength); JSTypedArray::Cast(*obj)->SetIsOnHeap(false); } else { @@ -544,6 +544,27 @@ JSHandle TypedArrayHelper::TypedArrayCreate(JSThread *thread, const JS return newTypedArray; } +// TypedArrayCreateSameType ( exemplar, argumentList ) +JSHandle TypedArrayHelper::TypedArrayCreateSameType(JSThread *thread, const JSHandle &obj, + uint32_t argc, JSTaggedType argv[]) +{ + // 1. Let constructor be the intrinsic object associated with the constructor name exemplar.[[TypedArrayName]] + // in Table 70. + JSHandle buffHandle(thread, JSTaggedValue(argv[0])); + JSHandle constructor = + TypedArrayHelper::GetConstructor(thread, JSHandle(obj)); + argv[0] = buffHandle.GetTaggedType(); + // 2. Let result be ? TypedArrayCreate(constructor, argumentList). + JSHandle result = TypedArrayHelper::TypedArrayCreate(thread, constructor, argc, argv); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle(thread, JSTaggedValue::Exception())); + // 3. Assert: result has [[TypedArrayName]] and [[ContentType]] internal slots. + // 4. Assert: result.[[ContentType]] is exemplar.[[ContentType]]. + [[maybe_unused]] ContentType objContentType = obj->GetContentType(); + [[maybe_unused]] ContentType resultContentType = JSHandle::Cast(result)->GetContentType(); + ASSERT(objContentType == resultContentType); + return result; +} + // es11 22.2.3.5.1 Runtime Semantics: ValidateTypedArray ( O ) JSTaggedValue TypedArrayHelper::ValidateTypedArray(JSThread *thread, const JSHandle &value) { @@ -577,7 +598,7 @@ int32_t TypedArrayHelper::SortCompare(JSThread *thread, const JSHandleIsUndefined()) { - const int32_t argsLength = 2; + const uint32_t argsLength = 2; JSHandle undefined = globalConst->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, callbackfnHandle, undefined, undefined, argsLength); @@ -623,7 +644,9 @@ int32_t TypedArrayHelper::SortCompare(JSThread *thread, const JSHandle &constructorName, const DataViewType arrayType); - static JSHandle AllocateTypedArray(ObjectFactory *factory, EcmaVM *ecmaVm, + static JSHandle AllocateTypedArray(JSThread *thread, const JSHandle &constructorName, const JSHandle &newTarget, const DataViewType arrayType); - static JSHandle AllocateTypedArray(ObjectFactory *factory, EcmaVM *ecmaVm, + static JSHandle AllocateTypedArray(JSThread *thread, const JSHandle &constructorName, - const JSHandle &newTarget, int32_t length, + const JSHandle &newTarget, uint32_t length, const DataViewType arrayType); static JSHandle TypedArraySpeciesCreate(JSThread *thread, const JSHandle &obj, uint32_t argc, JSTaggedType argv[]); static JSHandle TypedArrayCreate(JSThread *thread, const JSHandle &constructor, uint32_t argc, const JSTaggedType argv[]); + static JSHandle TypedArrayCreateSameType(JSThread *thread, const JSHandle &obj, + uint32_t argc, JSTaggedType argv[]); static JSTaggedValue ValidateTypedArray(JSThread *thread, const JSHandle &value); inline static DataViewType GetType(const JSHandle &obj); inline static DataViewType GetType(JSType type); @@ -61,7 +63,7 @@ private: const DataViewType arrayType); static JSTaggedValue CreateFromArrayBuffer(EcmaRuntimeCallInfo *argv, const JSHandle &obj, const DataViewType arrayType); - static JSHandle AllocateTypedArrayBuffer(JSThread *thread, EcmaVM *ecmaVm, const JSHandle &obj, + static JSHandle AllocateTypedArrayBuffer(JSThread *thread, const JSHandle &obj, uint64_t length, const DataViewType arrayType); static JSTaggedValue FastCopyElementFromArray(EcmaRuntimeCallInfo *argv, const JSHandle &obj, const DataViewType arrayType); diff --git a/ecmascript/builtins/builtins.cpp b/ecmascript/builtins/builtins.cpp index 58ce98581605bc1f801a8dc1a268ddcd1d34b802..c50aafc52707df979b3c7532828906b9f57c9f03 100644 --- a/ecmascript/builtins/builtins.cpp +++ b/ecmascript/builtins/builtins.cpp @@ -92,6 +92,7 @@ #include "ecmascript/require/js_cjs_module_cache.h" #include "ecmascript/require/js_cjs_require.h" #include "ecmascript/require/js_cjs_exports.h" +#include "ecmascript/symbol_table.h" #include "ecmascript/napi/include/jsnapi.h" #include "ecmascript/object_factory.h" #ifdef ARK_SUPPORT_INTL @@ -357,6 +358,7 @@ void Builtins::Initialize(const JSHandle &env, JSThread *thread, bool InitializeCjsRequire(env); InitializeDefaultExportOfScript(env); InitializeFunctionHclassForOptimized(env); + InitializePropertyDetector(env); JSHandle generatorFuncClass = factory_->CreateFunctionClass(FunctionKind::GENERATOR_FUNCTION, JSFunction::SIZE, JSType::JS_GENERATOR_FUNCTION, env->GetGeneratorFunctionPrototype()); @@ -397,6 +399,15 @@ void Builtins::InitializeFunctionHclassForOptimized(const JSHandle &e #undef JSFUNCTION_JCLASS_LIST } +void Builtins::InitializePropertyDetector(const JSHandle &env) const +{ +#define INITIALIZE_PROPERTY_DETECTOR(type, name, index) \ + JSHandle name##detector = factory_->NewMarkerCell(); \ + env->Set##name(thread_, name##detector); + GLOBAL_ENV_DETECTOR_FIELDS(INITIALIZE_PROPERTY_DETECTOR) +#undef INITIALIZE_PROPERTY_DETECTOR +} + void Builtins::SetLazyAccessor(const JSHandle &object, const JSHandle &key, const JSHandle &accessor) const { @@ -425,6 +436,7 @@ void Builtins::InitializeGlobalObject(const JSHandle &env, const JSHa // Global object test SetFunction(env, globalObject, "print", Global::PrintEntrypoint, 0); + SetFunction(env, globalObject, "markModuleCollectable", Global::MarkModuleCollectable, 0); #if ECMASCRIPT_ENABLE_RUNTIME_STAT SetFunction(env, globalObject, "startRuntimeStat", Global::StartRuntimeStat, 0); SetFunction(env, globalObject, "stopRuntimeStat", Global::StopRuntimeStat, 0); @@ -455,6 +467,8 @@ void Builtins::InitializeGlobalObject(const JSHandle &env, const JSHa SetFunction(env, globalObject, "isNaN", Global::IsNaN, FunctionLength::ONE); SetFunction(env, globalObject, "decodeURI", Global::DecodeURI, FunctionLength::ONE); SetFunction(env, globalObject, "encodeURI", Global::EncodeURI, FunctionLength::ONE); + SetFunction(env, globalObject, "escape", Global::Escape, FunctionLength::ONE); + SetFunction(env, globalObject, "unescape", Global::Unescape, FunctionLength::ONE); SetFunction(env, globalObject, "decodeURIComponent", Global::DecodeURIComponent, FunctionLength::ONE); SetFunction(env, globalObject, "encodeURIComponent", Global::EncodeURIComponent, FunctionLength::ONE); @@ -515,7 +529,8 @@ void Builtins::InitializeFunction(const JSHandle &env, const JSHandle // Function.prototype method // 19.2.3.1 Function.prototype.apply ( thisArg, argArray ) - SetFunction(env, funcFuncPrototypeObj, "apply", Function::FunctionPrototypeApply, FunctionLength::TWO); + SetFunction(env, funcFuncPrototypeObj, "apply", Function::FunctionPrototypeApply, FunctionLength::TWO, + BUILTINS_STUB_ID(FunctionPrototypeApply)); // 19.2.3.2 Function.prototype.bind ( thisArg , ...args) SetFunction(env, funcFuncPrototypeObj, "bind", Function::FunctionPrototypeBind, FunctionLength::ONE); // 19.2.3.3 Function.prototype.call (thisArg , ...args) @@ -530,64 +545,15 @@ void Builtins::InitializeObject(const JSHandle &env, const JSHandleGlobalConstants()->GetHandledToStringString(), Object::ToString, - FunctionLength::ZERO); - // 19.1.3.7 Object.prototype.valueOf() - SetFunction(env, objFuncPrototype, thread_->GlobalConstants()->GetHandledValueOfString(), Object::ValueOf, - FunctionLength::ZERO); - - SetFunction(env, objFuncPrototype, "createRealm", Object::CreateRealm, FunctionLength::ZERO); + for (const base::BuiltinFunctionEntry &entry: Object::GetObjectFunctions()) { + SetFunction(env, objFunc, entry.GetName(), entry.GetEntrypoint(), + entry.GetLength(), entry.GetBuiltinStubId()); + } + // Object.prototype method + for (const base::BuiltinFunctionEntry &entry: Object::GetObjectPrototypeFunctions()) { + SetFunction(env, objFuncPrototype, entry.GetName(), entry.GetEntrypoint(), + entry.GetLength(), entry.GetBuiltinStubId()); + } // B.2.2.1 Object.prototype.__proto__ JSHandle protoKey(factory_->NewFromASCII("__proto__")); @@ -618,8 +584,10 @@ void Builtins::InitializeSymbol(const JSHandle &env, const JSHandle::Cast(symbolFunction), true, false, true); JSObject::DefineOwnProperty(thread_, symbolFuncPrototype, constructorKey, descriptor); - SetFunction(env, symbolFunction, "for", Symbol::For, FunctionLength::ONE); - SetFunction(env, symbolFunction, "keyFor", Symbol::KeyFor, FunctionLength::ONE); + for (const base::BuiltinFunctionEntry &entry: Symbol::GetSymbolFunctions()) { + SetFunction(env, symbolFunction, entry.GetName(), entry.GetEntrypoint(), + entry.GetLength(), entry.GetBuiltinStubId()); + } // Symbol attribute JSHandle hasInstanceSymbol(factory_->NewWellKnownSymbolWithChar("Symbol.hasInstance")); @@ -636,14 +604,10 @@ void Builtins::InitializeSymbol(const JSHandle &env, const JSHandle matchAllSymbol(factory_->NewPublicSymbolWithChar("Symbol.matchAll")); SetNoneAttributeProperty(symbolFunction, "matchAll", matchAllSymbol); - JSHandle replaceSymbol(factory_->NewPublicSymbolWithChar("Symbol.replace")); - SetNoneAttributeProperty(symbolFunction, "replace", replaceSymbol); JSHandle searchSymbol(factory_->NewPublicSymbolWithChar("Symbol.search")); SetNoneAttributeProperty(symbolFunction, "search", searchSymbol); JSHandle speciesSymbol(factory_->NewPublicSymbolWithChar("Symbol.species")); SetNoneAttributeProperty(symbolFunction, "species", speciesSymbol); - JSHandle splitSymbol(factory_->NewPublicSymbolWithChar("Symbol.split")); - SetNoneAttributeProperty(symbolFunction, "split", splitSymbol); JSHandle toPrimitiveSymbol(factory_->NewPublicSymbolWithChar("Symbol.toPrimitive")); SetNoneAttributeProperty(symbolFunction, "toPrimitive", toPrimitiveSymbol); JSHandle unscopablesSymbol(factory_->NewPublicSymbolWithChar("Symbol.unscopables")); @@ -653,6 +617,32 @@ void Builtins::InitializeSymbol(const JSHandle &env, const JSHandle detachSymbol(factory_->NewPublicSymbolWithChar("Symbol.detach")); SetNoneAttributeProperty(symbolFunction, "detach", detachSymbol); + // Symbol attributes with detectors + // Create symbol string before create symbol to allocate symbol continuously + // Attention: Symbol serialization & deserialization are not supported now and + // the order of symbols and symbol-strings must be maintained too when + // Symbol serialization & deserialization are ready. +#define INIT_SYMBOL_STRING(name, description, key) \ + { \ + [[maybe_unused]] JSHandle string = factory_->NewFromUtf8(description); \ + } +DETECTOR_SYMBOL_LIST(INIT_SYMBOL_STRING) +#undef INIT_SYMBOL_STRING + +#define INIT_PUBLIC_SYMBOL(name, description, key) \ + JSHandle key##Symbol = factory_->NewEmptySymbol(); \ + JSHandle key##String = factory_->NewFromUtf8(description); \ + key##Symbol->SetDescription(thread_, key##String.GetTaggedValue()); \ + key##Symbol->SetHashField(SymbolTable::Hash(key##String.GetTaggedValue())); \ + env->Set##name(thread_, key##Symbol); +DETECTOR_SYMBOL_LIST(INIT_PUBLIC_SYMBOL) +#undef INIT_PUBLIC_SYMBOL + +#define REGISTER_SYMBOL(name, description, key) \ + SetNoneAttributeProperty(symbolFunction, #key, JSHandle(key##Symbol)); +DETECTOR_SYMBOL_LIST(REGISTER_SYMBOL) +#undef REGISTER_SYMBOL + // symbol.prototype.description PropertyDescriptor descriptionDesc(thread_); JSHandle getterKey(factory_->NewFromASCII("description")); @@ -676,10 +666,8 @@ void Builtins::InitializeSymbol(const JSHandle &env, const JSHandleSetAsyncIteratorSymbol(thread_, asyncIteratorSymbol); env->SetMatchSymbol(thread_, matchSymbol); env->SetMatchAllSymbol(thread_, matchAllSymbol); - env->SetReplaceSymbol(thread_, replaceSymbol); env->SetSearchSymbol(thread_, searchSymbol); env->SetSpeciesSymbol(thread_, speciesSymbol); - env->SetSplitSymbol(thread_, splitSymbol); env->SetToPrimitiveSymbol(thread_, toPrimitiveSymbol); env->SetUnscopablesSymbol(thread_, unscopablesSymbol); env->SetAttachSymbol(thread_, attachSymbol); @@ -723,25 +711,18 @@ void Builtins::InitializeSymbolWithRealm(const JSHandle &realm, PropertyDescriptor descriptor(thread_, JSHandle::Cast(symbolFunction), true, false, true); JSObject::DefineOwnProperty(thread_, symbolFuncPrototype, constructorKey, descriptor); - SetFunction(realm, symbolFunction, "for", Symbol::For, FunctionLength::ONE); - SetFunction(realm, symbolFunction, "keyFor", Symbol::KeyFor, FunctionLength::ONE); + for (const base::BuiltinFunctionEntry &entry: Symbol::GetSymbolFunctions()) { + SetFunction(realm, symbolFunction, entry.GetName(), entry.GetEntrypoint(), + entry.GetLength(), entry.GetBuiltinStubId()); + } +#define BUILTIN_SYMBOL_CREATE_WITH_REALM(name, Name) \ + SetNoneAttributeProperty(symbolFunction, #name, env->Get##Name##Symbol()); \ + realm->Set##Name##Symbol(thread_, env->Get##Name##Symbol()); + + realm->SetSymbolFunction(thread_, symbolFunction); // Symbol attribute - SetNoneAttributeProperty(symbolFunction, "hasInstance", env->GetHasInstanceSymbol()); - SetNoneAttributeProperty(symbolFunction, "isConcatSpreadable", env->GetIsConcatSpreadableSymbol()); - SetNoneAttributeProperty(symbolFunction, "toStringTag", env->GetToStringTagSymbol()); - SetNoneAttributeProperty(symbolFunction, "iterator", env->GetIteratorSymbol()); - SetNoneAttributeProperty(symbolFunction, "asyncIterator", env->GetAsyncIteratorSymbol()); - SetNoneAttributeProperty(symbolFunction, "match", env->GetMatchSymbol()); - SetNoneAttributeProperty(symbolFunction, "matchAll", env->GetMatchAllSymbol()); - SetNoneAttributeProperty(symbolFunction, "replace", env->GetReplaceSymbol()); - SetNoneAttributeProperty(symbolFunction, "search", env->GetSearchSymbol()); - SetNoneAttributeProperty(symbolFunction, "species", env->GetSpeciesSymbol()); - SetNoneAttributeProperty(symbolFunction, "split", env->GetSplitSymbol()); - SetNoneAttributeProperty(symbolFunction, "toPrimitive", env->GetToPrimitiveSymbol()); - SetNoneAttributeProperty(symbolFunction, "unscopables", env->GetUnscopablesSymbol()); - SetNoneAttributeProperty(symbolFunction, "attach", env->GetAttachSymbol()); - SetNoneAttributeProperty(symbolFunction, "detach", env->GetDetachSymbol()); + BUILTIN_ALL_SYMBOLS(BUILTIN_SYMBOL_CREATE_WITH_REALM) // symbol.prototype.description PropertyDescriptor descriptionDesc(thread_); @@ -754,28 +735,10 @@ void Builtins::InitializeSymbolWithRealm(const JSHandle &realm, "[Symbol.toPrimitive]", Symbol::ToPrimitive, FunctionLength::ONE); // install the Symbol.prototype methods - SetFunction(realm, symbolFuncPrototype, thread_->GlobalConstants()->GetHandledToStringString(), Symbol::ToString, - FunctionLength::ZERO); - SetFunction(realm, symbolFuncPrototype, thread_->GlobalConstants()->GetHandledValueOfString(), Symbol::ValueOf, - FunctionLength::ZERO); - - realm->SetSymbolFunction(thread_, symbolFunction); - realm->SetHasInstanceSymbol(thread_, env->GetHasInstanceSymbol()); - realm->SetIsConcatSpreadableSymbol(thread_, env->GetIsConcatSpreadableSymbol()); - realm->SetToStringTagSymbol(thread_, env->GetToStringTagSymbol()); - realm->SetIteratorSymbol(thread_, env->GetIteratorSymbol()); - realm->SetAsyncIteratorSymbol(thread_, env->GetAsyncIteratorSymbol()); - realm->SetMatchSymbol(thread_, env->GetMatchSymbol()); - realm->SetMatchAllSymbol(thread_, env->GetMatchAllSymbol()); - realm->SetReplaceSymbol(thread_, env->GetReplaceSymbol()); - realm->SetSearchSymbol(thread_, env->GetSearchSymbol()); - realm->SetSpeciesSymbol(thread_, env->GetSpeciesSymbol()); - realm->SetSplitSymbol(thread_, env->GetSplitSymbol()); - realm->SetToPrimitiveSymbol(thread_, env->GetToPrimitiveSymbol()); - realm->SetUnscopablesSymbol(thread_, env->GetUnscopablesSymbol()); - realm->SetAttachSymbol(thread_, env->GetAttachSymbol()); - realm->SetDetachSymbol(thread_, env->GetDetachSymbol()); - + for (const base::BuiltinFunctionEntry &entry: Symbol::GetSymbolPrototypeFunctions()) { + SetFunction(realm, symbolFuncPrototype, entry.GetName(), entry.GetEntrypoint(), + entry.GetLength(), entry.GetBuiltinStubId()); + } // Setup %SymbolPrototype% SetStringTagSymbol(realm, symbolFuncPrototype, "Symbol"); @@ -790,6 +753,7 @@ void Builtins::InitializeSymbolWithRealm(const JSHandle &realm, realm, funcFuncPrototypeObj, realm->GetHasInstanceSymbol(), "[Symbol.hasInstance]", Function::FunctionPrototypeHasInstance, FunctionLength::ONE); } +#undef BUILTIN_SYMBOL_CREATE_WITH_REALM void Builtins::InitializeNumber(const JSHandle &env, const JSHandle &globalObject, const JSHandle &primRefObjHClass) @@ -811,37 +775,23 @@ void Builtins::InitializeNumber(const JSHandle &env, const JSHandle()->SetFunctionPrototype(thread_, numFuncInstanceHClass.GetTaggedValue()); // Number.prototype method - SetFunction(env, numFuncPrototype, "toExponential", Number::ToExponential, FunctionLength::ONE); - SetFunction(env, numFuncPrototype, "toFixed", Number::ToFixed, FunctionLength::ONE); - SetFunction(env, numFuncPrototype, "toLocaleString", Number::ToLocaleString, FunctionLength::ZERO); - SetFunction(env, numFuncPrototype, "toPrecision", Number::ToPrecision, FunctionLength::ONE); - SetFunction(env, numFuncPrototype, thread_->GlobalConstants()->GetHandledToStringString(), Number::ToString, - FunctionLength::ONE); - SetFunction(env, numFuncPrototype, thread_->GlobalConstants()->GetHandledValueOfString(), Number::ValueOf, - FunctionLength::ZERO); - + for (const base::BuiltinFunctionEntry &entry: Number::GetNumberPrototypeFunctions()) { + SetFunction(env, numFuncPrototype, entry.GetName(), entry.GetEntrypoint(), + entry.GetLength(), entry.GetBuiltinStubId()); + } // Number method - SetFunction(env, numFunction, "isFinite", Number::IsFinite, FunctionLength::ONE); - SetFunction(env, numFunction, "isInteger", Number::IsInteger, FunctionLength::ONE); - SetFunction(env, numFunction, "isNaN", Number::IsNaN, FunctionLength::ONE); - SetFunction(env, numFunction, "isSafeInteger", Number::IsSafeInteger, FunctionLength::ONE); - SetFuncToObjAndGlobal(env, globalObject, numFunction, "parseFloat", Number::ParseFloat, FunctionLength::ONE); - SetFuncToObjAndGlobal(env, globalObject, numFunction, "parseInt", Number::ParseInt, FunctionLength::TWO); - + for (const base::BuiltinFunctionEntry &entry: Number::GetNumberNonGlobalFunctions()) { + SetFunction(env, numFunction, entry.GetName(), entry.GetEntrypoint(), + entry.GetLength(), entry.GetBuiltinStubId()); + } + for (const base::BuiltinFunctionEntry &entry: Number::GetNumberGlobalFunctions()) { + SetFuncToObjAndGlobal(env, globalObject, numFunction, + entry.GetName(), entry.GetEntrypoint(), entry.GetLength()); + } // Number constant - const double epsilon = 2.220446049250313e-16; - const double maxSafeInteger = 9007199254740991; - const double maxValue = 1.7976931348623157e+308; - const double minValue = 5e-324; - const double positiveInfinity = std::numeric_limits::infinity(); - SetConstant(numFunction, "MAX_VALUE", JSTaggedValue(maxValue)); - SetConstant(numFunction, "MIN_VALUE", JSTaggedValue(minValue)); - SetConstant(numFunction, "NaN", JSTaggedValue(NAN)); - SetConstant(numFunction, "NEGATIVE_INFINITY", JSTaggedValue(-positiveInfinity)); - SetConstant(numFunction, "POSITIVE_INFINITY", JSTaggedValue(positiveInfinity)); - SetConstant(numFunction, "MAX_SAFE_INTEGER", JSTaggedValue(maxSafeInteger)); - SetConstant(numFunction, "MIN_SAFE_INTEGER", JSTaggedValue(-maxSafeInteger)); - SetConstant(numFunction, "EPSILON", JSTaggedValue(epsilon)); + for (const base::BuiltinConstantEntry &entry: Number::GetNumberConstants()) { + SetConstant(numFunction, entry.GetName(), entry.GetTaggedValue()); + } env->SetNumberFunction(thread_, numFunction); } @@ -890,7 +840,6 @@ void Builtins::InitializeBigInt(const JSHandle &env, const JSHandle &env, const JSHandle &objFuncClass) const { [[maybe_unused]] EcmaHandleScope scope(thread_); - constexpr int utcLength = 7; // Date.prototype JSHandle dateFuncPrototype = factory_->NewJSObjectWithInit(objFuncClass); JSHandle dateFuncPrototypeValue(dateFuncPrototype); @@ -906,64 +855,20 @@ void Builtins::InitializeDate(const JSHandle &env, const JSHandle(dateFunction)->SetFunctionPrototype(thread_, dateFuncInstanceHClass.GetTaggedValue()); // Date.prototype method - SetFunction(env, dateFuncPrototype, "getDate", Date::GetDate, FunctionLength::ZERO); - SetFunction(env, dateFuncPrototype, "getDay", Date::GetDay, FunctionLength::ZERO); - SetFunction(env, dateFuncPrototype, "getFullYear", Date::GetFullYear, FunctionLength::ZERO); - SetFunction(env, dateFuncPrototype, "getHours", Date::GetHours, FunctionLength::ZERO); - SetFunction(env, dateFuncPrototype, "getMilliseconds", Date::GetMilliseconds, FunctionLength::ZERO); - SetFunction(env, dateFuncPrototype, "getMinutes", Date::GetMinutes, FunctionLength::ZERO); - SetFunction(env, dateFuncPrototype, "getMonth", Date::GetMonth, FunctionLength::ZERO); - SetFunction(env, dateFuncPrototype, "getSeconds", Date::GetSeconds, FunctionLength::ZERO); - SetFunction(env, dateFuncPrototype, "getTime", Date::GetTime, FunctionLength::ZERO); - SetFunction(env, dateFuncPrototype, "getTimezoneOffset", Date::GetTimezoneOffset, FunctionLength::ZERO); - SetFunction(env, dateFuncPrototype, "getUTCDate", Date::GetUTCDate, FunctionLength::ZERO); - SetFunction(env, dateFuncPrototype, "getUTCDay", Date::GetUTCDay, FunctionLength::ZERO); - SetFunction(env, dateFuncPrototype, "getUTCFullYear", Date::GetUTCFullYear, FunctionLength::ZERO); - SetFunction(env, dateFuncPrototype, "getUTCHours", Date::GetUTCHours, FunctionLength::ZERO); - SetFunction(env, dateFuncPrototype, "getUTCMilliseconds", Date::GetUTCMilliseconds, FunctionLength::ZERO); - SetFunction(env, dateFuncPrototype, "getUTCMinutes", Date::GetUTCMinutes, FunctionLength::ZERO); - SetFunction(env, dateFuncPrototype, "getUTCMonth", Date::GetUTCMonth, FunctionLength::ZERO); - SetFunction(env, dateFuncPrototype, "getUTCSeconds", Date::GetUTCSeconds, FunctionLength::ZERO); - - SetFunction(env, dateFuncPrototype, "setDate", Date::SetDate, FunctionLength::ONE); - SetFunction(env, dateFuncPrototype, "setFullYear", Date::SetFullYear, FunctionLength::THREE); - SetFunction(env, dateFuncPrototype, "setHours", Date::SetHours, FunctionLength::FOUR); - SetFunction(env, dateFuncPrototype, "setMilliseconds", Date::SetMilliseconds, FunctionLength::ONE); - SetFunction(env, dateFuncPrototype, "setMinutes", Date::SetMinutes, FunctionLength::THREE); - SetFunction(env, dateFuncPrototype, "setMonth", Date::SetMonth, FunctionLength::TWO); - SetFunction(env, dateFuncPrototype, "setSeconds", Date::SetSeconds, FunctionLength::TWO); - SetFunction(env, dateFuncPrototype, "setTime", Date::SetTime, FunctionLength::ONE); - SetFunction(env, dateFuncPrototype, "setUTCDate", Date::SetUTCDate, FunctionLength::ONE); - SetFunction(env, dateFuncPrototype, "setUTCFullYear", Date::SetUTCFullYear, FunctionLength::THREE); - SetFunction(env, dateFuncPrototype, "setUTCHours", Date::SetUTCHours, FunctionLength::FOUR); - SetFunction(env, dateFuncPrototype, "setUTCMilliseconds", Date::SetUTCMilliseconds, FunctionLength::ONE); - SetFunction(env, dateFuncPrototype, "setUTCMinutes", Date::SetUTCMinutes, FunctionLength::THREE); - SetFunction(env, dateFuncPrototype, "setUTCMonth", Date::SetUTCMonth, FunctionLength::TWO); - SetFunction(env, dateFuncPrototype, "setUTCSeconds", Date::SetUTCSeconds, FunctionLength::TWO); - - SetFunction(env, dateFuncPrototype, "toDateString", Date::ToDateString, FunctionLength::ZERO); - SetFunction(env, dateFuncPrototype, "toISOString", Date::ToISOString, FunctionLength::ZERO); - SetFunction(env, dateFuncPrototype, "toJSON", Date::ToJSON, FunctionLength::ONE); - SetFunction(env, dateFuncPrototype, "toLocaleDateString", Date::ToLocaleDateString, FunctionLength::ZERO); - SetFunction(env, dateFuncPrototype, "toLocaleString", Date::ToLocaleString, FunctionLength::ZERO); - SetFunction(env, dateFuncPrototype, "toLocaleTimeString", Date::ToLocaleTimeString, FunctionLength::ZERO); - SetFunction(env, dateFuncPrototype, thread_->GlobalConstants()->GetHandledToStringString(), Date::ToString, - FunctionLength::ZERO); - SetFunction(env, dateFuncPrototype, "toTimeString", Date::ToTimeString, FunctionLength::ZERO); - SetFunction(env, dateFuncPrototype, "toUTCString", Date::ToUTCString, FunctionLength::ZERO); - SetFunction(env, dateFuncPrototype, thread_->GlobalConstants()->GetHandledValueOfString(), Date::ValueOf, - FunctionLength::ZERO); - + for (const base::BuiltinFunctionEntry &entry: Date::GetDatePrototypeFunctions()) { + SetFunction(env, dateFuncPrototype, entry.GetName(), entry.GetEntrypoint(), + entry.GetLength(), entry.GetBuiltinStubId()); + } SetFunctionAtSymbol(env, dateFuncPrototype, env->GetToPrimitiveSymbol(), "[Symbol.toPrimitive]", Date::ToPrimitive, FunctionLength::ONE); // Date method - SetFunction(env, dateFunction, "now", Date::Now, FunctionLength::ZERO); - SetFunction(env, dateFunction, "parse", Date::Parse, FunctionLength::ONE); - SetFunction(env, dateFunction, "UTC", Date::UTC, utcLength); - + for (const base::BuiltinFunctionEntry &entry: Date::GetDateFunctions()) { + SetFunction(env, dateFunction, entry.GetName(), entry.GetEntrypoint(), + entry.GetLength(), entry.GetBuiltinStubId()); + } // Date.length - SetConstant(dateFunction, "length", JSTaggedValue(utcLength)); + SetConstant(dateFunction, "length", JSTaggedValue(Date::UTC_LENGTH)); env->SetDateFunction(thread_, dateFunction); } @@ -1015,7 +920,7 @@ void Builtins::InitializeProxy(const JSHandle &env) } JSHandle Builtins::InitializeExoticConstructor(const JSHandle &env, EcmaEntrypoint ctorFunc, - const char *name, int length) + std::string_view name, int length) { JSHandle ctor = factory_->NewJSFunction(env, reinterpret_cast(ctorFunc), FunctionKind::BUILTIN_PROXY_CONSTRUCTOR); @@ -1171,7 +1076,7 @@ void Builtins::SetErrorWithRealm(const JSHandle &realm, const JSType } void Builtins::GeneralUpdateError(ErrorParameter *error, EcmaEntrypoint constructor, EcmaEntrypoint method, - const char *name, JSType type) const + std::string_view name, JSType type) const { error->nativeConstructor = constructor; error->nativeMethod = method; @@ -1274,7 +1179,7 @@ void Builtins::InitializeError(const JSHandle &env, const JSHandle &env, const JSHandle &prototype, - const JSHandle &ctor, const char *name, int length) const + const JSHandle &ctor, std::string_view name, int length) const { const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); JSFunction::SetFunctionLength(thread_, ctor, JSTaggedValue(length)); @@ -1313,25 +1218,18 @@ void Builtins::InitializeSet(const JSHandle &env, const JSHandle constructorKey = globalConst->GetHandledConstructorString(); JSObject::SetProperty(thread_, JSHandle(setFuncPrototype), constructorKey, setFunction); - // set.prototype.add() - SetFunction(env, setFuncPrototype, "add", BuiltinsSet::Add, FunctionLength::ONE); - // set.prototype.clear() - SetFunction(env, setFuncPrototype, "clear", BuiltinsSet::Clear, FunctionLength::ZERO); - // set.prototype.delete() - SetFunction(env, setFuncPrototype, "delete", BuiltinsSet::Delete, FunctionLength::ONE); - // set.prototype.has() - SetFunction(env, setFuncPrototype, "has", BuiltinsSet::Has, FunctionLength::ONE); - // set.prototype.forEach() - SetFunction(env, setFuncPrototype, "forEach", BuiltinsSet::ForEach, FunctionLength::ONE); - // set.prototype.entries() - SetFunction(env, setFuncPrototype, "entries", BuiltinsSet::Entries, FunctionLength::ZERO); - // set.prototype.keys() - SetFunction(env, setFuncPrototype, "values", BuiltinsSet::Values, FunctionLength::ZERO); - // set.prototype.values() + RETURN_IF_ABRUPT_COMPLETION(thread_); + // Set.prototype functions, excluding keys() + for (const base::BuiltinFunctionEntry &entry: BuiltinsSet::GetSetPrototypeFunctions()) { + SetFunction(env, setFuncPrototype, entry.GetName(), entry.GetEntrypoint(), + entry.GetLength(), entry.GetBuiltinStubId()); + } + // Set.prototype.keys, which is strictly equal to Set.prototype.values JSHandle keys(factory_->NewFromASCII("keys")); JSHandle values(factory_->NewFromASCII("values")); JSHandle valuesFunc = JSObject::GetMethod(thread_, JSHandle::Cast(setFuncPrototype), values); + RETURN_IF_ABRUPT_COMPLETION(thread_); PropertyDescriptor descriptor(thread_, valuesFunc, true, false, true); JSObject::DefineOwnProperty(thread_, setFuncPrototype, keys, descriptor); @@ -1386,25 +1284,12 @@ void Builtins::InitializeMap(const JSHandle &env, const JSHandle constructorKey = globalConst->GetHandledConstructorString(); JSObject::SetProperty(thread_, JSHandle(mapFuncPrototype), constructorKey, mapFunction); - // map.prototype.set() - SetFunction(env, mapFuncPrototype, globalConst->GetHandledSetString(), BuiltinsMap::Set, FunctionLength::TWO); - // map.prototype.clear() - SetFunction(env, mapFuncPrototype, "clear", BuiltinsMap::Clear, FunctionLength::ZERO); - // map.prototype.delete() - SetFunction(env, mapFuncPrototype, "delete", BuiltinsMap::Delete, FunctionLength::ONE); - // map.prototype.has() - SetFunction(env, mapFuncPrototype, "has", BuiltinsMap::Has, FunctionLength::ONE); - // map.prototype.get() - SetFunction(env, mapFuncPrototype, thread_->GlobalConstants()->GetHandledGetString(), BuiltinsMap::Get, - FunctionLength::ONE); - // map.prototype.forEach() - SetFunction(env, mapFuncPrototype, "forEach", BuiltinsMap::ForEach, FunctionLength::ONE); - // map.prototype.keys() - SetFunction(env, mapFuncPrototype, "keys", BuiltinsMap::Keys, FunctionLength::ZERO); - // map.prototype.values() - SetFunction(env, mapFuncPrototype, "values", BuiltinsMap::Values, FunctionLength::ZERO); - // map.prototype.entries() - SetFunction(env, mapFuncPrototype, "entries", BuiltinsMap::Entries, FunctionLength::ZERO); + RETURN_IF_ABRUPT_COMPLETION(thread_); + // Map.prototype functions + for (const base::BuiltinFunctionEntry &entry: BuiltinsMap::GetMapPrototypeFunctions()) { + SetFunction(env, mapFuncPrototype, entry.GetName(), entry.GetEntrypoint(), + entry.GetLength(), entry.GetBuiltinStubId()); + } // @@ToStringTag SetStringTagSymbol(env, mapFuncPrototype, "Map"); @@ -1424,6 +1309,7 @@ void Builtins::InitializeMap(const JSHandle &env, const JSHandle entries(factory_->NewFromASCII("entries")); JSHandle entriesFunc = JSObject::GetMethod(thread_, JSHandle::Cast(mapFuncPrototype), entries); + RETURN_IF_ABRUPT_COMPLETION(thread_); PropertyDescriptor descriptor(thread_, entriesFunc, true, false, true); JSObject::DefineOwnProperty(thread_, mapFuncPrototype, iteratorSymbol, descriptor); @@ -1462,6 +1348,7 @@ void Builtins::InitializeWeakMap(const JSHandle &env, const JSHandle< // "constructor" property on the prototype JSHandle constructorKey = globalConst->GetHandledConstructorString(); JSObject::SetProperty(thread_, JSHandle(weakMapFuncPrototype), constructorKey, weakMapFunction); + RETURN_IF_ABRUPT_COMPLETION(thread_); // weakmap.prototype.set() SetFunction(env, weakMapFuncPrototype, globalConst->GetHandledSetString(), BuiltinsWeakMap::Set, FunctionLength::TWO); @@ -1506,6 +1393,7 @@ void Builtins::InitializeWeakSet(const JSHandle &env, const JSHandle< // "constructor" property on the prototype JSHandle constructorKey = globalConst->GetHandledConstructorString(); JSObject::SetProperty(thread_, JSHandle(weakSetFuncPrototype), constructorKey, weakSetFunction); + RETURN_IF_ABRUPT_COMPLETION(thread_); // set.prototype.add() SetFunction(env, weakSetFuncPrototype, "add", BuiltinsWeakSet::Add, FunctionLength::ONE); // set.prototype.delete() @@ -1536,18 +1424,11 @@ void Builtins::InitializeAtomics(const JSHandle &env, JSHandle atomicsHClass = factory_->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, objFuncPrototypeVal); JSHandle atomicsObject = factory_->NewJSObject(atomicsHClass); - SetFunction(env, atomicsObject, "add", Atomics::Add, FunctionLength::THREE); - SetFunction(env, atomicsObject, "and", Atomics::And, FunctionLength::THREE); - SetFunction(env, atomicsObject, "sub", Atomics::Sub, FunctionLength::THREE); - SetFunction(env, atomicsObject, "or", Atomics::Or, FunctionLength::THREE); - SetFunction(env, atomicsObject, "xor", Atomics::Xor, FunctionLength::THREE); - SetFunction(env, atomicsObject, "compareExchange", Atomics::CompareExchange, FunctionLength::FOUR); - SetFunction(env, atomicsObject, "exchange", Atomics::Exchange, FunctionLength::THREE); - SetFunction(env, atomicsObject, "isLockFree", Atomics::IsLockFree, FunctionLength::ONE); - SetFunction(env, atomicsObject, "load", Atomics::Load, FunctionLength::TWO); - SetFunction(env, atomicsObject, "store", Atomics::Store, FunctionLength::THREE); - SetFunction(env, atomicsObject, "wait", Atomics::Wait, FunctionLength::FOUR); - SetFunction(env, atomicsObject, "notify", Atomics::Notify, FunctionLength::THREE); + // Atomics functions + for (const base::BuiltinFunctionEntry &entry: Atomics::GetAtomicsFunctions()) { + SetFunction(env, atomicsObject, entry.GetName(), entry.GetEntrypoint(), + entry.GetLength(), entry.GetBuiltinStubId()); + } JSHandle atomicsString(factory_->NewFromASCII("Atomics")); JSHandle globalObject(thread_, env->GetGlobalObject()); PropertyDescriptor atomicsDesc(thread_, JSHandle::Cast(atomicsObject), true, false, true); @@ -1575,6 +1456,7 @@ void Builtins::InitializeWeakRef(const JSHandle &env, const JSHandle< // "constructor" property on the prototype JSHandle constructorKey = globalConst->GetHandledConstructorString(); JSObject::SetProperty(thread_, JSHandle(weakRefFuncPrototype), constructorKey, weakRefFunction); + RETURN_IF_ABRUPT_COMPLETION(thread_); // WeakRef.prototype.deref() SetFunction(env, weakRefFuncPrototype, "deref", BuiltinsWeakRef::Deref, FunctionLength::ZERO); @@ -1617,6 +1499,7 @@ void Builtins::InitializeFinalizationRegistry(const JSHandle &env, JSHandle constructorKey = globalConst->GetHandledConstructorString(); JSObject::SetProperty(thread_, JSHandle(finalizationRegistryFuncPrototype), constructorKey, finalizationRegistryFunction); + RETURN_IF_ABRUPT_COMPLETION(thread_); // FinalizationRegistry.prototype.deref() SetFunction(env, finalizationRegistryFuncPrototype, "register", BuiltinsFinalizationRegistry::Register, FunctionLength::TWO); @@ -1645,50 +1528,14 @@ void Builtins::InitializeMath(const JSHandle &env, const JSHandle mathClass = factory_->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, objFuncPrototypeVal); JSHandle mathObject = factory_->NewJSObjectWithInit(mathClass); RandomGenerator::InitRandom(); - SetFunction(env, mathObject, "abs", Math::Abs, FunctionLength::ONE, BUILTINS_STUB_ID(ABS)); - SetFunction(env, mathObject, "acos", Math::Acos, FunctionLength::ONE, BUILTINS_STUB_ID(ACOS)); - SetFunction(env, mathObject, "acosh", Math::Acosh, FunctionLength::ONE); - SetFunction(env, mathObject, "asin", Math::Asin, FunctionLength::ONE); - SetFunction(env, mathObject, "asinh", Math::Asinh, FunctionLength::ONE); - SetFunction(env, mathObject, "atan", Math::Atan, FunctionLength::ONE, BUILTINS_STUB_ID(ATAN)); - SetFunction(env, mathObject, "atanh", Math::Atanh, FunctionLength::ONE); - SetFunction(env, mathObject, "atan2", Math::Atan2, FunctionLength::TWO); - SetFunction(env, mathObject, "cbrt", Math::Cbrt, FunctionLength::ONE); - SetFunction(env, mathObject, "ceil", Math::Ceil, FunctionLength::ONE); - SetFunction(env, mathObject, "clz32", Math::Clz32, FunctionLength::ONE); - SetFunction(env, mathObject, "cos", Math::Cos, FunctionLength::ONE, BUILTINS_STUB_ID(COS)); - SetFunction(env, mathObject, "cosh", Math::Cosh, FunctionLength::ONE); - SetFunction(env, mathObject, "exp", Math::Exp, FunctionLength::ONE); - SetFunction(env, mathObject, "expm1", Math::Expm1, FunctionLength::ONE); - SetFunction(env, mathObject, "floor", Math::Floor, FunctionLength::ONE, BUILTINS_STUB_ID(FLOOR)); - SetFunction(env, mathObject, "fround", Math::Fround, FunctionLength::ONE); - SetFunction(env, mathObject, "hypot", Math::Hypot, FunctionLength::TWO); - SetFunction(env, mathObject, "imul", Math::Imul, FunctionLength::TWO); - SetFunction(env, mathObject, "log", Math::Log, FunctionLength::ONE); - SetFunction(env, mathObject, "log1p", Math::Log1p, FunctionLength::ONE); - SetFunction(env, mathObject, "log10", Math::Log10, FunctionLength::ONE); - SetFunction(env, mathObject, "log2", Math::Log2, FunctionLength::ONE); - SetFunction(env, mathObject, "max", Math::Max, FunctionLength::TWO); - SetFunction(env, mathObject, "min", Math::Min, FunctionLength::TWO); - SetFunction(env, mathObject, "pow", Math::Pow, FunctionLength::TWO); - SetFunction(env, mathObject, "random", Math::Random, FunctionLength::ZERO); - SetFunction(env, mathObject, "round", Math::Round, FunctionLength::ONE); - SetFunction(env, mathObject, "sign", Math::Sign, FunctionLength::ONE); - SetFunction(env, mathObject, "sin", Math::Sin, FunctionLength::ONE, BUILTINS_STUB_ID(SIN)); - SetFunction(env, mathObject, "sinh", Math::Sinh, FunctionLength::ONE); - SetFunction(env, mathObject, "sqrt", Math::Sqrt, FunctionLength::ONE, BUILTINS_STUB_ID(SQRT)); - SetFunction(env, mathObject, "tan", Math::Tan, FunctionLength::ONE); - SetFunction(env, mathObject, "tanh", Math::Tanh, FunctionLength::ONE); - SetFunction(env, mathObject, "trunc", Math::Trunc, FunctionLength::ONE); - - SetConstant(mathObject, "E", JSTaggedValue(Math::E)); - SetConstant(mathObject, "LN10", JSTaggedValue(Math::LN10)); - SetConstant(mathObject, "LN2", JSTaggedValue(Math::LN2)); - SetConstant(mathObject, "LOG10E", JSTaggedValue(Math::LOG10E)); - SetConstant(mathObject, "LOG2E", JSTaggedValue(Math::LOG2E)); - SetConstant(mathObject, "PI", JSTaggedValue(Math::PI)); - SetConstant(mathObject, "SQRT1_2", JSTaggedValue(Math::SQRT1_2)); - SetConstant(mathObject, "SQRT2", JSTaggedValue(Math::SQRT2)); + + for (const base::BuiltinFunctionEntry &entry: Math::GetMathFunctions()) { + SetFunction(env, mathObject, entry.GetName(), entry.GetEntrypoint(), + entry.GetLength(), entry.GetBuiltinStubId()); + } + for (const base::BuiltinConstantEntry &entry: Math::GetMathConstants()) { + SetConstant(mathObject, entry.GetName(), entry.GetTaggedValue()); + } JSHandle mathString(factory_->NewFromASCII("Math")); JSHandle globalObject(thread_, env->GetGlobalObject()); @@ -1706,7 +1553,7 @@ void Builtins::InitializeJson(const JSHandle &env, const JSHandle jsonObject = factory_->NewJSObjectWithInit(jsonHClass); SetFunction(env, jsonObject, "parse", Json::Parse, FunctionLength::TWO); - SetFunction(env, jsonObject, "stringify", Json::Stringify, FunctionLength::THREE); + SetFunction(env, jsonObject, "stringify", Json::Stringify, FunctionLength::THREE, BUILTINS_STUB_ID(STRINGIFY)); PropertyDescriptor jsonDesc(thread_, JSHandle::Cast(jsonObject), true, false, true); JSHandle jsonString(factory_->NewFromASCII("JSON")); @@ -1736,54 +1583,18 @@ void Builtins::InitializeString(const JSHandle &env, const JSHandle()->SetFunctionPrototype(thread_, stringFuncInstanceHClass.GetTaggedValue()); // String.prototype method - SetFunction(env, stringFuncPrototype, "charAt", BuiltinsString::CharAt, FunctionLength::ONE, - BUILTINS_STUB_ID(CharAt)); - SetFunction(env, stringFuncPrototype, "charCodeAt", BuiltinsString::CharCodeAt, FunctionLength::ONE, - BUILTINS_STUB_ID(CharCodeAt)); - SetFunction(env, stringFuncPrototype, "codePointAt", BuiltinsString::CodePointAt, FunctionLength::ONE); - SetFunction(env, stringFuncPrototype, "concat", BuiltinsString::Concat, FunctionLength::ONE); - SetFunction(env, stringFuncPrototype, "endsWith", BuiltinsString::EndsWith, FunctionLength::ONE); - SetFunction(env, stringFuncPrototype, "includes", BuiltinsString::Includes, FunctionLength::ONE); - SetFunction(env, stringFuncPrototype, "indexOf", BuiltinsString::IndexOf, FunctionLength::ONE, - BUILTINS_STUB_ID(IndexOf)); - SetFunction(env, stringFuncPrototype, "lastIndexOf", BuiltinsString::LastIndexOf, FunctionLength::ONE); - SetFunction(env, stringFuncPrototype, "localeCompare", BuiltinsString::LocaleCompare, FunctionLength::ONE); - SetFunction(env, stringFuncPrototype, "match", BuiltinsString::Match, FunctionLength::ONE); - SetFunction(env, stringFuncPrototype, "matchAll", BuiltinsString::MatchAll, FunctionLength::ONE); - SetFunction(env, stringFuncPrototype, "repeat", BuiltinsString::Repeat, FunctionLength::ONE); - SetFunction(env, stringFuncPrototype, "normalize", BuiltinsString::Normalize, FunctionLength::ZERO); - SetFunction(env, stringFuncPrototype, "padStart", BuiltinsString::PadStart, FunctionLength::ONE); - SetFunction(env, stringFuncPrototype, "padEnd", BuiltinsString::PadEnd, FunctionLength::ONE); - SetFunction(env, stringFuncPrototype, "replace", BuiltinsString::Replace, FunctionLength::TWO); - SetFunction(env, stringFuncPrototype, "replaceAll", BuiltinsString::ReplaceAll, FunctionLength::TWO); - SetFunction(env, stringFuncPrototype, "search", BuiltinsString::Search, FunctionLength::ONE); - SetFunction(env, stringFuncPrototype, "slice", BuiltinsString::Slice, FunctionLength::TWO); - SetFunction(env, stringFuncPrototype, "split", BuiltinsString::Split, FunctionLength::TWO); - SetFunction(env, stringFuncPrototype, "startsWith", BuiltinsString::StartsWith, FunctionLength::ONE); - SetFunction(env, stringFuncPrototype, "substring", BuiltinsString::Substring, FunctionLength::TWO, - BUILTINS_STUB_ID(Substring)); - SetFunction(env, stringFuncPrototype, "substr", BuiltinsString::SubStr, FunctionLength::TWO); - SetFunction(env, stringFuncPrototype, "at", BuiltinsString::At, FunctionLength::ONE); - SetFunction(env, stringFuncPrototype, "toLocaleLowerCase", BuiltinsString::ToLocaleLowerCase, FunctionLength::ZERO); - SetFunction(env, stringFuncPrototype, "toLocaleUpperCase", BuiltinsString::ToLocaleUpperCase, FunctionLength::ZERO); - SetFunction(env, stringFuncPrototype, "toLowerCase", BuiltinsString::ToLowerCase, FunctionLength::ZERO); - SetFunction(env, stringFuncPrototype, thread_->GlobalConstants()->GetHandledToStringString(), - BuiltinsString::ToString, FunctionLength::ZERO); - SetFunction(env, stringFuncPrototype, "toUpperCase", BuiltinsString::ToUpperCase, FunctionLength::ZERO); - SetFunction(env, stringFuncPrototype, "trim", BuiltinsString::Trim, FunctionLength::ZERO); - SetFunction(env, stringFuncPrototype, "trimStart", BuiltinsString::TrimStart, FunctionLength::ZERO); - SetFunction(env, stringFuncPrototype, "trimEnd", BuiltinsString::TrimEnd, FunctionLength::ZERO); - SetFunction(env, stringFuncPrototype, "trimLeft", BuiltinsString::TrimLeft, FunctionLength::ZERO); - SetFunction(env, stringFuncPrototype, "trimRight", BuiltinsString::TrimRight, FunctionLength::ZERO); - SetFunction(env, stringFuncPrototype, thread_->GlobalConstants()->GetHandledValueOfString(), - BuiltinsString::ValueOf, FunctionLength::ZERO); + for (const base::BuiltinFunctionEntry &entry: BuiltinsString::GetStringPrototypeFunctions()) { + SetFunction(env, stringFuncPrototype, entry.GetName(), entry.GetEntrypoint(), + entry.GetLength(), entry.GetBuiltinStubId()); + } SetFunctionAtSymbol(env, stringFuncPrototype, env->GetIteratorSymbol(), "[Symbol.iterator]", BuiltinsString::GetStringIterator, FunctionLength::ZERO); // String method - SetFunction(env, stringFunction, "fromCharCode", BuiltinsString::FromCharCode, FunctionLength::ONE); - SetFunction(env, stringFunction, "fromCodePoint", BuiltinsString::FromCodePoint, FunctionLength::ONE); - SetFunction(env, stringFunction, "raw", BuiltinsString::Raw, FunctionLength::ONE); + for (const base::BuiltinFunctionEntry &entry: BuiltinsString::GetStringFunctions()) { + SetFunction(env, stringFunction, entry.GetName(), entry.GetEntrypoint(), + entry.GetLength(), entry.GetBuiltinStubId()); + } // String.prototype.length JSHandle lengthGetter = CreateGetter(env, BuiltinsString::GetLength, "length", FunctionLength::ZERO); @@ -1791,6 +1602,7 @@ void Builtins::InitializeString(const JSHandle &env, const JSHandleSetStringFunction(thread_, stringFunction); + env->SetStringPrototype(thread_, stringFuncPrototype); } void Builtins::InitializeStringIterator(const JSHandle &env, @@ -1865,7 +1677,7 @@ void Builtins::InitializeIterator(const JSHandle &env, const JSHandle globalConst->SetConstant(ConstantIndex::JS_API_ITERATOR_FUNC_CLASS_INDEX, iteratorFuncClass); // Iterator result hclass - JSHandle iterResultHClass = factory_->CreateIteratorResultInstanceClass(); + JSHandle iterResultHClass = factory_->CreateIteratorResultInstanceClass(env); globalConst->SetConstant(ConstantIndex::ITERATOR_RESULT_CLASS, iterResultHClass); // ues for CloseIterator @@ -1986,7 +1798,7 @@ void Builtins::InitializeRegExp(const JSHandle &env) const GlobalEnvConstants *globalConstants = thread_->GlobalConstants(); // RegExp.prototype method - SetFunction(env, regPrototype, "exec", RegExp::Exec, FunctionLength::ONE); + JSHandle execFunc = SetAndReturnFunction(env, regPrototype, "exec", RegExp::Exec, FunctionLength::ONE); SetFunction(env, regPrototype, "test", RegExp::Test, FunctionLength::ONE); SetFunction(env, regPrototype, globalConstants->GetHandledToStringString(), RegExp::ToString, FunctionLength::ZERO); @@ -2003,6 +1815,11 @@ void Builtins::InitializeRegExp(const JSHandle &env) JSHandle globalKey(globalConstants->GetHandledGlobalString()); SetGetter(regPrototype, globalKey, globalGetter); + JSHandle hasIndicesGetter = + CreateGetter(env, RegExp::GetHasIndices, "hasIndices", FunctionLength::ZERO); + JSHandle hasIndicesKey(factory_->NewFromASCII("hasIndices")); + SetGetter(regPrototype, hasIndicesKey, hasIndicesGetter); + JSHandle ignoreCaseGetter = CreateGetter(env, RegExp::GetIgnoreCase, "ignoreCase", FunctionLength::ZERO); JSHandle ignoreCaseKey(factory_->NewFromASCII("ignoreCase")); @@ -2046,6 +1863,8 @@ void Builtins::InitializeRegExp(const JSHandle &env) FunctionLength::TWO); env->SetRegExpFunction(thread_, regexpFunction); + env->SetRegExpPrototype(thread_, regPrototype); + env->SetRegExpExecFunction(thread_, execFunc); auto globalConst = const_cast(thread_->GlobalConstants()); globalConst->SetConstant(ConstantIndex::JS_REGEXP_CLASS_INDEX, regexpFuncInstanceHClass.GetTaggedValue()); } @@ -2058,13 +1877,15 @@ void Builtins::InitializeArray(const JSHandle &env, const JSHandle arrFuncPrototype = factory_->NewJSObjectWithInit(arrBaseFuncInstanceHClass); - JSHandle::Cast(arrFuncPrototype)->SetLength(thread_, JSTaggedValue(FunctionLength::ZERO)); + JSHandle::Cast(arrFuncPrototype)->SetLength(FunctionLength::ZERO); auto accessor = thread_->GlobalConstants()->GetArrayLengthAccessor(); JSArray::Cast(*arrFuncPrototype)->SetPropertyInlinedProps(thread_, JSArray::LENGTH_INLINE_PROPERTY_INDEX, accessor); JSHandle arrFuncPrototypeValue(arrFuncPrototype); // Array.prototype_or_hclass JSHandle arrFuncInstanceHClass = factory_->CreateJSArrayInstanceClass(arrFuncPrototypeValue); + auto globalConstant = const_cast(thread_->GlobalConstants()); + globalConstant->InitElementKindHClass(thread_, arrFuncInstanceHClass); // Array = new Function() JSHandle arrayFunction( @@ -2079,54 +1900,26 @@ void Builtins::InitializeArray(const JSHandle &env, const JSHandleSetFunctionPrototype(thread_, arrFuncInstanceHClass.GetTaggedValue()); - // Array.prototype method - SetFunction(env, arrFuncPrototype, "concat", BuiltinsArray::Concat, FunctionLength::ONE); - SetFunction(env, arrFuncPrototype, "copyWithin", BuiltinsArray::CopyWithin, FunctionLength::TWO); - SetFunction(env, arrFuncPrototype, "entries", BuiltinsArray::Entries, FunctionLength::ZERO); - SetFunction(env, arrFuncPrototype, "every", BuiltinsArray::Every, FunctionLength::ONE); - SetFunction(env, arrFuncPrototype, "fill", BuiltinsArray::Fill, FunctionLength::ONE); - SetFunction(env, arrFuncPrototype, "filter", BuiltinsArray::Filter, FunctionLength::ONE); - SetFunction(env, arrFuncPrototype, "find", BuiltinsArray::Find, FunctionLength::ONE); - SetFunction(env, arrFuncPrototype, "findIndex", BuiltinsArray::FindIndex, FunctionLength::ONE); - SetFunction(env, arrFuncPrototype, "forEach", BuiltinsArray::ForEach, FunctionLength::ONE); - SetFunction(env, arrFuncPrototype, "indexOf", BuiltinsArray::IndexOf, FunctionLength::ONE); - SetFunction(env, arrFuncPrototype, "join", BuiltinsArray::Join, FunctionLength::ONE); - SetFunction(env, arrFuncPrototype, "keys", BuiltinsArray::Keys, FunctionLength::ZERO); - SetFunction(env, arrFuncPrototype, "lastIndexOf", BuiltinsArray::LastIndexOf, FunctionLength::ONE); - SetFunction(env, arrFuncPrototype, "map", BuiltinsArray::Map, FunctionLength::ONE); - SetFunction(env, arrFuncPrototype, "pop", BuiltinsArray::Pop, FunctionLength::ZERO); - SetFunction(env, arrFuncPrototype, "push", BuiltinsArray::Push, FunctionLength::ONE); - SetFunction(env, arrFuncPrototype, "reduce", BuiltinsArray::Reduce, FunctionLength::ONE); - SetFunction(env, arrFuncPrototype, "reduceRight", BuiltinsArray::ReduceRight, FunctionLength::ONE); - SetFunction(env, arrFuncPrototype, "reverse", BuiltinsArray::Reverse, FunctionLength::ZERO); - SetFunction(env, arrFuncPrototype, "shift", BuiltinsArray::Shift, FunctionLength::ZERO); - SetFunction(env, arrFuncPrototype, "slice", BuiltinsArray::Slice, FunctionLength::TWO); - SetFunction(env, arrFuncPrototype, "some", BuiltinsArray::Some, FunctionLength::ONE); - SetFunction(env, arrFuncPrototype, "sort", BuiltinsArray::Sort, FunctionLength::ONE); - SetFunction(env, arrFuncPrototype, "splice", BuiltinsArray::Splice, FunctionLength::TWO); - SetFunction(env, arrFuncPrototype, thread_->GlobalConstants()->GetHandledToLocaleStringString(), - BuiltinsArray::ToLocaleString, FunctionLength::ZERO); - SetFunction(env, arrFuncPrototype, thread_->GlobalConstants()->GetHandledToStringString(), BuiltinsArray::ToString, - FunctionLength::ZERO); - SetFunction(env, arrFuncPrototype, "unshift", BuiltinsArray::Unshift, FunctionLength::ONE); - SetFunction(env, arrFuncPrototype, "values", BuiltinsArray::Values, FunctionLength::ZERO); - SetFunction(env, arrFuncPrototype, "includes", BuiltinsArray::Includes, FunctionLength::ONE); - SetFunction(env, arrFuncPrototype, "flat", BuiltinsArray::Flat, FunctionLength::ZERO); - SetFunction(env, arrFuncPrototype, "flatMap", BuiltinsArray::FlatMap, FunctionLength::ONE); - SetFunction(env, arrFuncPrototype, "at", BuiltinsArray::At, FunctionLength::ONE); + // Array.prototype methods (excluding constructor and '@@' internal properties) + for (const base::BuiltinFunctionEntry &entry: BuiltinsArray::GetArrayPrototypeFunctions()) { + SetFunction(env, arrFuncPrototype, entry.GetName(), entry.GetEntrypoint(), + entry.GetLength(), entry.GetBuiltinStubId()); + } // %ArrayPrototype% [ @@iterator ] JSHandle values(factory_->NewFromASCII("values")); JSHandle iteratorSymbol = env->GetIteratorSymbol(); JSHandle valuesFunc = JSObject::GetMethod(thread_, JSHandle::Cast(arrFuncPrototype), values); + RETURN_IF_ABRUPT_COMPLETION(thread_); PropertyDescriptor iteartorDesc(thread_, valuesFunc, true, false, true); JSObject::DefineOwnProperty(thread_, arrFuncPrototype, iteratorSymbol, iteartorDesc); - // Array method - SetFunction(env, arrayFunction, "from", BuiltinsArray::From, FunctionLength::ONE); - SetFunction(env, arrayFunction, "isArray", BuiltinsArray::IsArray, FunctionLength::ONE); - SetFunction(env, arrayFunction, "of", BuiltinsArray::Of, FunctionLength::ZERO); + // Array methods (excluding '@@' internal properties) + for (const base::BuiltinFunctionEntry &entry: BuiltinsArray::GetArrayFunctions()) { + SetFunction(env, arrayFunction, entry.GetName(), entry.GetEntrypoint(), + entry.GetLength(), entry.GetBuiltinStubId()); + } // 22.1.2.5 get %Array% [ @@species ] JSHandle speciesSymbol = env->GetSpeciesSymbol(); @@ -2174,56 +1967,23 @@ void Builtins::InitializeTypedArray(const JSHandle &env, const JSHand ->SetProtoOrHClass(thread_, typedArrFuncInstanceHClass.GetTaggedValue()); // TypedArray.prototype method - SetFunction(env, typedArrFuncPrototype, "copyWithin", BuiltinsTypedArray::CopyWithin, FunctionLength::TWO); - SetFunction(env, typedArrFuncPrototype, "entries", BuiltinsTypedArray::Entries, FunctionLength::ZERO); - SetFunction(env, typedArrFuncPrototype, "every", BuiltinsTypedArray::Every, FunctionLength::ONE); - SetFunction(env, typedArrFuncPrototype, "fill", BuiltinsTypedArray::Fill, FunctionLength::ONE); - SetFunction(env, typedArrFuncPrototype, "filter", BuiltinsTypedArray::Filter, FunctionLength::ONE); - SetFunction(env, typedArrFuncPrototype, "find", BuiltinsTypedArray::Find, FunctionLength::ONE); - SetFunction(env, typedArrFuncPrototype, "findIndex", BuiltinsTypedArray::FindIndex, FunctionLength::ONE); - SetFunction(env, typedArrFuncPrototype, "forEach", BuiltinsTypedArray::ForEach, FunctionLength::ONE); - SetFunction(env, typedArrFuncPrototype, "indexOf", BuiltinsTypedArray::IndexOf, FunctionLength::ONE); - SetFunction(env, typedArrFuncPrototype, "join", BuiltinsTypedArray::Join, FunctionLength::ONE); - SetFunction(env, typedArrFuncPrototype, "keys", BuiltinsTypedArray::Keys, FunctionLength::ZERO); - SetFunction(env, typedArrFuncPrototype, "lastIndexOf", BuiltinsTypedArray::LastIndexOf, FunctionLength::ONE); - SetFunction(env, typedArrFuncPrototype, "map", BuiltinsTypedArray::Map, FunctionLength::ONE); - SetFunction(env, typedArrFuncPrototype, "reduce", BuiltinsTypedArray::Reduce, FunctionLength::ONE); - SetFunction(env, typedArrFuncPrototype, "reduceRight", BuiltinsTypedArray::ReduceRight, FunctionLength::ONE); - SetFunction(env, typedArrFuncPrototype, "reverse", BuiltinsTypedArray::Reverse, FunctionLength::ZERO); - SetFunction(env, typedArrFuncPrototype, "set", BuiltinsTypedArray::Set, FunctionLength::ONE); - SetFunction(env, typedArrFuncPrototype, "slice", BuiltinsTypedArray::Slice, FunctionLength::TWO); - SetFunction(env, typedArrFuncPrototype, "some", BuiltinsTypedArray::Some, FunctionLength::ONE); - SetFunction(env, typedArrFuncPrototype, "sort", BuiltinsTypedArray::Sort, FunctionLength::ONE); - SetFunction(env, typedArrFuncPrototype, "subarray", BuiltinsTypedArray::Subarray, FunctionLength::TWO); - SetFunction(env, typedArrFuncPrototype, thread_->GlobalConstants()->GetHandledToLocaleStringString(), - BuiltinsTypedArray::ToLocaleString, FunctionLength::ZERO); - SetFunction(env, typedArrFuncPrototype, "values", BuiltinsTypedArray::Values, FunctionLength::ZERO); - SetFunction(env, typedArrFuncPrototype, "includes", BuiltinsTypedArray::Includes, FunctionLength::ONE); - - JSHandle bufferGetter = - CreateGetter(env, BuiltinsTypedArray::GetBuffer, "buffer", FunctionLength::ZERO); - JSHandle bufferKey(factory_->NewFromASCII("buffer")); - SetGetter(typedArrFuncPrototype, bufferKey, bufferGetter); - - JSHandle byteLengthGetter = - CreateGetter(env, BuiltinsTypedArray::GetByteLength, "byteLength", FunctionLength::ZERO); - JSHandle byteLengthKey(factory_->NewFromASCII("byteLength")); - SetGetter(typedArrFuncPrototype, byteLengthKey, byteLengthGetter); - - JSHandle byteOffsetGetter = - CreateGetter(env, BuiltinsTypedArray::GetByteOffset, "byteOffset", FunctionLength::ZERO); - JSHandle byteOffsetKey(factory_->NewFromASCII("byteOffset")); - SetGetter(typedArrFuncPrototype, byteOffsetKey, byteOffsetGetter); - - JSHandle lengthGetter = - CreateGetter(env, BuiltinsTypedArray::GetLength, "length", FunctionLength::ZERO); - JSHandle lengthKey(factory_->NewFromASCII("length")); - SetGetter(typedArrFuncPrototype, lengthKey, lengthGetter); + for (const base::BuiltinFunctionEntry &entry: BuiltinsTypedArray::GetTypedArrayPrototypeFunctions()) { + SetFunction(env, typedArrFuncPrototype, entry.GetName(), entry.GetEntrypoint(), + entry.GetLength(), entry.GetBuiltinStubId()); + } + // TypedArray.prototype get accessor + for (const base::BuiltinFunctionEntry &entry: BuiltinsTypedArray::GetTypedArrayPrototypeAccessors()) { + JSHandle getter = + CreateGetter(env, entry.GetEntrypoint(), entry.GetName(), entry.GetLength()); + JSHandle key(factory_->NewFromASCII(entry.GetName())); + SetGetter(typedArrFuncPrototype, key, getter); + } - // %TypedArray%.prototype.toString() + // %TypedArray%.prototype.toString(), which is strictly equal to Array.prototype.toString JSHandle arrFuncPrototype = env->GetArrayPrototype(); JSHandle toStringFunc = JSObject::GetMethod(thread_, arrFuncPrototype, thread_->GlobalConstants()->GetHandledToStringString()); + RETURN_IF_ABRUPT_COMPLETION(thread_); PropertyDescriptor toStringDesc(thread_, toStringFunc, true, false, true); JSObject::DefineOwnProperty(thread_, typedArrFuncPrototype, thread_->GlobalConstants()->GetHandledToStringString(), toStringDesc); @@ -2233,6 +1993,7 @@ void Builtins::InitializeTypedArray(const JSHandle &env, const JSHand JSHandle iteratorSymbol = env->GetIteratorSymbol(); JSHandle valuesFunc = JSObject::GetMethod(thread_, JSHandle::Cast(typedArrFuncPrototype), values); + RETURN_IF_ABRUPT_COMPLETION(thread_); PropertyDescriptor iteartorDesc(thread_, valuesFunc, true, false, true); JSObject::DefineOwnProperty(thread_, typedArrFuncPrototype, iteratorSymbol, iteartorDesc); @@ -2243,8 +2004,10 @@ void Builtins::InitializeTypedArray(const JSHandle &env, const JSHand SetGetter(typedArrFuncPrototype, toStringTagSymbol, toStringTagGetter); // TypedArray method - SetFunction(env, typedArrayFunction, "from", BuiltinsTypedArray::From, FunctionLength::ONE); - SetFunction(env, typedArrayFunction, "of", BuiltinsTypedArray::Of, FunctionLength::ZERO); + for (const base::BuiltinFunctionEntry &entry: BuiltinsTypedArray::GetTypedArrayFunctions()) { + SetFunction(env, typedArrayFunction, entry.GetName(), entry.GetEntrypoint(), + entry.GetLength(), entry.GetBuiltinStubId()); + } // 22.2.2.4 get %TypedArray% [ @@species ] JSHandle speciesSymbol = env->GetSpeciesSymbol(); @@ -2260,17 +2023,10 @@ void Builtins::InitializeTypedArray(const JSHandle &env, const JSHand specificTypedArrayFuncClass->SetConstructor(true); env->SetSpecificTypedArrayFunctionClass(thread_, specificTypedArrayFuncClass); - InitializeInt8Array(env, typedArrFuncInstanceHClass); - InitializeUint8Array(env, typedArrFuncInstanceHClass); - InitializeUint8ClampedArray(env, typedArrFuncInstanceHClass); - InitializeInt16Array(env, typedArrFuncInstanceHClass); - InitializeUint16Array(env, typedArrFuncInstanceHClass); - InitializeInt32Array(env, typedArrFuncInstanceHClass); - InitializeUint32Array(env, typedArrFuncInstanceHClass); - InitializeFloat32Array(env, typedArrFuncInstanceHClass); - InitializeFloat64Array(env, typedArrFuncInstanceHClass); - InitializeBigInt64Array(env, typedArrFuncInstanceHClass); - InitializeBigUint64Array(env, typedArrFuncInstanceHClass); +#define BUILTIN_TYPED_ARRAY_CALL_INITIALIZE(Type, TYPE, bytesPerElement) \ + Initialize##Type(env, typedArrFuncInstanceHClass); + BUILTIN_TYPED_ARRAY_TYPES(BUILTIN_TYPED_ARRAY_CALL_INITIALIZE) +#undef BUILTIN_TYPED_ARRAY_CALL_INITIALIZE } void Builtins::LazyInitializeTypedArray(const JSHandle &env) const @@ -2284,299 +2040,50 @@ void Builtins::LazyInitializeTypedArray(const JSHandle &env) const env->SetTypedArrayFunction(thread_, accessor); env->SetTypedArrayPrototype(thread_, accessor); env->SetSpecificTypedArrayFunctionClass(thread_, accessor); - LazyInitializeInt8Array(env); - LazyInitializeUint8Array(env); - LazyInitializeUint8ClampedArray(env); - LazyInitializeInt16Array(env); - LazyInitializeUint16Array(env); - LazyInitializeInt32Array(env); - LazyInitializeUint32Array(env); - LazyInitializeFloat32Array(env); - LazyInitializeFloat64Array(env); - LazyInitializeBigInt64Array(env); - LazyInitializeBigUint64Array(env); -} - -void Builtins::InitializeInt8Array(const JSHandle &env, const JSHandle &objFuncClass) const -{ - [[maybe_unused]] EcmaHandleScope scope(thread_); - // Int8Array.prototype - JSHandle int8ArrFuncPrototype = factory_->NewJSObjectWithInit(objFuncClass); - JSHandle int8ArrFuncPrototypeValue(int8ArrFuncPrototype); - // Int8Array.prototype_or_hclass - JSHandle int8ArrFuncInstanceHClass = factory_->NewEcmaHClass( - panda::ecmascript::JSTypedArray::SIZE, JSType::JS_INT8_ARRAY, int8ArrFuncPrototypeValue); - - // Int8Array = new Function() - JSHandle int8ArrayFunction = factory_->NewSpecificTypedArrayFunction( - env, reinterpret_cast(BuiltinsTypedArray::Int8ArrayConstructor)); - InitializeCtor(env, int8ArrFuncPrototype, int8ArrayFunction, "Int8Array", FunctionLength::THREE); - - int8ArrayFunction->SetProtoOrHClass(thread_, int8ArrFuncInstanceHClass.GetTaggedValue()); - - constexpr int bytesPerElement = 1; - SetConstant(int8ArrFuncPrototype, "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); - SetConstant(JSHandle(int8ArrayFunction), "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); - env->SetInt8ArrayFunction(thread_, int8ArrayFunction); -} - -void Builtins::InitializeUint8Array(const JSHandle &env, const JSHandle &objFuncClass) const -{ - [[maybe_unused]] EcmaHandleScope scope(thread_); - // Uint8Array.prototype - JSHandle uint8ArrFuncPrototype = factory_->NewJSObjectWithInit(objFuncClass); - JSHandle uint8ArrFuncPrototypeValue(uint8ArrFuncPrototype); - - // Uint8Array.prototype_or_hclass - JSHandle uint8ArrFuncInstanceHClass = factory_->NewEcmaHClass( - panda::ecmascript::JSTypedArray::SIZE, JSType::JS_UINT8_ARRAY, uint8ArrFuncPrototypeValue); - - // Uint8Array = new Function() - JSHandle uint8ArrayFunction = factory_->NewSpecificTypedArrayFunction( - env, reinterpret_cast(BuiltinsTypedArray::Uint8ArrayConstructor)); - InitializeCtor(env, uint8ArrFuncPrototype, uint8ArrayFunction, "Uint8Array", FunctionLength::THREE); - - uint8ArrayFunction->SetProtoOrHClass(thread_, uint8ArrFuncInstanceHClass.GetTaggedValue()); - - constexpr int bytesPerElement = 1; - SetConstant(uint8ArrFuncPrototype, "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); - SetConstant(JSHandle(uint8ArrayFunction), "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); - env->SetUint8ArrayFunction(thread_, uint8ArrayFunction); -} - -#define TYPED_ARRAY_LAZY_INITIALIZE(type) \ - void Builtins::LazyInitialize##type(const JSHandle &env) const \ - { \ - [[maybe_unused]] EcmaHandleScope scope(thread_); \ - JSHandle globalObject(thread_, env->GetGlobalObject()); \ - JSHandle key(factory_->NewFromUtf8(#type)); \ - auto accessor = factory_->NewInternalAccessor(nullptr, reinterpret_cast(BuiltinsLazyCallback::type)); \ - SetLazyAccessor(globalObject, key, accessor); \ - env->Set##type##Function(thread_, accessor); \ - } - -ITERATE_TYPED_ARRAY(TYPED_ARRAY_LAZY_INITIALIZE) -#undef TYPED_ARRAY_LAZY_INITIALIZE - -void Builtins::InitializeUint8ClampedArray(const JSHandle &env, - const JSHandle &objFuncClass) const -{ - [[maybe_unused]] EcmaHandleScope scope(thread_); - // Uint8ClampedArray.prototype - JSHandle uint8ClampedArrFuncPrototype = factory_->NewJSObjectWithInit(objFuncClass); - JSHandle uint8ClampedArrFuncPrototypeValue(uint8ClampedArrFuncPrototype); - - // Uint8ClampedArray.prototype_or_hclass - JSHandle uint8ClampedArrFuncInstanceHClass = - factory_->NewEcmaHClass(panda::ecmascript::JSTypedArray::SIZE, JSType::JS_UINT8_CLAMPED_ARRAY, - uint8ClampedArrFuncPrototypeValue); - - // Uint8ClampedArray = new Function() - JSHandle uint8ClampedArrayFunction = factory_->NewSpecificTypedArrayFunction( - env, reinterpret_cast(BuiltinsTypedArray::Uint8ClampedArrayConstructor)); - InitializeCtor(env, uint8ClampedArrFuncPrototype, uint8ClampedArrayFunction, "Uint8ClampedArray", - FunctionLength::THREE); - - uint8ClampedArrayFunction->SetProtoOrHClass(thread_, uint8ClampedArrFuncInstanceHClass.GetTaggedValue()); - - constexpr int bytesPerElement = 1; - SetConstant(uint8ClampedArrFuncPrototype, "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); - SetConstant(JSHandle(uint8ClampedArrayFunction), "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); - env->SetUint8ClampedArrayFunction(thread_, uint8ClampedArrayFunction); -} - -void Builtins::InitializeInt16Array(const JSHandle &env, const JSHandle &objFuncClass) const -{ - [[maybe_unused]] EcmaHandleScope scope(thread_); - // Int16Array.prototype - JSHandle int16ArrFuncPrototype = factory_->NewJSObjectWithInit(objFuncClass); - JSHandle int16ArrFuncPrototypeValue(int16ArrFuncPrototype); - - // Int16Array.prototype_or_hclass - JSHandle int16ArrFuncInstanceHClass = factory_->NewEcmaHClass( - panda::ecmascript::JSTypedArray::SIZE, JSType::JS_INT16_ARRAY, int16ArrFuncPrototypeValue); - - // Int16Array = new Function() - JSHandle int16ArrayFunction = factory_->NewSpecificTypedArrayFunction( - env, reinterpret_cast(BuiltinsTypedArray::Int16ArrayConstructor)); - InitializeCtor(env, int16ArrFuncPrototype, int16ArrayFunction, "Int16Array", FunctionLength::THREE); - - int16ArrayFunction->SetProtoOrHClass(thread_, int16ArrFuncInstanceHClass.GetTaggedValue()); - - constexpr int bytesPerElement = 2; - SetConstant(int16ArrFuncPrototype, "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); - SetConstant(JSHandle(int16ArrayFunction), "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); - env->SetInt16ArrayFunction(thread_, int16ArrayFunction); -} - -void Builtins::InitializeUint16Array(const JSHandle &env, const JSHandle &objFuncClass) const -{ - [[maybe_unused]] EcmaHandleScope scope(thread_); - // Uint16Array.prototype - JSHandle uint16ArrFuncPrototype = factory_->NewJSObjectWithInit(objFuncClass); - JSHandle uint16ArrFuncPrototypeValue(uint16ArrFuncPrototype); - - // Uint16Array.prototype_or_hclass - JSHandle uint16ArrFuncInstanceHClass = factory_->NewEcmaHClass( - panda::ecmascript::JSTypedArray::SIZE, JSType::JS_UINT16_ARRAY, uint16ArrFuncPrototypeValue); - - // Uint16Array = new Function() - JSHandle uint16ArrayFunction = factory_->NewSpecificTypedArrayFunction( - env, reinterpret_cast(BuiltinsTypedArray::Uint16ArrayConstructor)); - InitializeCtor(env, uint16ArrFuncPrototype, uint16ArrayFunction, "Uint16Array", FunctionLength::THREE); - - uint16ArrayFunction->SetProtoOrHClass(thread_, uint16ArrFuncInstanceHClass.GetTaggedValue()); - - constexpr int bytesPerElement = 2; - SetConstant(uint16ArrFuncPrototype, "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); - SetConstant(JSHandle(uint16ArrayFunction), "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); - env->SetUint16ArrayFunction(thread_, uint16ArrayFunction); -} - -void Builtins::InitializeInt32Array(const JSHandle &env, const JSHandle &objFuncClass) const -{ - [[maybe_unused]] EcmaHandleScope scope(thread_); - // Int32Array.prototype - JSHandle int32ArrFuncPrototype = factory_->NewJSObjectWithInit(objFuncClass); - JSHandle int32ArrFuncPrototypeValue(int32ArrFuncPrototype); - - // Int32Array.prototype_or_hclass - JSHandle int32ArrFuncInstanceHClass = factory_->NewEcmaHClass( - panda::ecmascript::JSTypedArray::SIZE, JSType::JS_INT32_ARRAY, int32ArrFuncPrototypeValue); - - // Int32Array = new Function() - JSHandle int32ArrayFunction = factory_->NewSpecificTypedArrayFunction( - env, reinterpret_cast(BuiltinsTypedArray::Int32ArrayConstructor)); - InitializeCtor(env, int32ArrFuncPrototype, int32ArrayFunction, "Int32Array", FunctionLength::THREE); - - int32ArrayFunction->SetProtoOrHClass(thread_, int32ArrFuncInstanceHClass.GetTaggedValue()); - - constexpr int bytesPerElement = 4; - SetConstant(int32ArrFuncPrototype, "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); - SetConstant(JSHandle(int32ArrayFunction), "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); - env->SetInt32ArrayFunction(thread_, int32ArrayFunction); -} - -void Builtins::InitializeUint32Array(const JSHandle &env, const JSHandle &objFuncClass) const -{ - [[maybe_unused]] EcmaHandleScope scope(thread_); - // Uint32Array.prototype - JSHandle uint32ArrFuncPrototype = factory_->NewJSObjectWithInit(objFuncClass); - JSHandle uint32ArrFuncPrototypeValue(uint32ArrFuncPrototype); - - // Uint32Array.prototype_or_hclass - JSHandle uint32ArrFuncInstanceHClass = factory_->NewEcmaHClass( - panda::ecmascript::JSTypedArray::SIZE, JSType::JS_UINT32_ARRAY, uint32ArrFuncPrototypeValue); - - // Uint32Array = new Function() - JSHandle uint32ArrayFunction = factory_->NewSpecificTypedArrayFunction( - env, reinterpret_cast(BuiltinsTypedArray::Uint32ArrayConstructor)); - InitializeCtor(env, uint32ArrFuncPrototype, uint32ArrayFunction, "Uint32Array", FunctionLength::THREE); - - uint32ArrayFunction->SetProtoOrHClass(thread_, uint32ArrFuncInstanceHClass.GetTaggedValue()); - - constexpr int bytesPerElement = 4; - SetConstant(uint32ArrFuncPrototype, "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); - SetConstant(JSHandle(uint32ArrayFunction), "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); - env->SetUint32ArrayFunction(thread_, uint32ArrayFunction); -} - -void Builtins::InitializeFloat32Array(const JSHandle &env, const JSHandle &objFuncClass) const -{ - [[maybe_unused]] EcmaHandleScope scope(thread_); - // Float32Array.prototype - JSHandle float32ArrFuncPrototype = factory_->NewJSObjectWithInit(objFuncClass); - JSHandle float32ArrFuncPrototypeValue(float32ArrFuncPrototype); - - // Float32Array.prototype_or_hclass - JSHandle float32ArrFuncInstanceHClass = factory_->NewEcmaHClass( - panda::ecmascript::JSTypedArray::SIZE, JSType::JS_FLOAT32_ARRAY, float32ArrFuncPrototypeValue); - - // Float32Array = new Function() - JSHandle float32ArrayFunction = factory_->NewSpecificTypedArrayFunction( - env, reinterpret_cast(BuiltinsTypedArray::Float32ArrayConstructor)); - InitializeCtor(env, float32ArrFuncPrototype, float32ArrayFunction, "Float32Array", FunctionLength::THREE); - - float32ArrayFunction->SetProtoOrHClass(thread_, float32ArrFuncInstanceHClass.GetTaggedValue()); - - constexpr int bytesPerElement = 4; - SetConstant(float32ArrFuncPrototype, "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); - SetConstant(JSHandle(float32ArrayFunction), "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); - env->SetFloat32ArrayFunction(thread_, float32ArrayFunction); -} - -void Builtins::InitializeFloat64Array(const JSHandle &env, const JSHandle &objFuncClass) const -{ - [[maybe_unused]] EcmaHandleScope scope(thread_); - // Float64Array.prototype - JSHandle float64ArrFuncPrototype = factory_->NewJSObjectWithInit(objFuncClass); - JSHandle float64ArrFuncPrototypeValue(float64ArrFuncPrototype); - - // Float64Array.prototype_or_hclass - JSHandle float64ArrFuncInstanceHClass = factory_->NewEcmaHClass( - panda::ecmascript::JSTypedArray::SIZE, JSType::JS_FLOAT64_ARRAY, float64ArrFuncPrototypeValue); - - // Float64Array = new Function() - JSHandle float64ArrayFunction = factory_->NewSpecificTypedArrayFunction( - env, reinterpret_cast(BuiltinsTypedArray::Float64ArrayConstructor)); - InitializeCtor(env, float64ArrFuncPrototype, float64ArrayFunction, "Float64Array", FunctionLength::THREE); - - float64ArrayFunction->SetProtoOrHClass(thread_, float64ArrFuncInstanceHClass.GetTaggedValue()); - - constexpr int bytesPerElement = 8; - SetConstant(float64ArrFuncPrototype, "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); - SetConstant(JSHandle(float64ArrayFunction), "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); - env->SetFloat64ArrayFunction(thread_, float64ArrayFunction); -} - -void Builtins::InitializeBigInt64Array(const JSHandle &env, const JSHandle &objFuncClass) const -{ - [[maybe_unused]] EcmaHandleScope scope(thread_); - // BigInt64Array.prototype - JSHandle bigInt64ArrFuncPrototype = factory_->NewJSObjectWithInit(objFuncClass); - JSHandle bigInt64ArrFuncPrototypeValue(bigInt64ArrFuncPrototype); - - // BigInt64Array.prototype_or_hclass - JSHandle bigInt64ArrFuncInstanceHClass = factory_->NewEcmaHClass( - panda::ecmascript::JSTypedArray::SIZE, JSType::JS_BIGINT64_ARRAY, bigInt64ArrFuncPrototypeValue); - - // BigInt64Array = new Function() - JSHandle bigInt64ArrayFunction = factory_->NewSpecificTypedArrayFunction( - env, reinterpret_cast(BuiltinsTypedArray::BigInt64ArrayConstructor)); - InitializeCtor(env, bigInt64ArrFuncPrototype, bigInt64ArrayFunction, "BigInt64Array", FunctionLength::THREE); - - bigInt64ArrayFunction->SetProtoOrHClass(thread_, bigInt64ArrFuncInstanceHClass.GetTaggedValue()); - - constexpr int bytesPerElement = 8; - SetConstant(bigInt64ArrFuncPrototype, "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); - SetConstant(JSHandle(bigInt64ArrayFunction), "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); - env->SetBigInt64ArrayFunction(thread_, bigInt64ArrayFunction); -} - -void Builtins::InitializeBigUint64Array(const JSHandle &env, const JSHandle &objFuncClass) const -{ - [[maybe_unused]] EcmaHandleScope scope(thread_); - // BigUint64Array.prototype - JSHandle bigUint64ArrFuncPrototype = factory_->NewJSObjectWithInit(objFuncClass); - JSHandle bigUint64ArrFuncPrototypeValue(bigUint64ArrFuncPrototype); - - // BigUint64Array.prototype_or_hclass - JSHandle bigUint64ArrFuncInstanceHClass = factory_->NewEcmaHClass( - panda::ecmascript::JSTypedArray::SIZE, JSType::JS_BIGUINT64_ARRAY, bigUint64ArrFuncPrototypeValue); - - // BigUint64Array = new Function() - JSHandle bigUint64ArrayFunction = factory_->NewSpecificTypedArrayFunction( - env, reinterpret_cast(BuiltinsTypedArray::BigUint64ArrayConstructor)); - InitializeCtor(env, bigUint64ArrFuncPrototype, bigUint64ArrayFunction, "BigUint64Array", FunctionLength::THREE); - - bigUint64ArrayFunction->SetProtoOrHClass(thread_, bigUint64ArrFuncInstanceHClass.GetTaggedValue()); - - constexpr int bytesPerElement = 8; - SetConstant(bigUint64ArrFuncPrototype, "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); - SetConstant(JSHandle(bigUint64ArrayFunction), "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); - env->SetBigUint64ArrayFunction(thread_, bigUint64ArrayFunction); -} +#define BUILTIN_TYPED_ARRAY_CALL_LAZY_INITIALIZE(Type, TYPE, bytesPerElement) \ + LazyInitialize##Type(env); + BUILTIN_TYPED_ARRAY_TYPES(BUILTIN_TYPED_ARRAY_CALL_LAZY_INITIALIZE) +#undef BUILTIN_TYPED_ARRAY_CALL_LAZY_INITIALIZE +} + +#define BUILTIN_TYPED_ARRAY_DEFINE_INITIALIZE(Type, TYPE, bytesPerElement) \ +void Builtins::Initialize##Type(const JSHandle &env, const JSHandle &arrFuncClass) const \ +{ \ + [[maybe_unused]] EcmaHandleScope scope(thread_); \ + /* %TypedArray%.prototype (where %TypedArray% is one of Int8Array, Uint8Array, etc.) */ \ + JSHandle arrFuncPrototype = factory_->NewJSObjectWithInit(arrFuncClass); \ + JSHandle arrFuncPrototypeValue(arrFuncPrototype); \ + /* %TypedArray%.prototype_or_hclass */ \ + JSHandle arrFuncInstanceHClass = factory_->NewEcmaHClass( \ + panda::ecmascript::JSTypedArray::SIZE, JSType::JS_##TYPE, arrFuncPrototypeValue); \ + /* %TypedArray% = new Function() */ \ + JSHandle arrayFunction = factory_->NewSpecificTypedArrayFunction( \ + env, reinterpret_cast(BuiltinsTypedArray::Type##Constructor)); \ + InitializeCtor(env, arrFuncPrototype, arrayFunction, #Type, FunctionLength::THREE); \ + \ + arrayFunction->SetProtoOrHClass(thread_, arrFuncInstanceHClass.GetTaggedValue()); \ + SetConstant(arrFuncPrototype, "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); \ + SetConstant(JSHandle(arrayFunction), "BYTES_PER_ELEMENT", JSTaggedValue(bytesPerElement)); \ + env->Set##Type##Function(thread_, arrayFunction); \ +} + +BUILTIN_TYPED_ARRAY_TYPES(BUILTIN_TYPED_ARRAY_DEFINE_INITIALIZE) +#undef BUILTIN_TYPED_ARRAY_DEFINE_INITIALIZE + +#define BUILTIN_TYPED_ARRAY_DEFINE_LAZY_INITIALIZE(Type, TYPE, bytesPerElement) \ +void Builtins::LazyInitialize##Type(const JSHandle &env) const \ +{ \ + [[maybe_unused]] EcmaHandleScope scope(thread_); \ + JSHandle globalObject(thread_, env->GetGlobalObject()); \ + JSHandle key(factory_->NewFromUtf8(#Type)); \ + auto accessor = factory_->NewInternalAccessor(nullptr, reinterpret_cast(BuiltinsLazyCallback::Type)); \ + SetLazyAccessor(globalObject, key, accessor); \ + env->Set##Type##Function(thread_, accessor); \ +} + +BUILTIN_TYPED_ARRAY_TYPES(BUILTIN_TYPED_ARRAY_DEFINE_LAZY_INITIALIZE) +#undef BUILTIN_TYPED_ARRAY_DEFINE_LAZY_INITIALIZE void Builtins::InitializeArrayBuffer(const JSHandle &env, const JSHandle &objFuncClass) const { @@ -2639,20 +2146,11 @@ void Builtins::InitializeReflect(const JSHandle &env, factory_->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, objFuncPrototypeVal); JSHandle reflectObject = factory_->NewJSObjectWithInit(reflectHClass); - SetFunction(env, reflectObject, "apply", Reflect::ReflectApply, FunctionLength::THREE); - SetFunction(env, reflectObject, "construct", Reflect::ReflectConstruct, FunctionLength::TWO); - SetFunction(env, reflectObject, "defineProperty", Reflect::ReflectDefineProperty, FunctionLength::THREE); - SetFunction(env, reflectObject, "deleteProperty", Reflect::ReflectDeleteProperty, FunctionLength::TWO); - SetFunction(env, reflectObject, "get", Reflect::ReflectGet, FunctionLength::TWO); - SetFunction(env, reflectObject, "getOwnPropertyDescriptor", Reflect::ReflectGetOwnPropertyDescriptor, - FunctionLength::TWO); - SetFunction(env, reflectObject, "getPrototypeOf", Reflect::ReflectGetPrototypeOf, FunctionLength::ONE); - SetFunction(env, reflectObject, "has", Reflect::ReflectHas, FunctionLength::TWO); - SetFunction(env, reflectObject, "isExtensible", Reflect::ReflectIsExtensible, FunctionLength::ONE); - SetFunction(env, reflectObject, "ownKeys", Reflect::ReflectOwnKeys, FunctionLength::ONE); - SetFunction(env, reflectObject, "preventExtensions", Reflect::ReflectPreventExtensions, FunctionLength::ONE); - SetFunction(env, reflectObject, "set", Reflect::ReflectSet, FunctionLength::THREE); - SetFunction(env, reflectObject, "setPrototypeOf", Reflect::ReflectSetPrototypeOf, FunctionLength::TWO); + // Reflect functions + for (const base::BuiltinFunctionEntry &entry: Reflect::GetReflectFunctions()) { + SetFunction(env, reflectObject, entry.GetName(), entry.GetEntrypoint(), + entry.GetLength(), entry.GetBuiltinStubId()); + } JSHandle reflectString(factory_->NewFromASCII("Reflect")); JSHandle globalObject(thread_, env->GetGlobalObject()); @@ -2715,7 +2213,8 @@ void Builtins::LazyInitializeSharedArrayBuffer(const JSHandle &env) c [[maybe_unused]] EcmaHandleScope scope(thread_); JSHandle globalObject(thread_, env->GetGlobalObject()); JSHandle key(factory_->NewFromUtf8("SharedArrayBuffer")); - auto accessor = factory_->NewInternalAccessor(nullptr, reinterpret_cast(BuiltinsLazyCallback::SharedArrayBuffer)); + auto accessor = + factory_->NewInternalAccessor(nullptr, reinterpret_cast(BuiltinsLazyCallback::SharedArrayBuffer)); SetLazyAccessor(globalObject, key, accessor); env->SetSharedArrayBufferFunction(thread_, accessor); } @@ -2735,18 +2234,15 @@ void Builtins::InitializePromise(const JSHandle &env, const JSHandle< JSHandle(promiseFunction)->SetFunctionPrototype(thread_, promiseFuncInstanceHClass.GetTaggedValue()); // Promise method - SetFunction(env, promiseFunction, "all", Promise::All, FunctionLength::ONE); - SetFunction(env, promiseFunction, "race", Promise::Race, FunctionLength::ONE); - SetFunction(env, promiseFunction, "resolve", Promise::Resolve, FunctionLength::ONE); - SetFunction(env, promiseFunction, "reject", Promise::Reject, FunctionLength::ONE); - SetFunction(env, promiseFunction, "any", Promise::Any, FunctionLength::ONE); - SetFunction(env, promiseFunction, "allSettled", Promise::AllSettled, FunctionLength::ONE); - + for (const base::BuiltinFunctionEntry &entry: Promise::GetPromiseFunctions()) { + SetFunction(env, promiseFunction, entry.GetName(), entry.GetEntrypoint(), + entry.GetLength(), entry.GetBuiltinStubId()); + } // promise.prototype method - SetFunction(env, promiseFuncPrototype, "catch", Promise::Catch, FunctionLength::ONE); - SetFunction(env, promiseFuncPrototype, "then", Promise::Then, FunctionLength::TWO); - SetFunction(env, promiseFuncPrototype, "finally", Promise::Finally, FunctionLength::ONE); - + for (const base::BuiltinFunctionEntry &entry: Promise::GetPromisePrototypeFunctions()) { + SetFunction(env, promiseFuncPrototype, entry.GetName(), entry.GetEntrypoint(), + entry.GetLength(), entry.GetBuiltinStubId()); + } // Promise.prototype [ @@toStringTag ] SetStringTagSymbol(env, promiseFuncPrototype, "Promise"); @@ -2842,26 +2338,10 @@ void Builtins::InitializeDataView(const JSHandle &env, const JSHandle JSHandle(dataViewFunction)->SetProtoOrHClass(thread_, dataViewFuncInstanceHClass.GetTaggedValue()); // DataView.prototype method - SetFunction(env, dataViewFuncPrototype, "getFloat32", DataView::GetFloat32, FunctionLength::ONE); - SetFunction(env, dataViewFuncPrototype, "getFloat64", DataView::GetFloat64, FunctionLength::ONE); - SetFunction(env, dataViewFuncPrototype, "getInt8", DataView::GetInt8, FunctionLength::ONE); - SetFunction(env, dataViewFuncPrototype, "getInt16", DataView::GetInt16, FunctionLength::ONE); - SetFunction(env, dataViewFuncPrototype, "getInt32", DataView::GetInt32, FunctionLength::ONE); - SetFunction(env, dataViewFuncPrototype, "getUint8", DataView::GetUint8, FunctionLength::ONE); - SetFunction(env, dataViewFuncPrototype, "getUint16", DataView::GetUint16, FunctionLength::ONE); - SetFunction(env, dataViewFuncPrototype, "getUint32", DataView::GetUint32, FunctionLength::ONE); - SetFunction(env, dataViewFuncPrototype, "getBigInt64", DataView::GetBigInt64, FunctionLength::ONE); - SetFunction(env, dataViewFuncPrototype, "getBigUint64", DataView::GetBigUint64, FunctionLength::ONE); - SetFunction(env, dataViewFuncPrototype, "setFloat32", DataView::SetFloat32, FunctionLength::TWO); - SetFunction(env, dataViewFuncPrototype, "setFloat64", DataView::SetFloat64, FunctionLength::TWO); - SetFunction(env, dataViewFuncPrototype, "setInt8", DataView::SetInt8, FunctionLength::TWO); - SetFunction(env, dataViewFuncPrototype, "setInt16", DataView::SetInt16, FunctionLength::TWO); - SetFunction(env, dataViewFuncPrototype, "setInt32", DataView::SetInt32, FunctionLength::TWO); - SetFunction(env, dataViewFuncPrototype, "setUint8", DataView::SetUint8, FunctionLength::TWO); - SetFunction(env, dataViewFuncPrototype, "setUint16", DataView::SetUint16, FunctionLength::TWO); - SetFunction(env, dataViewFuncPrototype, "setUint32", DataView::SetUint32, FunctionLength::TWO); - SetFunction(env, dataViewFuncPrototype, "setBigInt64", DataView::SetBigInt64, FunctionLength::TWO); - SetFunction(env, dataViewFuncPrototype, "setBigUint64", DataView::SetBigUint64, FunctionLength::TWO); + for (const base::BuiltinFunctionEntry &entry: DataView::GetDataViewPrototypeFunctions()) { + SetFunction(env, dataViewFuncPrototype, entry.GetName(), entry.GetEntrypoint(), + entry.GetLength(), entry.GetBuiltinStubId()); + } // 24.2.4.1 get DataView.prototype.buffer JSHandle bufferGetter = CreateGetter(env, DataView::GetBuffer, "buffer", FunctionLength::ZERO); @@ -2896,7 +2376,7 @@ void Builtins::LazyInitializeDataView(const JSHandle &env) const JSHandle Builtins::NewBuiltinConstructor(const JSHandle &env, const JSHandle &prototype, EcmaEntrypoint ctorFunc, - const char *name, int length, + std::string_view name, int length, kungfu::BuiltinsStubCSigns::ID builtinId) const { JSHandle ctor = @@ -2907,7 +2387,7 @@ JSHandle Builtins::NewBuiltinConstructor(const JSHandle & JSHandle Builtins::NewBuiltinCjsCtor(const JSHandle &env, const JSHandle &prototype, EcmaEntrypoint ctorFunc, - const char *name, int length) const + std::string_view name, int length) const { JSHandle ctor = factory_->NewJSFunction(env, reinterpret_cast(ctorFunc), FunctionKind::BUILTIN_CONSTRUCTOR); @@ -2941,7 +2421,7 @@ JSHandle Builtins::NewFunction(const JSHandle &env, const return function; } -void Builtins::SetFunction(const JSHandle &env, const JSHandle &obj, const char *key, +void Builtins::SetFunction(const JSHandle &env, const JSHandle &obj, std::string_view key, EcmaEntrypoint func, int length, kungfu::BuiltinsStubCSigns::ID builtinId) const { JSHandle keyString(factory_->NewFromUtf8(key)); @@ -2957,7 +2437,25 @@ void Builtins::SetFunction(const JSHandle &env, const JSHandle &env, const JSHandle &obj, const char *key, +JSHandle Builtins::SetAndReturnFunction(const JSHandle &env, const JSHandle &obj, + const char *key, EcmaEntrypoint func, int length, + kungfu::BuiltinsStubCSigns::ID builtinId) const +{ + JSHandle keyString(factory_->NewFromUtf8(key)); + return SetAndReturnFunction(env, obj, keyString, func, length, builtinId); +} + +JSHandle Builtins::SetAndReturnFunction(const JSHandle &env, const JSHandle &obj, + const JSHandle &key, EcmaEntrypoint func, int length, + kungfu::BuiltinsStubCSigns::ID builtinId) const +{ + JSHandle function(NewFunction(env, key, func, length, builtinId)); + PropertyDescriptor descriptor(thread_, JSHandle(function), true, false, true); + JSObject::DefineOwnProperty(thread_, obj, key, descriptor); + return function; +} + +void Builtins::SetFrozenFunction(const JSHandle &env, const JSHandle &obj, std::string_view key, EcmaEntrypoint func, int length) const { JSHandle keyString(factory_->NewFromUtf8(key)); @@ -2968,8 +2466,8 @@ void Builtins::SetFrozenFunction(const JSHandle &env, const JSHandle< template void Builtins::SetFunctionAtSymbol(const JSHandle &env, const JSHandle &obj, - const JSHandle &symbol, const char *name, EcmaEntrypoint func, - int length) const + const JSHandle &symbol, std::string_view name, + EcmaEntrypoint func, int length) const { JSHandle function = factory_->NewJSFunction(env, reinterpret_cast(func)); JSFunction::SetFunctionLength(thread_, function, JSTaggedValue(length)); @@ -2994,7 +2492,8 @@ void Builtins::SetFunctionAtSymbol(const JSHandle &env, const JSHandl JSObject::DefineOwnProperty(thread_, obj, symbol, descriptor); } -void Builtins::SetStringTagSymbol(const JSHandle &env, const JSHandle &obj, const char *key) const +void Builtins::SetStringTagSymbol(const JSHandle &env, const JSHandle &obj, + std::string_view key) const { JSHandle tag(factory_->NewFromUtf8(key)); JSHandle symbol = env->GetToStringTagSymbol(); @@ -3002,43 +2501,56 @@ void Builtins::SetStringTagSymbol(const JSHandle &env, const JSHandle JSObject::DefineOwnProperty(thread_, obj, symbol, desc); } -JSHandle Builtins::CreateGetter(const JSHandle &env, EcmaEntrypoint func, const char *name, - int length) const +JSHandle Builtins::CreateGetter(const JSHandle &env, EcmaEntrypoint func, + std::string_view name, int length) const +{ + JSHandle funcName(factory_->NewFromUtf8(name)); + return CreateGetter(env, func, funcName, length); +} + +JSHandle Builtins::CreateGetter(const JSHandle &env, EcmaEntrypoint func, + JSHandle key, int length) const { JSHandle function = factory_->NewJSFunction(env, reinterpret_cast(func)); JSFunction::SetFunctionLength(thread_, function, JSTaggedValue(length)); - JSHandle funcName(factory_->NewFromUtf8(name)); JSHandle prefix = thread_->GlobalConstants()->GetHandledGetString(); - JSFunction::SetFunctionName(thread_, JSHandle(function), funcName, prefix); + JSFunction::SetFunctionName(thread_, JSHandle(function), key, prefix); return JSHandle(function); } -JSHandle Builtins::CreateSetter(const JSHandle &env, EcmaEntrypoint func, const char *name, - int length) +JSHandle Builtins::CreateSetter(const JSHandle &env, EcmaEntrypoint func, + std::string_view name, int length) const +{ + JSHandle funcName(factory_->NewFromUtf8(name)); + return CreateSetter(env, func, funcName, length); +} + +JSHandle Builtins::CreateSetter(const JSHandle &env, EcmaEntrypoint func, + JSHandle key, int length) const { JSHandle function = factory_->NewJSFunction(env, reinterpret_cast(func)); JSFunction::SetFunctionLength(thread_, function, JSTaggedValue(length)); - JSHandle funcName(factory_->NewFromUtf8(name)); JSHandle prefix = thread_->GlobalConstants()->GetHandledSetString(); - JSFunction::SetFunctionName(thread_, JSHandle(function), funcName, prefix); + JSFunction::SetFunctionName(thread_, JSHandle(function), key, prefix); return JSHandle(function); } -void Builtins::SetConstant(const JSHandle &obj, const char *key, JSTaggedValue value) const +void Builtins::SetConstant(const JSHandle &obj, std::string_view key, JSTaggedValue value) const { JSHandle keyString(factory_->NewFromUtf8(key)); PropertyDescriptor descriptor(thread_, JSHandle(thread_, value), false, false, false); JSObject::DefineOwnProperty(thread_, obj, keyString, descriptor); } -void Builtins::SetConstantObject(const JSHandle &obj, const char *key, JSHandle &value) const +void Builtins::SetConstantObject(const JSHandle &obj, std::string_view key, + JSHandle &value) const { JSHandle keyString(factory_->NewFromUtf8(key)); PropertyDescriptor descriptor(thread_, value, false, false, false); JSObject::DefineOwnProperty(thread_, obj, keyString, descriptor); } -void Builtins::SetNonConstantObject(const JSHandle &obj, const char *key, +void Builtins::SetNonConstantObject(const JSHandle &obj, std::string_view key, JSHandle &value) const { JSHandle keyString(factory_->NewFromUtf8(key)); @@ -3046,21 +2558,22 @@ void Builtins::SetNonConstantObject(const JSHandle &obj, const char *k JSObject::DefineOwnProperty(thread_, obj, keyString, descriptor); } -void Builtins::SetGlobalThis(const JSHandle &obj, const char *key, const JSHandle &globalValue) +void Builtins::SetGlobalThis(const JSHandle &obj, std::string_view key, + const JSHandle &globalValue) { JSHandle keyString(factory_->NewFromUtf8(key)); PropertyDescriptor descriptor(thread_, globalValue, true, false, true); JSObject::DefineOwnProperty(thread_, obj, keyString, descriptor); } -void Builtins::SetAttribute(const JSHandle &obj, const char *key, const char *value) const +void Builtins::SetAttribute(const JSHandle &obj, std::string_view key, std::string_view value) const { JSHandle keyString(factory_->NewFromUtf8(key)); PropertyDescriptor descriptor(thread_, JSHandle(factory_->NewFromUtf8(value)), true, false, true); JSObject::DefineOwnProperty(thread_, obj, keyString, descriptor); } -void Builtins::SetNoneAttributeProperty(const JSHandle &obj, const char *key, +void Builtins::SetNoneAttributeProperty(const JSHandle &obj, std::string_view key, const JSHandle &value) const { JSHandle keyString(factory_->NewFromUtf8(key)); @@ -3069,7 +2582,8 @@ void Builtins::SetNoneAttributeProperty(const JSHandle &obj, const cha } void Builtins::SetFuncToObjAndGlobal(const JSHandle &env, const JSHandle &globalObject, - const JSHandle &obj, const char *key, EcmaEntrypoint func, int length) + const JSHandle &obj, std::string_view key, + EcmaEntrypoint func, int length) { JSHandle function = factory_->NewJSFunction(env, reinterpret_cast(func)); JSFunction::SetFunctionLength(thread_, function, JSTaggedValue(length)); @@ -3283,9 +2797,10 @@ void Builtins::SetGetter(const JSHandle &obj, const JSHandle::Cast(obj), key, accessor, attr); } + #ifdef ARK_SUPPORT_INTL JSHandle Builtins::NewIntlConstructor(const JSHandle &env, const JSHandle &prototype, - EcmaEntrypoint ctorFunc, const char *name, int length) + EcmaEntrypoint ctorFunc, std::string_view name, int length) { JSHandle ctor = factory_->NewJSFunction(env, reinterpret_cast(ctorFunc), FunctionKind::BUILTIN_CONSTRUCTOR); @@ -3309,7 +2824,7 @@ ITERATE_INTL(INTL_LAZY_INITIALIZE) #undef INTL_LAZY_INITIALIZE void Builtins::InitializeIntlCtor(const JSHandle &env, const JSHandle &prototype, - const JSHandle &ctor, const char *name, int length) + const JSHandle &ctor, std::string_view name, int length) { const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); JSFunction::SetFunctionLength(thread_, ctor, JSTaggedValue(length)); @@ -3666,51 +3181,74 @@ void Builtins::InitializeListFormat(const JSHandle &env) // 13.4.5 Intl.ListFormat.prototype.resolvedOptions() SetFunction(env, lfPrototype, "resolvedOptions", ListFormat::ResolvedOptions, FunctionLength::ZERO); } -#endif +#endif // #ifdef ARK_SUPPORT_INTL + JSHandle Builtins::InitializeArkTools(const JSHandle &env) const { JSHandle tools = factory_->NewEmptyJSObject(); - SetFunction(env, tools, "print", builtins::BuiltinsArkTools::ObjectDump, FunctionLength::ZERO); - SetFunction(env, tools, "excutePendingJob", builtins::BuiltinsArkTools::ExcutePendingJob, FunctionLength::ZERO); - SetFunction(env, tools, "getLexicalEnv", builtins::BuiltinsArkTools::GetLexicalEnv, FunctionLength::ONE); - SetFunction(env, tools, "compareHClass", builtins::BuiltinsArkTools::CompareHClass, FunctionLength::TWO); - SetFunction(env, tools, "dumpHClass", builtins::BuiltinsArkTools::DumpHClass, FunctionLength::ONE); - SetFunction(env, tools, "isTSHClass", builtins::BuiltinsArkTools::IsTSHClass, FunctionLength::ONE); - SetFunction(env, tools, "getHClass", builtins::BuiltinsArkTools::GetHClass, FunctionLength::ONE); - SetFunction(env, tools, "hasTSSubtyping", builtins::BuiltinsArkTools::HasTSSubtyping, FunctionLength::ONE); - SetFunction(env, tools, "isNotHoleProperty", builtins::BuiltinsArkTools::IsNotHoleProperty, - FunctionLength::TWO); - SetFunction(env, tools, "forceFullGC", builtins::BuiltinsArkTools::ForceFullGC, FunctionLength::ZERO); - SetFunction(env, tools, "removeAOTFlag", builtins::BuiltinsArkTools::RemoveAOTFlag, FunctionLength::ONE); -#if defined(ECMASCRIPT_SUPPORT_CPUPROFILER) - SetFunction(env, tools, "startCpuProf", builtins::BuiltinsArkTools::StartCpuProfiler, FunctionLength::ZERO); - SetFunction(env, tools, "stopCpuProf", builtins::BuiltinsArkTools::StopCpuProfiler, FunctionLength::ZERO); -#endif - SetFunction(env, tools, "isPrototype", builtins::BuiltinsArkTools::IsPrototype, FunctionLength::ONE); + for (const base::BuiltinFunctionEntry &entry: builtins::BuiltinsArkTools::GetArkToolsFunctions()) { + SetFunction(env, tools, entry.GetName(), entry.GetEntrypoint(), + entry.GetLength(), entry.GetBuiltinStubId()); + } return tools; } void Builtins::InitializeGlobalRegExp(JSHandle &obj) const { - JSHandle emptyString = thread_->GlobalConstants()->GetHandledEmptyString(); - JSHandle newBox1 = JSHandle(factory_->NewPropertyBox(emptyString)); - SetConstantObject(obj, "$1", newBox1); - JSHandle newBox2 = JSHandle(factory_->NewPropertyBox(emptyString)); - SetConstantObject(obj, "$2", newBox2); - JSHandle newBox3 = JSHandle(factory_->NewPropertyBox(emptyString)); - SetConstantObject(obj, "$3", newBox3); - JSHandle newBox4 = JSHandle(factory_->NewPropertyBox(emptyString)); - SetConstantObject(obj, "$4", newBox4); - JSHandle newBox5 = JSHandle(factory_->NewPropertyBox(emptyString)); - SetConstantObject(obj, "$5", newBox5); - JSHandle newBox6 = JSHandle(factory_->NewPropertyBox(emptyString)); - SetConstantObject(obj, "$6", newBox6); - JSHandle newBox7 = JSHandle(factory_->NewPropertyBox(emptyString)); - SetConstantObject(obj, "$7", newBox7); - JSHandle newBox8 = JSHandle(factory_->NewPropertyBox(emptyString)); - SetConstantObject(obj, "$8", newBox8); - JSHandle newBox9 = JSHandle(factory_->NewPropertyBox(emptyString)); - SetConstantObject(obj, "$9", newBox9); + // $1 + auto accessor1 = factory_->NewInternalAccessor(reinterpret_cast(RegExp::SetCapture1), + reinterpret_cast(RegExp::GetCapture1)); + PropertyDescriptor descriptor1(thread_, JSHandle::Cast(accessor1), true, false, true); + JSHandle dollar1Key = thread_->GlobalConstants()->GetHandledDollarStringOne(); + JSObject::DefineOwnProperty(thread_, obj, dollar1Key, descriptor1); + // $2 + auto accessor2 = factory_->NewInternalAccessor(reinterpret_cast(RegExp::SetCapture2), + reinterpret_cast(RegExp::GetCapture2)); + PropertyDescriptor descriptor2(thread_, JSHandle::Cast(accessor2), true, false, true); + JSHandle dollar2Key = thread_->GlobalConstants()->GetHandledDollarStringTwo(); + JSObject::DefineOwnProperty(thread_, obj, dollar2Key, descriptor2); + // $3 + auto accessor3 = factory_->NewInternalAccessor(reinterpret_cast(RegExp::SetCapture3), + reinterpret_cast(RegExp::GetCapture3)); + PropertyDescriptor descriptor3(thread_, JSHandle::Cast(accessor3), true, false, true); + JSHandle dollar3Key = thread_->GlobalConstants()->GetHandledDollarStringThree(); + JSObject::DefineOwnProperty(thread_, obj, dollar3Key, descriptor3); + // $4 + auto accessor4 = factory_->NewInternalAccessor(reinterpret_cast(RegExp::SetCapture4), + reinterpret_cast(RegExp::GetCapture4)); + PropertyDescriptor descriptor4(thread_, JSHandle::Cast(accessor4), true, false, true); + JSHandle dollar4Key = thread_->GlobalConstants()->GetHandledDollarStringFour(); + JSObject::DefineOwnProperty(thread_, obj, dollar4Key, descriptor4); + // $5 + auto accessor5 = factory_->NewInternalAccessor(reinterpret_cast(RegExp::SetCapture5), + reinterpret_cast(RegExp::GetCapture5)); + PropertyDescriptor descriptor5(thread_, JSHandle::Cast(accessor5), true, false, true); + JSHandle dollar5Key = thread_->GlobalConstants()->GetHandledDollarStringFive(); + JSObject::DefineOwnProperty(thread_, obj, dollar5Key, descriptor5); + // $6 + auto accessor6 = factory_->NewInternalAccessor(reinterpret_cast(RegExp::SetCapture6), + reinterpret_cast(RegExp::GetCapture6)); + PropertyDescriptor descriptor6(thread_, JSHandle::Cast(accessor6), true, false, true); + JSHandle dollar6Key = thread_->GlobalConstants()->GetHandledDollarStringSix(); + JSObject::DefineOwnProperty(thread_, obj, dollar6Key, descriptor6); + // $7 + auto accessor7 = factory_->NewInternalAccessor(reinterpret_cast(RegExp::SetCapture7), + reinterpret_cast(RegExp::GetCapture7)); + PropertyDescriptor descriptor7(thread_, JSHandle::Cast(accessor7), true, false, true); + JSHandle dollar7Key = thread_->GlobalConstants()->GetHandledDollarStringSeven(); + JSObject::DefineOwnProperty(thread_, obj, dollar7Key, descriptor7); + // $8 + auto accessor8 = factory_->NewInternalAccessor(reinterpret_cast(RegExp::SetCapture8), + reinterpret_cast(RegExp::GetCapture8)); + PropertyDescriptor descriptor8(thread_, JSHandle::Cast(accessor8), true, false, true); + JSHandle dollar8Key = thread_->GlobalConstants()->GetHandledDollarStringEight(); + JSObject::DefineOwnProperty(thread_, obj, dollar8Key, descriptor8); + // $9 + auto accessor9 = factory_->NewInternalAccessor(reinterpret_cast(RegExp::SetCapture9), + reinterpret_cast(RegExp::GetCapture9)); + PropertyDescriptor descriptor9(thread_, JSHandle::Cast(accessor9), true, false, true); + JSHandle dollar9Key = thread_->GlobalConstants()->GetHandledDollarStringNine(); + JSObject::DefineOwnProperty(thread_, obj, dollar9Key, descriptor9); } JSHandle Builtins::InitializeArkPrivate(const JSHandle &env) const diff --git a/ecmascript/builtins/builtins.h b/ecmascript/builtins/builtins.h index 6c03118514e1cabbbb8be44829062db91dc88512..9363139f1b50ed48d044fd87937b49b6d533531c 100644 --- a/ecmascript/builtins/builtins.h +++ b/ecmascript/builtins/builtins.h @@ -28,7 +28,7 @@ namespace panda::ecmascript { struct ErrorParameter { EcmaEntrypoint nativeConstructor{nullptr}; EcmaEntrypoint nativeMethod{nullptr}; - const char *nativePropertyName{nullptr}; + std::string_view nativePropertyName{}; JSType nativeJstype{JSType::INVALID}; }; @@ -52,24 +52,26 @@ private: EcmaVM *vm_{nullptr}; JSHandle NewBuiltinConstructor(const JSHandle &env, const JSHandle &prototype, - EcmaEntrypoint ctorFunc, const char *name, int length, + EcmaEntrypoint ctorFunc, std::string_view name, int length, kungfu::BuiltinsStubCSigns::ID builtinId = kungfu::BuiltinsStubCSigns::INVALID) const; JSHandle NewBuiltinCjsCtor(const JSHandle &env, const JSHandle &prototype, EcmaEntrypoint ctorFunc, - const char *name, int length) const; + std::string_view name, int length) const; JSHandle NewFunction(const JSHandle &env, const JSHandle &key, EcmaEntrypoint func, int length, kungfu::BuiltinsStubCSigns::ID builtinId = kungfu::BuiltinsStubCSigns::INVALID) const; + void InitializePropertyDetector(const JSHandle &env) const; + void SetLazyAccessor(const JSHandle &object, const JSHandle &key, const JSHandle &accessor) const; void InitializeCtor(const JSHandle &env, const JSHandle &prototype, - const JSHandle &ctor, const char *name, int length) const; + const JSHandle &ctor, std::string_view name, int length) const; void InitializeGlobalObject(const JSHandle &env, const JSHandle &globalObject); @@ -147,9 +149,9 @@ private: // for Intl. JSHandle NewIntlConstructor(const JSHandle &env, const JSHandle &prototype, - EcmaEntrypoint ctorFunc, const char *name, int length); + EcmaEntrypoint ctorFunc, std::string_view name, int length); void InitializeIntlCtor(const JSHandle &env, const JSHandle &prototype, - const JSHandle &ctor, const char *name, int length); + const JSHandle &ctor, std::string_view name, int length); void InitializeIntl(const JSHandle &env, const JSHandle &objFuncPrototypeValue); void InitializeLocale(const JSHandle &env); void InitializeDateTimeFormat(const JSHandle &env); @@ -169,8 +171,8 @@ private: void LazyInitializeDisplayNames(const JSHandle &env) const; void LazyInitializeListFormat(const JSHandle &env) const; - void GeneralUpdateError(ErrorParameter *error, EcmaEntrypoint constructor, EcmaEntrypoint method, const char *name, - JSType type) const; + void GeneralUpdateError(ErrorParameter *error, EcmaEntrypoint constructor, EcmaEntrypoint method, + std::string_view name, JSType type) const; void InitializeSet(const JSHandle &env, const JSHandle &objFuncClass) const; void LazyInitializeSet(const JSHandle &env); @@ -245,7 +247,7 @@ private: void InitializeGenerator(const JSHandle &env, const JSHandle &objFuncClass) const; JSHandle InitializeExoticConstructor(const JSHandle &env, EcmaEntrypoint ctorFunc, - const char *name, int length); + std::string_view name, int length); void InitializePromise(const JSHandle &env, const JSHandle &promiseFuncClass); @@ -261,7 +263,7 @@ private: void InitializeDefaultExportOfScript(const JSHandle &env) const; - void SetFunction(const JSHandle &env, const JSHandle &obj, const char *key, + void SetFunction(const JSHandle &env, const JSHandle &obj, std::string_view key, EcmaEntrypoint func, int length, kungfu::BuiltinsStubCSigns::ID builtinId = kungfu::BuiltinsStubCSigns::INVALID) const; @@ -269,32 +271,49 @@ private: EcmaEntrypoint func, int length, kungfu::BuiltinsStubCSigns::ID builtinId = kungfu::BuiltinsStubCSigns::INVALID) const; + JSHandle SetAndReturnFunction(const JSHandle &env, const JSHandle &obj, + const char *key, EcmaEntrypoint func, int length, + kungfu::BuiltinsStubCSigns::ID builtinId = + kungfu::BuiltinsStubCSigns::INVALID) const ; + + JSHandle SetAndReturnFunction(const JSHandle &env, const JSHandle &obj, + const JSHandle &key, EcmaEntrypoint func, int length, + kungfu::BuiltinsStubCSigns::ID builtinId = + kungfu::BuiltinsStubCSigns::INVALID) const; + void SetFuncToObjAndGlobal(const JSHandle &env, const JSHandle &globalObject, - const JSHandle &obj, const char *key, EcmaEntrypoint func, int length); + const JSHandle &obj, std::string_view key, EcmaEntrypoint func, int length); template void SetFunctionAtSymbol(const JSHandle &env, const JSHandle &obj, - const JSHandle &symbol, const char *name, EcmaEntrypoint func, + const JSHandle &symbol, std::string_view name, EcmaEntrypoint func, int length) const; - void SetStringTagSymbol(const JSHandle &env, const JSHandle &obj, const char *key) const; - JSHandle CreateGetter(const JSHandle &env, EcmaEntrypoint func, const char *name, - int length) const; + void SetStringTagSymbol(const JSHandle &env, const JSHandle &obj, + std::string_view key) const; + JSHandle CreateGetter(const JSHandle &env, EcmaEntrypoint func, + std::string_view name, int length) const; + JSHandle CreateGetter(const JSHandle &env, EcmaEntrypoint func, + JSHandle key, int length) const; - void SetConstant(const JSHandle &obj, const char *key, JSTaggedValue value) const; + void SetConstant(const JSHandle &obj, std::string_view key, JSTaggedValue value) const; - void SetGlobalThis(const JSHandle &obj, const char *key, const JSHandle &globalValue); + void SetGlobalThis(const JSHandle &obj, std::string_view key, + const JSHandle &globalValue); - void SetAttribute(const JSHandle &obj, const char *key, const char *value) const; + void SetAttribute(const JSHandle &obj, std::string_view key, std::string_view value) const; - void SetNoneAttributeProperty(const JSHandle &obj, const char *key, + void SetNoneAttributeProperty(const JSHandle &obj, std::string_view key, const JSHandle &value) const; void StrictModeForbiddenAccessCallerArguments(const JSHandle &env, const JSHandle &prototype) const; - JSHandle CreateSetter(const JSHandle &env, EcmaEntrypoint func, const char *name, - int length); + JSHandle CreateSetter(const JSHandle &env, EcmaEntrypoint func, + std::string_view name, int length) const; + JSHandle CreateSetter(const JSHandle &env, EcmaEntrypoint func, + JSHandle key, int length) const; + void SetArgumentsSharedAccessor(const JSHandle &env); void SetAccessor(const JSHandle &obj, const JSHandle &key, const JSHandle &getter, const JSHandle &setter) const; @@ -304,10 +323,12 @@ private: void InitializeGlobalRegExp(JSHandle &obj) const; // Using to initialize jsapi container JSHandle InitializeArkPrivate(const JSHandle &env) const; - void SetConstantObject(const JSHandle &obj, const char *key, JSHandle &value) const; - void SetFrozenFunction(const JSHandle &env, const JSHandle &obj, const char *key, + void SetConstantObject(const JSHandle &obj, std::string_view key, + JSHandle &value) const; + void SetFrozenFunction(const JSHandle &env, const JSHandle &obj, std::string_view key, EcmaEntrypoint func, int length) const; - void SetNonConstantObject(const JSHandle &obj, const char *key, JSHandle &value) const; + void SetNonConstantObject(const JSHandle &obj, std::string_view key, + JSHandle &value) const; friend class builtins::BuiltinsLazyCallback; }; diff --git a/ecmascript/builtins/builtins_ark_tools.cpp b/ecmascript/builtins/builtins_ark_tools.cpp index 81d76c1c2a3cdfd9c7fff5841374d833cf5e0321..b88b25834dfed77ecbba436c3331a77bb4143307 100644 --- a/ecmascript/builtins/builtins_ark_tools.cpp +++ b/ecmascript/builtins/builtins_ark_tools.cpp @@ -23,6 +23,8 @@ #include "ecmascript/js_tagged_value-inl.h" #include "ecmascript/mem/tagged_object-inl.h" #include "ecmascript/napi/include/dfx_jsnapi.h" +#include "ecmascript/mem/clock_scope.h" +#include "ecmascript/property_detector-inl.h" namespace panda::ecmascript::builtins { using StringHelper = base::StringHelper; @@ -37,6 +39,7 @@ JSTaggedValue BuiltinsArkTools::ObjectDump(EcmaRuntimeCallInfo *info) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle str = JSTaggedValue::ToString(thread, GetCallArg(info, 0)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // The default log level of ace_engine and js_runtime is error LOG_ECMA(ERROR) << ": " << EcmaStringAccessor(str).ToStdString(); @@ -144,6 +147,15 @@ JSTaggedValue BuiltinsArkTools::IsNotHoleProperty(EcmaRuntimeCallInfo *info) return GetTaggedBoolean(attr.IsNotHole()); } +JSTaggedValue BuiltinsArkTools::HiddenStackSourceFile(EcmaRuntimeCallInfo *info) +{ + [[maybe_unused]] DisallowGarbageCollection noGc; + ASSERT(info); + JSThread *thread = info->GetThread(); + thread->SetEnableStackSourceFile(false); + return JSTaggedValue::True(); +} + JSTaggedValue BuiltinsArkTools::ExcutePendingJob(EcmaRuntimeCallInfo *info) { ASSERT(info); @@ -208,6 +220,7 @@ JSTaggedValue BuiltinsArkTools::StartCpuProfiler(EcmaRuntimeCallInfo *info) std::string fileName = ""; if (fileNameValue->IsString()) { JSHandle str = JSTaggedValue::ToString(thread, fileNameValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); fileName = EcmaStringAccessor(str).ToStdString() + ".cpuprofile"; } else { fileName = GetProfileName(); @@ -305,4 +318,18 @@ JSTaggedValue BuiltinsArkTools::IsPrototype(EcmaRuntimeCallInfo *info) JSHClass *objHclass = obj->GetTaggedObject()->GetClass(); return JSTaggedValue(objHclass->IsPrototype()); } + +JSTaggedValue BuiltinsArkTools::IsRegExpReplaceDetectorValid(EcmaRuntimeCallInfo *info) +{ + ASSERT(info); + JSThread *thread = info->GetThread(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + return JSTaggedValue(PropertyDetector::IsRegExpReplaceDetectorValid(env)); +} + +JSTaggedValue BuiltinsArkTools::TimeInUs([[maybe_unused]] EcmaRuntimeCallInfo *info) +{ + ClockScope scope; + return JSTaggedValue(scope.GetCurTime()); +} } // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_ark_tools.h b/ecmascript/builtins/builtins_ark_tools.h index fb77e9af5f1ffa76c67ccbb3a7a2cfc489c84089..762557d7461918608ce4d86e2bd3f8a5cae686b5 100644 --- a/ecmascript/builtins/builtins_ark_tools.h +++ b/ecmascript/builtins/builtins_ark_tools.h @@ -19,6 +19,39 @@ #include "ecmascript/base/builtins_base.h" #include "ecmascript/js_thread.h" +// List of functions in ArkTools, extension of ArkTS engine. +// V(name, func, length, stubIndex) +// where BuiltinsArkTools::func refers to the native implementation of ArkTools[name]. +// kungfu::BuiltinsStubCSigns::stubIndex refers to the builtin stub index, or INVALID if no stub available. +#define BUILTIN_ARK_TOOLS_FUNCTIONS_COMMON(V) \ + V("compareHClass", CompareHClass, 2, INVALID) \ + V("dumpHClass", DumpHClass, 1, INVALID) \ + V("excutePendingJob", ExcutePendingJob, 0, INVALID) \ + V("forceFullGC", ForceFullGC, 0, INVALID) \ + V("getHClass", GetHClass, 1, INVALID) \ + V("getLexicalEnv", GetLexicalEnv, 1, INVALID) \ + V("hasTSSubtyping", HasTSSubtyping, 1, INVALID) \ + V("hiddenStackSourceFile", HiddenStackSourceFile, 0, INVALID) \ + V("isNotHoleProperty", IsNotHoleProperty, 2, INVALID) \ + V("isPrototype", IsPrototype, 1, INVALID) \ + V("isRegExpReplaceDetectorValid", IsRegExpReplaceDetectorValid, 0, INVALID) \ + V("isTSHClass", IsTSHClass, 1, INVALID) \ + V("print", ObjectDump, 0, INVALID) \ + V("removeAOTFlag", RemoveAOTFlag, 1, INVALID) \ + V("timeInUs", TimeInUs, 0, INVALID) + +#ifdef ECMASCRIPT_SUPPORT_CPUPROFILER +#define BUILTIN_ARK_TOOLS_FUNCTIONS_CPUPROFILER(V) \ + V("startCpuProf", StartCpuProfiler, 0, INVALID) \ + V("stopCpuProf", StopCpuProfiler, 0, INVALID) +#else +#define BUILTIN_ARK_TOOLS_FUNCTIONS_CPUPROFILER(V) // Nothing +#endif + +#define BUILTIN_ARK_TOOLS_FUNCTIONS(V) \ + BUILTIN_ARK_TOOLS_FUNCTIONS_COMMON(V) \ + BUILTIN_ARK_TOOLS_FUNCTIONS_CPUPROFILER(V) + namespace panda::ecmascript::builtins { class BuiltinsArkTools : public base::BuiltinsBase { public: @@ -45,6 +78,8 @@ public: static JSTaggedValue ForceFullGC(EcmaRuntimeCallInfo *info); + static JSTaggedValue HiddenStackSourceFile(EcmaRuntimeCallInfo *info); + static JSTaggedValue RemoveAOTFlag(EcmaRuntimeCallInfo *info); #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER) @@ -58,6 +93,24 @@ public: #endif // ArkTools.isPrototype(object) static JSTaggedValue IsPrototype(EcmaRuntimeCallInfo *info); + + static JSTaggedValue IsRegExpReplaceDetectorValid(EcmaRuntimeCallInfo *info); + + static JSTaggedValue TimeInUs(EcmaRuntimeCallInfo *info); + + static Span GetArkToolsFunctions() + { + return Span(ARK_TOOLS_FUNCTIONS); + } + +private: +#define BUILTINS_ARK_TOOLS_FUNCTION_ENTRY(name, method, length, id) \ + base::BuiltinFunctionEntry::Create(name, BuiltinsArkTools::method, length, kungfu::BuiltinsStubCSigns::id), + + static constexpr std::array ARK_TOOLS_FUNCTIONS = { + BUILTIN_ARK_TOOLS_FUNCTIONS(BUILTINS_ARK_TOOLS_FUNCTION_ENTRY) + }; +#undef BUILTINS_ARK_TOOLS_FUNCTION_ENTRY }; } // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_array.cpp b/ecmascript/builtins/builtins_array.cpp index e82fa47d7ca26378de7276b3b483dacdf0614ca1..12924226113b3b6ce94cfdabe87bba773347c88a 100644 --- a/ecmascript/builtins/builtins_array.cpp +++ b/ecmascript/builtins/builtins_array.cpp @@ -21,6 +21,7 @@ #include "ecmascript/base/number_helper.h" #include "ecmascript/base/typed_array_helper-inl.h" #include "ecmascript/base/typed_array_helper.h" +#include "ecmascript/ecma_macros.h" #include "ecmascript/ecma_runtime_call_info.h" #include "ecmascript/ecma_string.h" #include "ecmascript/global_env.h" @@ -29,8 +30,10 @@ #include "ecmascript/js_array_iterator.h" #include "ecmascript/js_function.h" #include "ecmascript/js_handle.h" +#include "ecmascript/js_map_iterator.h" #include "ecmascript/js_stable_array.h" #include "ecmascript/js_tagged_number.h" +#include "ecmascript/js_tagged_value.h" #include "ecmascript/object_factory.h" #include "ecmascript/object_fast_operator-inl.h" #include "ecmascript/tagged_array-inl.h" @@ -65,14 +68,14 @@ JSTaggedValue BuiltinsArray::ArrayConstructor(EcmaRuntimeCallInfo *argv) // 22.1.1.1 Array ( ) if (argc == 0) { // 6. Return ArrayCreate(0, proto). - return JSTaggedValue(JSArray::ArrayCreate(thread, JSTaggedNumber(0), newTarget).GetObject()); + return JSArray::ArrayCreate(thread, JSTaggedNumber(0), newTarget).GetTaggedValue(); } // 22.1.1.2 Array(len) if (argc == 1) { // 6. Let array be ArrayCreate(0, proto). - uint32_t newLen = 0; - JSHandle newArrayHandle(JSArray::ArrayCreate(thread, JSTaggedNumber(newLen), newTarget)); + JSHandle newArrayHandle(JSArray::ArrayCreate(thread, JSTaggedNumber(0), newTarget)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle len = GetCallArg(argv, 0); // 7. If Type(len) is not Number, then // a. Let defineStatus be CreateDataProperty(array, "0", len). @@ -83,6 +86,7 @@ JSTaggedValue BuiltinsArray::ArrayConstructor(EcmaRuntimeCallInfo *argv) // b. If intLen ≠ len, throw a RangeError exception. // 9. Let setStatus be Set(array, "length", intLen, true). // 10. Assert: setStatus is not an abrupt completion. + uint32_t newLen = 0; if (!len->IsNumber()) { JSHandle key0 = thread->GlobalConstants()->GetHandledZeroString(); JSObject::CreateDataProperty(thread, newArrayHandle, key0, len); @@ -91,10 +95,10 @@ JSTaggedValue BuiltinsArray::ArrayConstructor(EcmaRuntimeCallInfo *argv) newLen = JSTaggedValue::ToUint32(thread, len); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); if (JSTaggedNumber(len.GetTaggedValue()).GetNumber() != newLen) { - THROW_RANGE_ERROR_AND_RETURN(thread, "The length is out of range.", JSTaggedValue::Exception()); + THROW_RANGE_ERROR_AND_RETURN(thread, "Invalid array length", JSTaggedValue::Exception()); } } - JSArray::SetCapacity(thread, newArrayHandle, 0, newLen); + JSArray::SetCapacity(thread, newArrayHandle, 0, newLen, true); // 11. Return array. return newArrayHandle.GetTaggedValue(); @@ -102,11 +106,11 @@ JSTaggedValue BuiltinsArray::ArrayConstructor(EcmaRuntimeCallInfo *argv) // 22.1.1.3 Array(...items ) JSTaggedValue newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(argc), newTarget).GetTaggedValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); if (!newArray.IsArray(thread)) { THROW_TYPE_ERROR_AND_RETURN(thread, "Failed to create array.", JSTaggedValue::Exception()); } JSHandle newArrayHandle(thread, newArray); - // 8. Let k be 0. // 9. Let items be a zero-origined List containing the argument items in order. // 10. Repeat, while k < numberOfArgs @@ -166,6 +170,11 @@ JSTaggedValue BuiltinsArray::From(EcmaRuntimeCallInfo *argv) // 6. If usingIterator is not undefined, then JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); if (!usingIterator->IsUndefined()) { + // Fast path for MapIterator + if (!mapping && items->IsJSMapIterator()) { + return JSMapIterator::MapIteratorToList(thread, items, usingIterator); + } + // a. If IsConstructor(C) is true, then // i. Let A be Construct(C). // b. Else, @@ -208,7 +217,7 @@ JSTaggedValue BuiltinsArray::From(EcmaRuntimeCallInfo *argv) if (next->IsFalse()) { JSTaggedValue::SetProperty(thread, JSHandle::Cast(newArrayHandle), lengthKey, key, true); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - return JSTaggedValue(newArrayHandle.GetTaggedValue()); + return newArrayHandle.GetTaggedValue(); } // v. Let nextValue be IteratorValue(next). JSHandle nextValue = JSIterator::IteratorValue(thread, next); @@ -220,15 +229,15 @@ JSTaggedValue BuiltinsArray::From(EcmaRuntimeCallInfo *argv) // 3. Let mappedValue be mappedValue.[[value]]. // viii. Else, let mappedValue be nextValue. if (mapping) { - const int32_t argsLength = 2; // 2: «nextValue, k» + const uint32_t argsLength = 2; // 2: «nextValue, k» EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, mapfn, thisArgHandle, undefined, argsLength); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); info->SetCallArg(nextValue.GetTaggedValue(), key.GetTaggedValue()); JSTaggedValue callResult = JSFunction::Call(info); - mapValue.Update(callResult); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, - JSIterator::IteratorClose(thread, iterator, mapValue).GetTaggedValue()); + JSIterator::IteratorClose(thread, iterator, mapValue).GetTaggedValue()); + mapValue.Update(callResult); } else { mapValue.Update(nextValue.GetTaggedValue()); } @@ -238,7 +247,7 @@ JSTaggedValue BuiltinsArray::From(EcmaRuntimeCallInfo *argv) JSHandle defineStatus( thread, JSTaggedValue(JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, key, mapValue))); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, - JSIterator::IteratorClose(thread, iterator, defineStatus).GetTaggedValue()); + JSIterator::IteratorClose(thread, iterator, defineStatus).GetTaggedValue()); k++; } } @@ -289,7 +298,7 @@ JSTaggedValue BuiltinsArray::From(EcmaRuntimeCallInfo *argv) RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); if (mapping) { key.Update(JSTaggedValue(k)); - const int32_t argsLength = 2; // 2: «kValue, k» + const uint32_t argsLength = 2; // 2: «kValue, k» EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, mapfn, thisArgHandle, undefined, argsLength); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -297,7 +306,6 @@ JSTaggedValue BuiltinsArray::From(EcmaRuntimeCallInfo *argv) JSTaggedValue callResult = JSFunction::Call(info); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); mapValue.Update(callResult); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); } else { mapValue.Update(kValue.GetTaggedValue()); } @@ -403,132 +411,107 @@ JSTaggedValue BuiltinsArray::Concat(EcmaRuntimeCallInfo *argv) BUILTINS_API_TRACE(argv->GetThread(), Array, Concat); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); - uint32_t argc = argv->GetArgsNumber(); + int argc = static_cast(argv->GetArgsNumber()); // 1. Let O be ToObject(this value). JSHandle thisHandle = GetThis(argv); JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); - // 2. ReturnIfAbrupt(O). RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle thisObjVal(thisObjHandle); - // 3. Let A be ArraySpeciesCreate(O, 0). + // 2. Let A be ArraySpeciesCreate(O, 0). uint32_t arrayLen = 0; JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle, JSTaggedNumber(arrayLen)); - // 4. ReturnIfAbrupt(A). RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle newArrayHandle(thread, newArray); - // 5. Let n be 0. + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + // Fast path + int64_t arrLen = ArrayHelper::GetArrayLength(thread, thisObjVal); + if (arrLen == 0 && argc == 1) { + JSHandle argHandle = GetCallArg(argv, 0); + int64_t argLen = ArrayHelper::GetArrayLength(thread, argHandle); + if (argLen == 0 && argHandle->IsJSArray()) { + JSHandle lenHandle(thread, JSTaggedValue(arrLen)); + JSTaggedValue::SetProperty(thread, JSHandle::Cast(newArrayHandle), + lengthKey, lenHandle, true); + return newArrayHandle.GetTaggedValue(); + } + } + + // 3. Let n be 0. int64_t n = 0; + JSMutableHandle ele(thread, JSTaggedValue::Undefined()); JSMutableHandle fromKey(thread, JSTaggedValue::Undefined()); JSMutableHandle toKey(thread, JSTaggedValue::Undefined()); - bool isSpreadable = ArrayHelper::IsConcatSpreadable(thread, thisHandle); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - if (isSpreadable) { - int64_t thisLen = ArrayHelper::GetArrayLength(thread, thisObjVal); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - if (n + thisLen > base::MAX_SAFE_INTEGER) { - THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception()); - } - int64_t k = 0; - if (thisObjVal->IsStableJSArray(thread)) { - JSStableArray::Concat(thread, newArrayHandle, thisObjHandle, k, n); - } - while (k < thisLen) { - fromKey.Update(JSTaggedValue(k)); - toKey.Update(JSTaggedValue(n)); - bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, fromKey); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - if (exists) { - JSHandle fromValHandle = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, fromValHandle); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - } - n++; - k++; - } - } else { - if (n >= base::MAX_SAFE_INTEGER) { - THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception()); + // 4. Prepend O to items. + // 5. For each element E of items, do + for (int i = -1; i < argc; i++) { + if (i < 0) { + ele.Update(thisObjHandle.GetTaggedValue()); + } else { + ele.Update(GetCallArg(argv, i)); } - JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, n, thisObjVal); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - n++; - } - // 7. Repeat, while items is not empty - for (uint32_t i = 0; i < argc; i++) { - // a. Remove the first element from items and let E be the value of the element - JSHandle addHandle = GetCallArg(argv, i); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - JSHandle addObjHandle(addHandle); - - // b. Let spreadable be IsConcatSpreadable(E). - isSpreadable = ArrayHelper::IsConcatSpreadable(thread, addHandle); - // c. ReturnIfAbrupt(spreadable). + // a. Let spreadable be ? IsConcatSpreadable(E). + bool isSpreadable = ArrayHelper::IsConcatSpreadable(thread, ele); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - // d. If spreadable is true, then + // b. If spreadable is true, then if (isSpreadable) { - // ii. Let len be ToLength(Get(E, "length")). - int64_t len = ArrayHelper::GetArrayLength(thread, JSHandle::Cast(addObjHandle)); - // iii. ReturnIfAbrupt(len). + // i. Let k be 0. + // ii. Let len be ? LengthOfArrayLike(E). + // iii. If n + len > 253 - 1, throw a TypeError exception. + int64_t len = ArrayHelper::GetArrayLength(thread, ele); + int64_t k = 0; RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - // iv. If n + len > 253-1, throw a TypeError exception. if (n + len > base::MAX_SAFE_INTEGER) { THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception()); } - int64_t k = 0; - JSHandle addObjVal(addObjHandle); - if (addObjVal->IsStableJSArray(thread)) { - JSStableArray::Concat(thread, newArrayHandle, addObjHandle, k, n); + + if (ele->IsStableJSArray(thread)) { + JSStableArray::Concat(thread, newArrayHandle, JSHandle::Cast(ele), k, n); } - // v. Repeat, while k < len + // iv. Repeat, while k < len, while (k < len) { - fromKey.Update(JSTaggedValue(k)); - toKey.Update(JSTaggedValue(n)); // 1. Let P be ToString(k). // 2. Let exists be HasProperty(E, P). - // 4. If exists is true, then - bool exists = JSTaggedValue::HasProperty(thread, JSHandle::Cast(addObjHandle), fromKey); + // 3. If exists is true, then + fromKey.Update(JSTaggedValue(k)); + toKey.Update(JSTaggedValue(n)); + bool exists = JSTaggedValue::HasProperty(thread, ele, fromKey); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); if (exists) { // a. Let subElement be Get(E, P). JSHandle fromValHandle = - JSArray::FastGetPropertyByValue(thread, addHandle, fromKey); - // b. ReturnIfAbrupt(subElement). + JSArray::FastGetPropertyByValue(thread, ele, fromKey); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // b. Perform ? CreateDataPropertyOrThrow(A, ! ToString(𝔽(n)), subElement). JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, fromValHandle); - // d. ReturnIfAbrupt(status). RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); } - // 5. Increase n by 1. - // 6. Increase k by 1. + // 4. Set n to n + 1. + // 5. Set k to k + 1. n++; k++; } - } else { // e. Else E is added as a single item rather than spread, - // i. If n≥253-1, throw a TypeError exception. + //c. Else + } else { + // ii. If n ≥ 253 - 1, throw a TypeError exception. if (n >= base::MAX_SAFE_INTEGER) { THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception()); } - // ii. Let status be CreateDataPropertyOrThrow (A, ToString(n), E). - JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, n, addHandle); - // iii. ReturnIfAbrupt(status). + // iii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(𝔽(n)), E). + // iv. Set n to n + 1. + JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, n, ele); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - - // iv. Increase n by 1. n++; } } - // 8. Let setStatus be Set(A, "length", n, true). - JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + // 6. Perform ? Set(A, "length", 𝔽(n), true). JSHandle lenHandle(thread, JSTaggedValue(n)); JSTaggedValue::SetProperty(thread, JSHandle::Cast(newArrayHandle), lengthKey, lenHandle, true); - // 9. ReturnIfAbrupt(setStatus). RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - // 10. Return A. + // 7. Return A. return newArrayHandle.GetTaggedValue(); } @@ -643,6 +626,7 @@ JSTaggedValue BuiltinsArray::CopyWithin(EcmaRuntimeCallInfo *argv) } else { if (thisObjVal->IsJSProxy()) { toKey.Update(JSTaggedValue::ToString(thread, toKey).GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); } JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, toKey); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -725,7 +709,7 @@ JSTaggedValue BuiltinsArray::Every(EcmaRuntimeCallInfo *argv) } } JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); - const int32_t argsLength = 3; // 3: «kValue, k, O» + const uint32_t argsLength = 3; // 3: «kValue, k, O» while (k < len) { bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, k); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -838,6 +822,13 @@ JSTaggedValue BuiltinsArray::Fill(EcmaRuntimeCallInfo *argv) return thisObjHandle.GetTaggedValue(); } } + if (thisHandle->IsTypedArray()) { + bool result = JSTypedArray::FastTypedArrayFill(thread, thisHandle, value, start, end); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (result) { + return thisObjHandle.GetTaggedValue(); + } + } int64_t k = start; while (k < end) { key.Update(JSTaggedValue(k)); @@ -910,7 +901,7 @@ JSTaggedValue BuiltinsArray::Filter(EcmaRuntimeCallInfo *argv) JSStableArray::Filter(newArrayHandle, thisObjHandle, argv, k, toIndex); } JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); - const int32_t argsLength = 3; // 3: «kValue, k, O» + const uint32_t argsLength = 3; // 3: «kValue, k, O» JSTaggedValue callResult = GetTaggedBoolean(true); while (k < len) { bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, k); @@ -924,6 +915,7 @@ JSTaggedValue BuiltinsArray::Filter(EcmaRuntimeCallInfo *argv) RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue()); callResult = JSFunction::Call(info); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); if (callResult.ToBoolean()) { toIndexHandle.Update(JSTaggedValue(toIndex)); JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toIndexHandle, kValue); @@ -982,16 +974,15 @@ JSTaggedValue BuiltinsArray::Find(EcmaRuntimeCallInfo *argv) JSHandle kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); key.Update(JSTaggedValue(k)); - const int32_t argsLength = 3; // 3: «kValue, k, O» + const uint32_t argsLength = 3; // 3: «kValue, k, O» JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue()); JSTaggedValue callResult = JSFunction::Call(info); - bool boolResult = callResult.ToBoolean(); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - if (boolResult) { + if (callResult.ToBoolean()) { return kValue.GetTaggedValue(); } k++; @@ -1043,13 +1034,14 @@ JSTaggedValue BuiltinsArray::FindIndex(EcmaRuntimeCallInfo *argv) JSTaggedValue callResult = GetTaggedBoolean(true); if (thisObjVal->IsStableJSArray(thread)) { callResult = JSStableArray::HandleFindIndexOfStable(thread, thisObjHandle, callbackFnHandle, thisArgHandle, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); if (callResult.ToBoolean()) { return GetTaggedDouble(k); } } JSMutableHandle key(thread, JSTaggedValue::Undefined()); JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); - const int32_t argsLength = 3; // 3: «kValue, k, O» + const uint32_t argsLength = 3; // 3: «kValue, k, O» while (k < len) { JSHandle kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -1059,6 +1051,7 @@ JSTaggedValue BuiltinsArray::FindIndex(EcmaRuntimeCallInfo *argv) RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue()); callResult = JSFunction::Call(info); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); if (callResult.ToBoolean()) { return GetTaggedDouble(k); } @@ -1112,9 +1105,10 @@ JSTaggedValue BuiltinsArray::ForEach(EcmaRuntimeCallInfo *argv) JSMutableHandle key(thread, JSTaggedValue::Undefined()); uint32_t k = 0; if (thisObjVal->IsStableJSArray(thread)) { - JSStableArray::HandleforEachOfStable(thread, thisObjHandle, callbackFnHandle, thisArgHandle, k); + JSStableArray::HandleforEachOfStable(thread, thisObjHandle, callbackFnHandle, thisArgHandle, len, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); } - const int32_t argsLength = 3; // 3: «kValue, k, O» + const uint32_t argsLength = 3; // 3: «kValue, k, O» JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); while (k < len) { bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, k); @@ -1137,89 +1131,91 @@ JSTaggedValue BuiltinsArray::ForEach(EcmaRuntimeCallInfo *argv) return JSTaggedValue::Undefined(); } -// 22.1.3.11 Array.prototype.indexOf ( searchElement [ , fromIndex ] ) -JSTaggedValue BuiltinsArray::IndexOf(EcmaRuntimeCallInfo *argv) +JSTaggedValue BuiltinsArray::IndexOfStable( + EcmaRuntimeCallInfo *argv, JSThread *thread, const JSHandle &thisHandle) { - ASSERT(argv); - BUILTINS_API_TRACE(argv->GetThread(), Array, IndexOf); - JSThread *thread = argv->GetThread(); - [[maybe_unused]] EcmaHandleScope handleScope(thread); - + int64_t length = JSHandle::Cast(thisHandle)->GetArrayLength(); + if (length == 0) { + return JSTaggedValue(-1); + } + int64_t fromIndex = 0; uint32_t argc = argv->GetArgsNumber(); + // 2: [target, fromIndex]. Note that fromIndex is missing in most usage cases. + if (UNLIKELY(argc >= 2)) { + JSHandle fromIndexHandle = argv->GetCallArg(1); + fromIndex = ArrayHelper::GetStartIndex(thread, fromIndexHandle, length); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // Slow path when fromIndex is obtained from an ECMAObject + // due to potential side effects in its 'toString' and 'valueOf' methods which modify the array object. + if (UNLIKELY(fromIndexHandle->IsECMAObject())) { + return IndexOfSlowPath(argv, thread, thisHandle, length, fromIndex); + } + } + if (fromIndex >= length) { + return JSTaggedValue(-1); + } + JSHandle target = GetCallArg(argv, 0); + return JSStableArray::IndexOf( + thread, thisHandle, target, static_cast(fromIndex), static_cast(length)); +} +JSTaggedValue BuiltinsArray::IndexOfSlowPath( + EcmaRuntimeCallInfo *argv, JSThread *thread, const JSHandle &thisHandle) +{ // 1. Let O be ToObject(this value). - JSHandle thisHandle = GetThis(argv); JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); // 2. ReturnIfAbrupt(O). RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle thisObjVal(thisObjHandle); - - JSHandle searchElement = GetCallArg(argv, 0); - // 3. Let len be ToLength(Get(O, "length")). - int64_t len = ArrayHelper::GetLength(thread, thisObjVal); + int64_t length = ArrayHelper::GetLength(thread, thisObjVal); // 4. ReturnIfAbrupt(len). RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - // 5. If len is 0, return −1. - if (len == 0) { - return GetTaggedInt(-1); + if (length == 0) { + return JSTaggedValue(-1); } - // 6. If argument fromIndex was passed let n be ToInteger(fromIndex); else let n be 0. - double fromIndex = 0; - if (argc > 1) { - JSHandle msg1 = GetCallArg(argv, 1); - JSTaggedNumber fromIndexTemp = JSTaggedValue::ToNumber(thread, msg1); - // 7. ReturnIfAbrupt(n). - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - fromIndex = base::NumberHelper::TruncateDouble(fromIndexTemp.GetNumber()); - } + int64_t fromIndex = ArrayHelper::GetStartIndexFromArgs(thread, argv, 1, length); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return IndexOfSlowPath(argv, thread, thisObjVal, length, fromIndex); +} - // 8. If n ≥ len, return −1. - if (fromIndex >= len) { - return GetTaggedInt(-1); +JSTaggedValue BuiltinsArray::IndexOfSlowPath( + EcmaRuntimeCallInfo *argv, JSThread *thread, const JSHandle &thisObjVal, + int64_t length, int64_t fromIndex) +{ + if (fromIndex >= length) { + return JSTaggedValue(-1); } - - // 9. If n ≥ 0, then - // a. Let k be n. - // 10. Else n<0, - // a. Let k be len - abs(n). - // b. If k < 0, let k be 0. - int64_t from = (fromIndex >= 0) ? fromIndex : ((len + fromIndex) >= 0 ? len + fromIndex : 0); - - // if it is stable array, we can go to fast path - if (thisObjVal->IsStableJSArray(thread)) { - return JSStableArray::IndexOf(thread, thisObjVal, searchElement, static_cast(from), - static_cast(len)); - } - - // 11. Repeat, while k key(thread, JSTaggedValue::Undefined()); - while (from < len) { - key.Update(JSTaggedValue(from)); - bool exists = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, key)); + JSMutableHandle keyHandle(thread, JSTaggedValue::Undefined()); + JSHandle target = GetCallArg(argv, 0); + // 11. Repeat, while k < len + for (int64_t curIndex = fromIndex; curIndex < length; ++curIndex) { + keyHandle.Update(JSTaggedValue(curIndex)); + bool found = ArrayHelper::ElementIsStrictEqualTo(thread, thisObjVal, keyHandle, target); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - if (exists) { - JSHandle kValueHandle = JSArray::FastGetPropertyByValue(thread, thisObjVal, key); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - if (JSTaggedValue::StrictEqual(thread, searchElement, kValueHandle)) { - return GetTaggedDouble(from); - } + if (UNLIKELY(found)) { + return JSTaggedValue(curIndex); } - from++; } - // 12. Return -1. - return GetTaggedInt(-1); + return JSTaggedValue(-1); +} + +// 22.1.3.11 Array.prototype.indexOf ( searchElement [ , fromIndex ] ) +JSTaggedValue BuiltinsArray::IndexOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, Array, IndexOf); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle thisHandle = GetThis(argv); + if (thisHandle->IsStableJSArray(thread)) { + return IndexOfStable(argv, thread, thisHandle); + } + return IndexOfSlowPath(argv, thread, thisHandle); } // 22.1.3.12 Array.prototype.join (separator) @@ -1230,21 +1226,26 @@ JSTaggedValue BuiltinsArray::Join(EcmaRuntimeCallInfo *argv) JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle thisHandle = GetThis(argv); + auto factory = thread->GetEcmaVM()->GetFactory(); + auto context = thread->GetCurrentEcmaContext(); + bool noCircular = context->JoinStackPushFastPath(thisHandle); + if (!noCircular) { + return factory->GetEmptyString().GetTaggedValue(); + } if (thisHandle->IsStableJSArray(thread)) { return JSStableArray::Join(JSHandle::Cast(thisHandle), argv); } - auto factory = thread->GetEcmaVM()->GetFactory(); // 1. Let O be ToObject(this value). JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); // 2. ReturnIfAbrupt(O). - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, thisHandle); JSHandle thisObjVal(thisObjHandle); // 3. Let len be ToLength(Get(O, "length")). int64_t len = ArrayHelper::GetLength(thread, thisObjVal); // 4. ReturnIfAbrupt(len). - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, thisHandle); // 5. If separator is undefined, let separator be the single-element String ",". // 6. Let sep be ToString(separator). @@ -1257,7 +1258,7 @@ JSTaggedValue BuiltinsArray::Join(EcmaRuntimeCallInfo *argv) JSHandle sepStringHandle = JSTaggedValue::ToString(thread, sepHandle); // 7. ReturnIfAbrupt(sep). - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, thisHandle); std::u16string sepStr = EcmaStringAccessor(sepStringHandle).ToU16String(); // 8. If len is zero, return the empty String. @@ -1277,22 +1278,19 @@ JSTaggedValue BuiltinsArray::Join(EcmaRuntimeCallInfo *argv) // e. Let R be a String value produced by concatenating S and next. // f. Increase k by 1. std::u16string concatStr; - std::u16string concatStrNew; for (int64_t k = 0; k < len; k++) { std::u16string nextStr; JSHandle element = JSArray::FastGetPropertyByValue(thread, thisObjVal, k); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, thisHandle); if (!element->IsUndefined() && !element->IsNull()) { JSHandle nextStringHandle = JSTaggedValue::ToString(thread, element); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, thisHandle); nextStr = EcmaStringAccessor(nextStringHandle).ToU16String(); } if (k > 0) { - concatStrNew = base::StringHelper::Append(concatStr, sepStr); - concatStr = base::StringHelper::Append(concatStrNew, nextStr); - continue; + concatStr.append(sepStr); } - concatStr = base::StringHelper::Append(concatStr, nextStr); + concatStr.append(nextStr); } // 14. Return R. @@ -1300,6 +1298,7 @@ JSTaggedValue BuiltinsArray::Join(EcmaRuntimeCallInfo *argv) auto *char16tData = const_cast(constChar16tData); auto *uint16tData = reinterpret_cast(char16tData); uint32_t u16strSize = concatStr.size(); + context->JoinStackPopFastPath(thisHandle); return factory->NewFromUtf16Literal(uint16tData, u16strSize).GetTaggedValue(); } @@ -1320,82 +1319,90 @@ JSTaggedValue BuiltinsArray::Keys(EcmaRuntimeCallInfo *argv) return iter.GetTaggedValue(); } -// 22.1.3.14 Array.prototype.lastIndexOf ( searchElement [ , fromIndex ] ) -JSTaggedValue BuiltinsArray::LastIndexOf(EcmaRuntimeCallInfo *argv) +JSTaggedValue BuiltinsArray::LastIndexOfStable( + EcmaRuntimeCallInfo *argv, JSThread *thread, const JSHandle &thisHandle) { - ASSERT(argv); - BUILTINS_API_TRACE(argv->GetThread(), Array, LastIndexOf); - JSThread *thread = argv->GetThread(); - [[maybe_unused]] EcmaHandleScope handleScope(thread); - + int64_t length = JSHandle::Cast(thisHandle)->GetArrayLength(); + if (length == 0) { + return JSTaggedValue(-1); + } + int64_t fromIndex = length - 1; uint32_t argc = argv->GetArgsNumber(); + // 2: [target, fromIndex]. Note that fromIndex is missing in most usage cases. + if (UNLIKELY(argc >= 2)) { + JSHandle fromIndexHandle = argv->GetCallArg(1); + fromIndex = ArrayHelper::GetLastStartIndex(thread, fromIndexHandle, length); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // Slow path when fromIndex is obtained from an ECMAObject + // due to potential side effects in its 'toString' and 'valueOf' methods which modify the array object. + if (UNLIKELY(fromIndexHandle->IsECMAObject())) { + return LastIndexOfSlowPath(argv, thread, thisHandle, fromIndex); + } + } + if (fromIndex < 0) { + return JSTaggedValue(-1); + } + JSHandle target = GetCallArg(argv, 0); + return JSStableArray::LastIndexOf( + thread, thisHandle, target, static_cast(fromIndex), static_cast(length)); +} +JSTaggedValue BuiltinsArray::LastIndexOfSlowPath( + EcmaRuntimeCallInfo *argv, JSThread *thread, const JSHandle &thisHandle) +{ // 1. Let O be ToObject(this value). - JSHandle thisHandle = GetThis(argv); JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); // 2. ReturnIfAbrupt(O). RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle thisObjVal(thisObjHandle); - - JSHandle searchElement = GetCallArg(argv, 0); - // 3. Let len be ToLength(Get(O, "length")). - int64_t len = ArrayHelper::GetLength(thread, thisObjVal); + int64_t length = ArrayHelper::GetLength(thread, thisObjVal); // 4. ReturnIfAbrupt(len). RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - // 5. If len is 0, return −1. - if (len == 0) { - return GetTaggedInt(-1); + if (length == 0) { + return JSTaggedValue(-1); } + // 6. If argument fromIndex was passed let n be ToInteger(fromIndex); else let n be 0. + int64_t fromIndex = ArrayHelper::GetLastStartIndexFromArgs(thread, argv, 1, length); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return LastIndexOfSlowPath(argv, thread, thisObjVal, fromIndex); +} - // 6. If argument fromIndex was passed let n be ToInteger(fromIndex); else let n be len-1. - double fromIndex = len - 1; - if (argc > 1) { - JSHandle msg1 = GetCallArg(argv, 1); - JSTaggedNumber fromIndexTemp = JSTaggedValue::ToNumber(thread, msg1); - // 7. ReturnIfAbrupt(n). - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - fromIndex = base::NumberHelper::TruncateDouble(fromIndexTemp.GetNumber()); +JSTaggedValue BuiltinsArray::LastIndexOfSlowPath( + EcmaRuntimeCallInfo *argv, JSThread *thread, const JSHandle &thisObjVal, int64_t fromIndex) +{ + if (fromIndex < 0) { + return JSTaggedValue(-1); } - - // 8. If n ≥ 0, let k be min(n, len – 1). - // 9. Else n < 0, - // a. Let k be len - abs(n). - int64_t from = 0; - if (fromIndex >= 0) { - from = (len - 1) < fromIndex ? len - 1 : fromIndex; - } else { - double tempFrom = len + fromIndex; - from = tempFrom >= 0 ? tempFrom : -1; - } - - // 10. Repeat, while k≥ 0 - // a. Let kPresent be HasProperty(O, ToString(k)). - // b. ReturnIfAbrupt(kPresent). - // c. If kPresent is true, then - // i. Let elementK be Get(O, ToString(k)). - // ii. ReturnIfAbrupt(elementK). - // iii. Let same be the result of performing Strict Equality Comparison searchElement === elementK. - // iv. If same is true, return k. - // d. Decrease k by 1. - JSMutableHandle key(thread, JSTaggedValue::Undefined()); - while (from >= 0) { - key.Update(JSTaggedValue(from)); - bool exists = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, key)); + JSMutableHandle keyHandle(thread, JSTaggedValue::Undefined()); + JSHandle target = base::BuiltinsBase::GetCallArg(argv, 0); + // 11. Repeat, while k < len + for (int64_t curIndex = fromIndex; curIndex >= 0; --curIndex) { + keyHandle.Update(JSTaggedValue(curIndex)); + bool found = ArrayHelper::ElementIsStrictEqualTo(thread, thisObjVal, keyHandle, target); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - if (exists) { - JSHandle kValueHandle = JSArray::FastGetPropertyByValue(thread, thisObjVal, key); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - if (JSTaggedValue::StrictEqual(thread, searchElement, kValueHandle)) { - return GetTaggedDouble(from); - } + if (UNLIKELY(found)) { + return JSTaggedValue(curIndex); } - from--; } + // 12. Return -1. + return JSTaggedValue(-1); +} - // 11. Return -1. - return GetTaggedInt(-1); +// 22.1.3.14 Array.prototype.lastIndexOf ( searchElement [ , fromIndex ] ) +JSTaggedValue BuiltinsArray::LastIndexOf(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), Array, IndexOf); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + JSHandle thisHandle = GetThis(argv); + if (thisHandle->IsStableJSArray(thread)) { + return LastIndexOfStable(argv, thread, thisHandle); + } + return LastIndexOfSlowPath(argv, thread, thisHandle); } // 22.1.3.15 Array.prototype.map ( callbackfn [ , thisArg ] ) @@ -1458,7 +1465,7 @@ JSTaggedValue BuiltinsArray::Map(EcmaRuntimeCallInfo *argv) JSMutableHandle key(thread, JSTaggedValue::Undefined()); JSMutableHandle mapResultHandle(thread, JSTaggedValue::Undefined()); JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); - const int32_t argsLength = 3; // 3: «kValue, k, O» + const uint32_t argsLength = 3; // 3: «kValue, k, O» while (k < len) { bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, k); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -1662,6 +1669,10 @@ JSTaggedValue BuiltinsArray::Reduce(EcmaRuntimeCallInfo *argv) } } + if (thisObjVal->IsStableJSArray(thread)) { + JSStableArray::Reduce(thread, thisObjHandle, callbackFnHandle, accumulator, k, len); + } + // 10. Repeat, while k < len // a. Let Pk be ToString(k). // b. Let kPresent be HasProperty(O, Pk). @@ -1825,6 +1836,10 @@ JSTaggedValue BuiltinsArray::Reverse(EcmaRuntimeCallInfo *argv) int64_t len = ArrayHelper::GetLength(thread, thisObjVal); // 4. ReturnIfAbrupt(len). RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // Fast path for stable array. Returns thisValue. + if (thisObjVal->IsStableJSArray(thread)) { + return JSStableArray::Reverse(thread, thisObjHandle, len); + } // 5. Let middle be floor(len/2). int64_t middle = std::floor(len / 2); @@ -1868,9 +1883,6 @@ JSTaggedValue BuiltinsArray::Reverse(EcmaRuntimeCallInfo *argv) JSMutableHandle upperP(thread, JSTaggedValue::Undefined()); JSHandle lowerValueHandle(thread, JSTaggedValue::Undefined()); JSHandle upperValueHandle(thread, JSTaggedValue::Undefined()); - if (thisObjVal->IsStableJSArray(thread)) { - JSStableArray::Reverse(thread, thisObjHandle, thisHandle, lower, len); - } while (lower != middle) { int64_t upper = len - lower - 1; lowerP.Update(JSTaggedValue(lower)); @@ -2067,25 +2079,7 @@ JSTaggedValue BuiltinsArray::Slice(EcmaRuntimeCallInfo *argv) int64_t count = final > k ? (final - k) : 0; if (thisHandle->IsStableJSArray(thread) && !thisObjHandle->GetJSHClass()->HasConstructor()) { - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - JSHandle destElements = factory->NewTaggedArray(count); - JSHandle newArrayHandle = factory->NewJSStableArrayWithElements(destElements); - TaggedArray *srcElements = TaggedArray::Cast(thisObjHandle->GetElements().GetTaggedObject()); - - uint32_t length = srcElements->GetLength(); - if (length > k + count) { - for (uint32_t idx = 0; idx < count; idx++) { - destElements->Set(thread, idx, srcElements->Get(k + idx)); - } - } else { - for (uint32_t idx = 0; idx < count; idx++) { - uint32_t index = static_cast(k) + idx; - JSTaggedValue value = length > index ? srcElements->Get(index) : JSTaggedValue::Hole(); - destElements->Set(thread, idx, value); - } - } - - return newArrayHandle.GetTaggedValue(); + return JSStableArray::Slice(thread, thisObjHandle, k, count); } // 12. Let A be ArraySpeciesCreate(O, count). @@ -2189,16 +2183,15 @@ JSTaggedValue BuiltinsArray::Some(EcmaRuntimeCallInfo *argv) key.Update(JSTaggedValue(k)); JSHandle kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, key); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - const int32_t argsLength = 3; // 3: «kValue, k, O» + const uint32_t argsLength = 3; // 3: «kValue, k, O» JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue()); JSTaggedValue callResult = JSFunction::Call(info); - bool boolResult = callResult.ToBoolean(); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - if (boolResult) { + if (callResult.ToBoolean()) { return GetTaggedBoolean(true); } } @@ -2213,62 +2206,23 @@ JSTaggedValue BuiltinsArray::Some(EcmaRuntimeCallInfo *argv) JSTaggedValue BuiltinsArray::Sort(EcmaRuntimeCallInfo *argv) { ASSERT(argv); - BUILTINS_API_TRACE(argv->GetThread(), Array, Sort); JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, Array, Sort); [[maybe_unused]] EcmaHandleScope handleScope(thread); - // 1. Let obj be ToObject(this value). - JSHandle thisHandle = GetThis(argv); - JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - + // 1. If comparefn is not undefined and IsCallable(comparefn) is false, throw a TypeError exception. JSHandle callbackFnHandle = GetCallArg(argv, 0); if (!callbackFnHandle->IsUndefined() && !callbackFnHandle->IsCallable()) { THROW_TYPE_ERROR_AND_RETURN(thread, "Callable is false", JSTaggedValue::Exception()); } - // 2. Let len be ToLength(Get(obj, "length")). - int64_t len = ArrayHelper::GetArrayLength(thread, JSHandle(thisObjHandle)); - // 3. ReturnIfAbrupt(len). + // 2. Let obj be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - JSMutableHandle presentValue(thread, JSTaggedValue::Undefined()); - JSMutableHandle middleValue(thread, JSTaggedValue::Undefined()); - JSMutableHandle previousValue(thread, JSTaggedValue::Undefined()); - for (int i = 1; i < len; i++) { - int beginIndex = 0; - int endIndex = i; - presentValue.Update(ObjectFastOperator::FastGetPropertyByIndex(thread, thisObjHandle.GetTaggedValue(), i)); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - while (beginIndex < endIndex) { - int middleIndex = (beginIndex + endIndex) / 2; // 2 : half - middleValue.Update( - ObjectFastOperator::FastGetPropertyByIndex(thread, thisObjHandle.GetTaggedValue(), middleIndex)); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - double compareResult = ArrayHelper::SortCompare(thread, callbackFnHandle, middleValue, presentValue); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - if (compareResult > 0) { - endIndex = middleIndex; - } else { - beginIndex = middleIndex + 1; - } - } - - if (endIndex >= 0 && endIndex < i) { - for (int j = i; j > endIndex; j--) { - previousValue.Update( - ObjectFastOperator::FastGetPropertyByIndex(thread, thisObjHandle.GetTaggedValue(), j - 1)); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - ObjectFastOperator::FastSetPropertyByIndex(thread, thisObjHandle.GetTaggedValue(), j, - previousValue.GetTaggedValue()); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - } - ObjectFastOperator::FastSetPropertyByIndex(thread, thisObjHandle.GetTaggedValue(), endIndex, - presentValue.GetTaggedValue()); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - } - } - + // Array sort + JSArray::Sort(thread, JSHandle::Cast(thisObjHandle), callbackFnHandle); return thisObjHandle.GetTaggedValue(); } @@ -2368,6 +2322,7 @@ JSTaggedValue BuiltinsArray::Splice(EcmaRuntimeCallInfo *argv) toKey.Update(JSTaggedValue(k)); if (newArrayHandle->IsJSProxy()) { toKey.Update(JSTaggedValue::ToString(thread, toKey).GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); } JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, fromValue); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -2501,17 +2456,6 @@ JSTaggedValue BuiltinsArray::ToLocaleString(EcmaRuntimeCallInfo *argv) // 4. ReturnIfAbrupt(len). RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - // 5. Let separator be the String value for the list-separator String appropriate for the host environment’s - // current locale (this is derived in an implementation-defined way). - JSHandle sepHandle; - if ((GetCallArg(argv, 0)->IsUndefined())) { - sepHandle = JSHandle::Cast(ecmaVm->GetFactory()->NewFromASCII(",")); - } else { - sepHandle = GetCallArg(argv, 0); - } - JSHandle sepStringHandle = JSTaggedValue::ToString(thread, sepHandle); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - CString sepString = ConvertToString(*sepStringHandle); // 6. If len is zero, return the empty String. if (len == 0) { return GetTaggedString(thread, ""); @@ -2563,7 +2507,7 @@ JSTaggedValue BuiltinsArray::ToLocaleString(EcmaRuntimeCallInfo *argv) RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString nextString = ConvertToString(*nextStringHandle); if (k > 0) { - concatStr += sepString; + concatStr += STRING_SEPERATOR; concatStr += nextString; continue; } @@ -2913,6 +2857,7 @@ JSTaggedValue BuiltinsArray::Includes(EcmaRuntimeCallInfo *argv) while (from < len) { JSHandle handledFrom(thread, JSTaggedValue(from)); fromStr = JSTaggedValue::ToString(thread, handledFrom); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); key.Update(fromStr.GetTaggedValue()); kValueHandle.Update(JSArray::FastGetPropertyByValue(thread, thisObjVal, key).GetTaggedValue()); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -2935,6 +2880,9 @@ JSTaggedValue BuiltinsArray::At(EcmaRuntimeCallInfo *argv) // 1. Let O be ToObject(this value). JSHandle thisHandle = GetThis(argv); + if (thisHandle->IsStableJSArray(thread)) { + return JSStableArray::At(JSHandle::Cast(thisHandle), argv); + } JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); // ReturnIfAbrupt(O). RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -2972,4 +2920,441 @@ JSTaggedValue BuiltinsArray::At(EcmaRuntimeCallInfo *argv) RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return element.GetTaggedValue(); } + +// 23.1.3.39 Array.prototype.with ( index, value ) +// NOLINTNEXTLINE(readability-function-size) +JSTaggedValue BuiltinsArray::With(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, Array, With); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + // 2. Let len be ? LengthOfArrayLike(O). + int64_t len = ArrayHelper::GetLength(thread, thisObjVal); + // ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. Let relativeIndex be ? ToIntegerOrInfinity(relativeIndex). + JSTaggedNumber index = JSTaggedValue::ToInteger(thread, GetCallArg(argv, 0)); + // ReturnIfAbrupt(index). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int64_t relativeIndex = index.GetNumber(); + int64_t actualIndex = 0; + JSHandle value = GetCallArg(argv, 1); + // 4. If relativeIndex ≥ 0, let actualIndex be relativeIndex. + // 5. Else, let actualIndex be len + relativeIndex. + // 6. If actualIndex ≥ len or actualIndex < 0, throw a RangeError exception. + if (relativeIndex >= 0) { + actualIndex = relativeIndex; + } else { + actualIndex = len + relativeIndex; + } + if (actualIndex >= len || actualIndex < 0) { + THROW_RANGE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception()); + } + // 7. Let A be ? ArrayCreate(len). + JSTaggedValue newArray = + JSArray::ArrayCreate(thread, JSTaggedNumber(static_cast(len))).GetTaggedValue(); + // ReturnIfAbrupt(A). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle newArrayHandle(thread, newArray); + if (thisHandle->IsStableJSArray(thread) && !thisObjHandle->GetJSHClass()->HasConstructor()) { + return JSStableArray::With(thread, JSHandle::Cast(thisHandle), len, actualIndex, value); + } + // 8. Let k be 0. + int64_t k = 0; + // 9. Repeat, while k < len, + // a. Let Pk be ! ToString(𝔽(k)). + // b. If k is actualIndex, let fromValue be value. + // c. Else, let fromValue be ? Get(O, Pk). + // d. Perform ! CreateDataPropertyOrThrow(A, Pk, fromValue). + // e. Set k to k + 1. + JSMutableHandle fromKey(thread, JSTaggedValue::Undefined()); + JSHandle fromValue; + while (k < len) { + fromKey.Update(JSTaggedValue(k)); + if (k == actualIndex) { + fromValue = value; + } else { + fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, fromKey, fromValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + ++k; + } + // 10. Return A. + return newArrayHandle.GetTaggedValue(); +} + +// 23.1.3.34 Array.prototype.toSorted ( comparefn ) +JSTaggedValue BuiltinsArray::ToSorted(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, Array, ToSorted); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. If comparefn is not undefined and IsCallable(comparefn) is false, throw a TypeError exception. + JSHandle callbackFnHandle = GetCallArg(argv, 0); + if (!callbackFnHandle->IsUndefined() && !callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Callable is false", JSTaggedValue::Exception()); + } + + // 2. Let obj be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. Let len be ToLength(Get(obj, "length")). + int64_t len = ArrayHelper::GetArrayLength(thread, JSHandle(thisObjHandle)); + // ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 4. Let A be ? ArrayCreate(len). + JSTaggedValue newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(static_cast(len))).GetTaggedValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle newArrayHandle(thread, newArray); + + // 5. Let SortCompare be a new Abstract Closure with parameters (x, y) that captures comparefn and performs + // the following steps when called: + // a. Return ? CompareArrayElements(x, y, comparefn). + // 6. Let sortedList be ? SortIndexedProperties(O, len, SortCompare, read-through-holes). + JSHandle sortedList = + ArrayHelper::SortIndexedProperties(thread, JSHandle::Cast(thisObjHandle), len, callbackFnHandle, + base::HolesType::READ_THROUGH_HOLES); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + //7. Let j be 0. + int64_t j = 0; + // 8. Repeat, while j < len, + // a. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(j)), sortedList[j]). + // b. Set j to j + 1. + JSMutableHandle itemValue(thread, JSTaggedValue::Undefined()); + while (j < len) { + itemValue.Update(sortedList->Get(j)); + JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, j, itemValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + ++j; + } + // 9. Return A. + return newArrayHandle.GetTaggedValue(); +} + +// 23.1.3.35 Array.prototype.toSpliced ( start, skipCount, ...items ) +JSTaggedValue BuiltinsArray::ToSpliced(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, Array, ToSpliced); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + uint32_t argc = argv->GetArgsNumber(); + // 1. Let O be ? ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + // 2. Let len be ? LengthOfArrayLike(O). + int64_t len = ArrayHelper::GetArrayLength(thread, thisObjVal); + // ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int64_t actualStart = 0; + int64_t actualSkipCount = 0; + int64_t newLen = 0; + int64_t insertCount = 0; + // 3. Let relativeStart be ? ToIntegerOrInfinity(start). + if (argc > 0) { + JSTaggedNumber argStart = JSTaggedValue::ToInteger(thread, GetCallArg(argv, 0)); + // ReturnIfAbrupt(relativeStart). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double relativeStart = argStart.GetNumber(); + // 4. If relativeStart = -∞, let k be 0. + // 5. Else if relativeStart < 0, let k be max(len + relativeStart, 0). + // 6. Else, let k be min(relativeStart, len). + if (relativeStart < 0) { + double tempStart = relativeStart + len; + actualStart = tempStart > 0 ? tempStart : 0; + } else { + actualStart = relativeStart < len ? relativeStart : len; + } + actualSkipCount = len - actualStart; + } + // 7. Let insertCount be the number of elements in items. + // 8. If start is not present, then + // a. Let actualSkipCount be 0. + // 9. Else if skipCount is not present, then + // a. Let actualSkipCount be len - actualStart. + // 10. Else, + // a. Let sc be ? ToIntegerOrInfinity(skipCount). + // b. Let actualSkipCount be the result of clamping sc between 0 and len - actualStart. + if (argc > 1) { + insertCount = argc - 2; // 2:2 means there two arguments before the insert items. + JSTaggedNumber argSkipCount = JSTaggedValue::ToInteger(thread, GetCallArg(argv, 1)); + // ReturnIfAbrupt(argSkipCount). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + double skipCount = argSkipCount.GetNumber(); + skipCount = skipCount > 0 ? skipCount : 0; + actualSkipCount = skipCount < (len - actualStart) ? skipCount : len - actualStart; + } + // 11. Let newLen be len + insertCount - actualSkipCount. + newLen = len + insertCount - actualSkipCount; + // 12. If newLen > 2^53 - 1, throw a TypeError exception. + if (newLen > base::MAX_SAFE_INTEGER) { + THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception()); + } + if (thisHandle->IsStableJSArray(thread) && !thisObjHandle->GetJSHClass()->HasConstructor()) { + return JSStableArray::ToSpliced(JSHandle::Cast(thisHandle), argv, argc, actualStart, + actualSkipCount, newLen); + } + // 13. Let A be ? ArrayCreate(newLen). + JSHandle newJsTaggedArray = + JSArray::ArrayCreate(thread, JSTaggedNumber(static_cast(newLen))); + // ReturnIfAbrupt(newArray). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle newArrayHandle(thread, newJsTaggedArray.GetTaggedValue()); + // 14. Let i be 0. + int64_t i = 0; + // 15. Let r be actualStart + actualSkipCount. + int64_t r = actualStart + actualSkipCount; + // 16. Repeat, while i < actualStart, + // a. Let Pi be ! ToString(𝔽(i)). + // b. Let iValue be ? Get(O, Pi). + // c. Perform ! CreateDataPropertyOrThrow(A, Pi, iValue). + // d. Set i to i + 1. + while (i < actualStart) { + JSHandle iValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, i); + // ReturnIfAbrupt(iValue). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, i, iValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + ++i; + } + // 17. For each element E of items, do + // a. Let Pi be ! ToString(𝔽(i)). + // b. Perform ! CreateDataPropertyOrThrow(A, Pi, E). + // c. Set i to i + 1. + JSMutableHandle pi(thread, JSTaggedValue::Undefined()); + for (int64_t pos = 2; pos < argc; ++pos) { // 2:2 means there two arguments before the insert items. + pi.Update(JSTaggedValue(i)); + JSHandle element = GetCallArg(argv, pos); + JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, pi, element); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + ++i; + } + // 18. Repeat, while i < newLen, + // a. Let Pi be ! ToString(𝔽(i)). + // b. Let from be ! ToString(𝔽(r)). + // c. Let fromValue be ? Get(O, from). + // d. Perform ! CreateDataPropertyOrThrow(A, Pi, fromValue). + // e. Set i to i + 1. + // f. Set r to r + 1. + JSMutableHandle from(thread, JSTaggedValue::Undefined()); + while (i < newLen) { + pi.Update(JSTaggedValue(i)); + from.Update(JSTaggedValue(r)); + JSHandle fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, from); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, pi, fromValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + ++i; + ++r; + } + JSHandle lengthKey = thread->GlobalConstants()->GetHandledLengthString(); + JSHandle newLenHandle(thread, JSTaggedValue(newLen)); + JSTaggedValue::SetProperty(thread, newJsTaggedArray, lengthKey, newLenHandle, true); + // ReturnIfAbrupt(setStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 19. Return A. + return newArrayHandle.GetTaggedValue(); +} + +// 23.1.3.11 Array.prototype.findLast ( predicate [ , thisArg ] ) +JSTaggedValue BuiltinsArray::FindLast(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, Array, FindLast); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + int64_t len = ArrayHelper::GetLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If IsCallable(predicate) is false, throw a TypeError exception. + JSHandle callbackFnHandle = GetCallArg(argv, 0); + if (!callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the predicate is not callable.", JSTaggedValue::Exception()); + } + + // 6. If thisArg was supplied, let T be thisArg; else let T be undefined. + JSHandle thisArgHandle = GetCallArg(argv, 1); + + // 7. Let k be (len - 1). + // 8. Repeat, while k >= 0 + // a. Let Pk be ToString(k). + // b. Let kValue be Get(O, Pk). + // c. ReturnIfAbrupt(kValue). + // d. Let testResult be ToBoolean(Call(predicate, T, «kValue, k, O»)). + // e. ReturnIfAbrupt(testResult). + // f. If testResult is true, return kValue. + // g. Decrease k by 1. + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + int64_t k = len - 1; + JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); + const uint32_t argsLength = 3; // 3: «kValue, k, O» + while (k >= 0) { + JSHandle kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + key.Update(JSTaggedValue(k)); + EcmaRuntimeCallInfo *info = + EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue()); + JSTaggedValue callResult = JSFunction::Call(info); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (callResult.ToBoolean()) { + return kValue.GetTaggedValue(); + } + k--; + } + + // 9. Return undefined. + return JSTaggedValue::Undefined(); +} + +// 23.1.3.12 Array.prototype.findLastIndex ( predicate [ , thisArg ] ) +JSTaggedValue BuiltinsArray::FindLastIndex(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, Array, FindLastIndex); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // 2. ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 3. Let len be ToLength(Get(O, "length")). + int64_t len = ArrayHelper::GetLength(thread, thisObjVal); + // 4. ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. If IsCallable(predicate) is false, throw a TypeError exception. + JSHandle callbackFnHandle = GetCallArg(argv, 0); + if (!callbackFnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the predicate is not callable.", JSTaggedValue::Exception()); + } + + // 6. If thisArg was supplied, let T be thisArg; else let T be undefined. + JSHandle thisArgHandle = GetCallArg(argv, 1); + + // 7. Let k be (len - 1). + // 8. Repeat, while k >=0 + // a. Let Pk be ToString(k). + // b. Let kValue be Get(O, Pk). + // c. ReturnIfAbrupt(kValue). + // d. Let testResult be ToBoolean(Call(predicate, T, «kValue, k, O»)). + // e. ReturnIfAbrupt(testResult). + // f. If testResult is true, return k. + // g. Decrease k by 1. + int64_t k = len - 1; + JSTaggedValue callResult = GetTaggedBoolean(true); + if (thisObjVal->IsStableJSArray(thread)) { + callResult = + JSStableArray::HandleFindLastIndexOfStable(thread, thisObjHandle, callbackFnHandle, thisArgHandle, k); + if (callResult.ToBoolean()) { + return GetTaggedDouble(k); + } + } + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); + const uint32_t argsLength = 3; // 3: «kValue, k, O» + while (k >= 0) { + JSHandle kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + key.Update(JSTaggedValue(k)); + EcmaRuntimeCallInfo *info = + EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + info->SetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisObjVal.GetTaggedValue()); + callResult = JSFunction::Call(info); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (callResult.ToBoolean()) { + return GetTaggedDouble(k); + } + k--; + } + + // 9. Return -1. + return GetTaggedDouble(-1); +} + +// 23.1.3.33 Array.prototype.toReversed ( ) +JSTaggedValue BuiltinsArray::ToReversed(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, Array, ToReversed); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjVal(thisObjHandle); + + // 2. Let len be ? LengthOfArrayLike(O). + int64_t len = ArrayHelper::GetLength(thread, thisObjVal); + // ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (thisHandle->IsStableJSArray(thread) && !thisObjHandle->GetJSHClass()->HasConstructor()) { + return JSStableArray::ToReversed(thread, JSHandle::Cast(thisHandle), len); + } + // 3. Let A be ? ArrayCreate(len). + JSTaggedValue newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(static_cast(len))).GetTaggedValue(); + // ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle newArrayHandle(thread, newArray); + + // 4. Let k be 0. + // 5. Repeat, while k < len, + // a. Let from be ! ToString(𝔽(len - k - 1)). + // b. Let Pk be ! ToString(𝔽(k)). + // c. Let fromValue be ? Get(O, from). + // d. Perform ! CreateDataPropertyOrThrow(A, Pk, fromValue). + // e. Set k to k + 1. + JSMutableHandle fromKey(thread, JSTaggedValue::Undefined()); + JSMutableHandle toKey(thread, JSTaggedValue::Undefined()); + int64_t k = 0; + while (k < len) { + int64_t from = len - k - 1; + fromKey.Update(JSTaggedValue(from)); + toKey.Update(JSTaggedValue(k)); + JSHandle fromValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, fromKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, fromValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + k++; + } + // 6. Return A. + return newArrayHandle.GetTaggedValue(); +} } // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_array.h b/ecmascript/builtins/builtins_array.h index e63cd3e969579bdda31fcb2fe7acc097c16d5a02..08a0a8c9271b567a208920e5919e4df94c3670b9 100644 --- a/ecmascript/builtins/builtins_array.h +++ b/ecmascript/builtins/builtins_array.h @@ -18,9 +18,103 @@ #include "ecmascript/base/builtins_base.h" +// List of functions in Array, excluding the '@@' properties. +// V(name, func, length, stubIndex) +// where BuiltinsArray::func refers to the native implementation of Array[name]. +// kungfu::BuiltinsStubCSigns::stubIndex refers to the builtin stub index, or INVALID if no stub available. +#define BUILTIN_ARRAY_FUNCTIONS(V) \ + /* Array.from ( items [ , mapfn [ , thisArg ] ] ) */ \ + V("from", From, 1, INVALID) \ + /* Array.isArray ( arg ) */ \ + V("isArray", IsArray, 1, INVALID) \ + /* Array.of ( ...items ) */ \ + V("of", Of, 0, INVALID) + +// List of functions in Array.prototype, excluding the constructor and '@@' properties. +// V(name, func, length, stubIndex) +// where BuiltinsArray::func refers to the native implementation of Array.prototype[name]. +#define BUILTIN_ARRAY_PROTOTYPE_FUNCTIONS(V) \ + /* Array.prototype.at ( index ) */ \ + V("at", At, 1, INVALID) \ + /* Array.prototype.concat ( ...items ) */ \ + V("concat", Concat, 1, ArrayConcat) \ + /* Array.prototype.copyWithin ( target, start [ , end ] ) */ \ + V("copyWithin", CopyWithin, 2, INVALID) \ + /* Array.prototype.entries ( ) */ \ + V("entries", Entries, 0, INVALID) \ + /* Array.prototype.every ( callbackfn [ , thisArg ] ) */ \ + V("every", Every, 1, INVALID) \ + /* Array.prototype.fill ( value [ , start [ , end ] ] ) */ \ + V("fill", Fill, 1, INVALID) \ + /* Array.prototype.filter ( callbackfn [ , thisArg ] ) */ \ + V("filter", Filter, 1, ArrayFilter) \ + /* Array.prototype.find ( predicate [ , thisArg ] ) */ \ + V("find", Find, 1, INVALID) \ + /* Array.prototype.findIndex ( predicate [ , thisArg ] ) */ \ + V("findIndex", FindIndex, 1, INVALID) \ + /* Array.prototype.findLast ( predicate [ , thisArg ] ) */ \ + V("findLast", FindLast, 1, INVALID) \ + /* Array.prototype.findLastIndex ( predicate [ , thisArg ] ) */ \ + V("findLastIndex", FindLastIndex, 1, INVALID) \ + /* Array.prototype.flat ( [ depth ] ) */ \ + V("flat", Flat, 0, INVALID) \ + /* Array.prototype.flatMap ( mapperFunction [ , thisArg ] ) */ \ + V("flatMap", FlatMap, 1, INVALID) \ + /* Array.prototype.forEach ( callbackfn [ , thisArg ] ) */ \ + V("forEach", ForEach, 1, ArrayForEach) \ + /* Array.prototype.includes ( searchElement [ , fromIndex ] ) */ \ + V("includes", Includes, 1, INVALID) \ + /* Array.prototype.indexOf ( searchElement [ , fromIndex ] ) */ \ + V("indexOf", IndexOf, 1, ArrayIndexOf) \ + /* Array.prototype.join ( separator ) */ \ + V("join", Join, 1, INVALID) \ + /* Array.prototype.keys ( ) */ \ + V("keys", Keys, 0, INVALID) \ + /* Array.prototype.lastIndexOf ( searchElement [ , fromIndex ] ) */ \ + V("lastIndexOf", LastIndexOf, 1, ArrayLastIndexOf) \ + /* Array.prototype.map ( callbackfn [ , thisArg ] ) */ \ + V("map", Map, 1, INVALID) \ + /* Array.prototype.pop ( ) */ \ + V("pop", Pop, 0, INVALID) \ + /* Array.prototype.push ( ...items ) */ \ + V("push", Push, 1, ArrayPush) \ + /* Array.prototype.reduce ( callbackfn [ , initialValue ] ) */ \ + V("reduce", Reduce, 1, INVALID) \ + /* Array.prototype.reduceRight ( callbackfn [ , initialValue ] ) */ \ + V("reduceRight", ReduceRight, 1, INVALID) \ + /* Array.prototype.reverse ( ) */ \ + V("reverse", Reverse, 0, ArrayReverse) \ + /* Array.prototype.shift ( ) */ \ + V("shift", Shift, 0, INVALID) \ + /* Array.prototype.slice ( start, end ) */ \ + V("slice", Slice, 2, ArraySlice) \ + /* Array.prototype.some ( callbackfn [ , thisArg ] ) */ \ + V("some", Some, 1, INVALID) \ + /* Array.prototype.sort ( comparefn ) */ \ + V("sort", Sort, 1, SORT) \ + /* Array.prototype.splice ( start, deleteCount, ...items ) */ \ + V("splice", Splice, 2, INVALID) \ + /* Array.prototype.toLocaleString ( [ reserved1 [ , reserved2 ] ] ) */ \ + V("toLocaleString", ToLocaleString, 0, INVALID) \ + /* Array.prototype.toReversed ( ) */ \ + V("toReversed", ToReversed, 0, INVALID) \ + /* Array.prototype.toSorted ( comparefn ) */ \ + V("toSorted", ToSorted, 1, INVALID) \ + /* Array.prototype.toSpliced ( start, skipCount, ...items ) */ \ + V("toSpliced", ToSpliced, 2, INVALID) \ + /* Array.prototype.toString ( ) */ \ + V("toString", ToString, 0, INVALID) \ + /* Array.prototype.unshift ( ...items ) */ \ + V("unshift", Unshift, 1, INVALID) \ + /* Array.prototype.values ( ) */ \ + V("values", Values, 0, INVALID) \ + /* Array.prototype.with ( index, value ) */ \ + V("with", With, 2, INVALID) + namespace panda::ecmascript::builtins { static constexpr uint8_t INDEX_TWO = 2; static constexpr uint8_t INDEX_THREE = 3; +static const CString STRING_SEPERATOR = ","; class BuiltinsArray : public base::BuiltinsBase { public: // 22.1.1 @@ -102,7 +196,58 @@ public: static JSTaggedValue FlatMap(EcmaRuntimeCallInfo *argv); // 23.1.3.1 Array.prototype.at ( index ) static JSTaggedValue At(EcmaRuntimeCallInfo *argv); + // 23.1.3.33 Array.prototype.toReversed ( ) + static JSTaggedValue ToReversed(EcmaRuntimeCallInfo *argv); + // 23.1.3.39 Array.prototype.with ( index, value ) + static JSTaggedValue With(EcmaRuntimeCallInfo *argv); + // 23.1.3.34 Array.prototype.toSorted ( comparefn ) + static JSTaggedValue ToSorted(EcmaRuntimeCallInfo *argv); + // 23.1.3.11 + static JSTaggedValue FindLast(EcmaRuntimeCallInfo *argv); + // 23.1.3.12 + static JSTaggedValue FindLastIndex(EcmaRuntimeCallInfo *argv); + // 23.1.3.35 Array.prototype.toSpliced ( start, skipCount, ...items ) + static JSTaggedValue ToSpliced(EcmaRuntimeCallInfo *argv); + + // Excluding the '@@' internal properties + static Span GetArrayFunctions() + { + return Span(ARRAY_FUNCTIONS); + } + + // Excluding the constructor and '@@' internal properties. + static Span GetArrayPrototypeFunctions() + { + return Span(ARRAY_PROTOTYPE_FUNCTIONS); + } + +private: +#define BUILTIN_ARRAY_FUNCTION_ENTRY(name, method, length, id) \ + base::BuiltinFunctionEntry::Create(name, BuiltinsArray::method, length, kungfu::BuiltinsStubCSigns::id), + + static constexpr std::array ARRAY_FUNCTIONS = { + BUILTIN_ARRAY_FUNCTIONS(BUILTIN_ARRAY_FUNCTION_ENTRY) + }; + static constexpr std::array ARRAY_PROTOTYPE_FUNCTIONS = { + BUILTIN_ARRAY_PROTOTYPE_FUNCTIONS(BUILTIN_ARRAY_FUNCTION_ENTRY) + }; +#undef BUILTIN_ARRAY_FUNCTION_ENTRY + + static JSTaggedValue IndexOfStable( + EcmaRuntimeCallInfo *argv, JSThread *thread, const JSHandle &thisHandle); + static JSTaggedValue IndexOfSlowPath( + EcmaRuntimeCallInfo *argv, JSThread *thread, const JSHandle &thisHandle); + static JSTaggedValue IndexOfSlowPath( + EcmaRuntimeCallInfo *argv, JSThread *thread, const JSHandle &thisObjVal, + int64_t length, int64_t fromIndex); + + static JSTaggedValue LastIndexOfStable( + EcmaRuntimeCallInfo *argv, JSThread *thread, const JSHandle &thisHandle); + static JSTaggedValue LastIndexOfSlowPath( + EcmaRuntimeCallInfo *argv, JSThread *thread, const JSHandle &thisHandle); + static JSTaggedValue LastIndexOfSlowPath( + EcmaRuntimeCallInfo *argv, JSThread *thread, const JSHandle &thisObjVal, int64_t fromIndex); }; } // namespace panda::ecmascript::builtins -#endif // ECMASCRIPT_BUILTINS_BUILTINS_ARRAY_H \ No newline at end of file +#endif // ECMASCRIPT_BUILTINS_BUILTINS_ARRAY_H diff --git a/ecmascript/builtins/builtins_arraybuffer.cpp b/ecmascript/builtins/builtins_arraybuffer.cpp index aaa5db6007de5f1f5d188088eca35bd3e9489fb6..0373de8b430a25d0fa5c5d0848404f4b9de5c3b8 100644 --- a/ecmascript/builtins/builtins_arraybuffer.cpp +++ b/ecmascript/builtins/builtins_arraybuffer.cpp @@ -87,7 +87,7 @@ JSTaggedValue BuiltinsArrayBuffer::GetByteLength(EcmaRuntimeCallInfo *argv) JSThread *thread = argv->GetThread(); BUILTINS_API_TRACE(thread, ArrayBuffer, GetByteLength); [[maybe_unused]] EcmaHandleScope handleScope(thread); - + // 1. Let O be the this value. JSHandle thisHandle = GetThis(argv); // 2. If Type(O) is not Object, throw a TypeError exception. @@ -180,6 +180,7 @@ JSTaggedValue BuiltinsArrayBuffer::Slice(EcmaRuntimeCallInfo *argv) RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); info->SetCallArg(JSTaggedValue(newLen)); JSTaggedValue taggedNewArrBuf = JSFunction::Construct(info); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle newArrBuf(thread, taggedNewArrBuf); // 16. ReturnIfAbrupt(new). RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -384,7 +385,7 @@ JSTaggedValue BuiltinsArrayBuffer::SetValueInBuffer(JSThread *thread, JSTaggedVa void *pointer = GetDataPointFromBuffer(arrBuf); uint8_t *block = reinterpret_cast(pointer); double val = value.GetTaggedValue().GetNumber(); - return SetValueInBuffer(byteIndex, block, type, val, littleEndian); + return SetValueInBuffer(thread, byteIndex, block, type, val, littleEndian); } // es12 25.1.2.7 IsBigIntElementType ( type ) @@ -461,9 +462,9 @@ T BuiltinsArrayBuffer::LittleEndianToBigEndian64Bit(T liValue) template JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForInteger(uint8_t *block, uint32_t byteIndex, bool littleEndian) { - static_assert(std::is_integral_v, "T must be integral"); - static_assert(sizeof(T) == size, "Invalid number size"); - static_assert(sizeof(T) >= sizeof(uint16_t), "T must have a size more than uint8"); + ASSERT_PRINT(std::is_integral_v, "T must be integral"); + ASSERT_PRINT(sizeof(T) == size, "Invalid number size"); + ASSERT_PRINT(sizeof(T) >= sizeof(uint16_t), "T must have a size more than uint8"); ASSERT(size >= NumberSize::UINT16 || size <= NumberSize::FLOAT64); T res = *reinterpret_cast(block + byteIndex); @@ -485,38 +486,50 @@ JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForInteger(uint8_t *block, template JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForFloat(uint8_t *block, uint32_t byteIndex, bool littleEndian) { - static_assert(std::is_same_v || std::is_same_v, "T must be correct type"); - static_assert(sizeof(T) == size, "Invalid number size"); + ASSERT_PRINT((std::is_same_v || std::is_same_v), "T must be correct type"); + ASSERT_PRINT(sizeof(T) == size, "Invalid number size"); UnionType unionValue = {0}; // NOLINTNEXTLINE(readability-braces-around-statements) if constexpr (std::is_same_v) { unionValue.uValue = *reinterpret_cast(block + byteIndex); - if (std::isnan(unionValue.value)) { - return GetTaggedDouble(unionValue.value); - } - if (!littleEndian) { - uint32_t res = LittleEndianToBigEndian(unionValue.uValue); - return GetTaggedDouble(base::bit_cast(res)); - } + uint32_t res = LittleEndianToBigEndian(unionValue.uValue); + return CommonConvert(unionValue.value, res, littleEndian); } else if constexpr (std::is_same_v) { // NOLINTNEXTLINE(readability-braces-around-statements) unionValue.uValue = *reinterpret_cast(block + byteIndex); - if (std::isnan(unionValue.value) && !JSTaggedValue::IsImpureNaN(unionValue.value)) { - return GetTaggedDouble(unionValue.value); - } - if (!littleEndian) { - uint64_t res = LittleEndianToBigEndian64Bit(unionValue.uValue); - return GetTaggedDouble(base::bit_cast(res)); - } + uint64_t res = LittleEndianToBigEndian64Bit(unionValue.uValue); + return CommonConvert(unionValue.value, res, littleEndian); } return GetTaggedDouble(unionValue.value); } + +template +JSTaggedValue BuiltinsArrayBuffer::CommonConvert(T1 &value, T2 &res, bool littleEndian) +{ + if (std::isnan(value) && !JSTaggedValue::IsImpureNaN(value)) { + return GetTaggedDouble(value); + } + if (!littleEndian) { + T1 d = base::bit_cast(res); + if (JSTaggedValue::IsImpureNaN(d)) { + return GetTaggedDouble(base::NAN_VALUE); + } + return GetTaggedDouble(d); + } else { + if (JSTaggedValue::IsImpureNaN(value)) { + return GetTaggedDouble(base::NAN_VALUE); + } + } + return GetTaggedDouble(value); +} + + template JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForBigInt(JSThread *thread, uint8_t *block, uint32_t byteIndex, bool littleEndian) { - static_assert(std::is_same_v || std::is_same_v, "T must be uint64_t/int64_t"); + ASSERT_PRINT((std::is_same_v || std::is_same_v), "T must be uint64_t/int64_t"); auto pTmp = *reinterpret_cast(block + byteIndex); if (!littleEndian) { pTmp = LittleEndianToBigEndian64Bit(pTmp); @@ -531,7 +544,7 @@ JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForBigInt(JSThread *thread, template void BuiltinsArrayBuffer::SetValueInBufferForByte(double val, uint8_t *block, uint32_t byteIndex) { - static_assert(std::is_same_v || std::is_same_v, "T must be int8/uint8"); + ASSERT_PRINT((std::is_same_v || std::is_same_v), "T must be int8/uint8"); T res; if (std::isnan(val) || std::isinf(val)) { res = 0; @@ -562,8 +575,8 @@ void BuiltinsArrayBuffer::SetValueInBufferForUint8Clamped(double val, uint8_t *b template void BuiltinsArrayBuffer::SetValueInBufferForInteger(double val, uint8_t *block, uint32_t byteIndex, bool littleEndian) { - static_assert(std::is_integral_v, "T must be integral"); - static_assert(sizeof(T) >= sizeof(uint16_t), "T must have a size more than uint8"); + ASSERT_PRINT(std::is_integral_v, "T must be integral"); + ASSERT_PRINT(sizeof(T) >= sizeof(uint16_t), "T must have a size more than uint8"); T res; if (std::isnan(val) || std::isinf(val)) { res = 0; @@ -590,7 +603,7 @@ void BuiltinsArrayBuffer::SetValueInBufferForInteger(double val, uint8_t *block, template void BuiltinsArrayBuffer::SetValueInBufferForFloat(double val, uint8_t *block, uint32_t byteIndex, bool littleEndian) { - static_assert(std::is_same_v || std::is_same_v, "T must be float type"); + ASSERT_PRINT((std::is_same_v || std::is_same_v), "T must be float type"); auto data = static_cast(val); if (std::isnan(val)) { SetTypeData(block, data, byteIndex); @@ -614,7 +627,7 @@ void BuiltinsArrayBuffer::SetValueInBufferForBigInt(JSThread *thread, JSHandle &arrBuf, uint32_t byteIndex, bool littleEndian) { - static_assert(std::is_same_v || std::is_same_v, "T must be int64_t/uint64_t"); + ASSERT_PRINT((std::is_same_v || std::is_same_v), "T must be int64_t/uint64_t"); T value = 0; bool lossless = true; if constexpr(std::is_same_v) { @@ -631,16 +644,38 @@ void BuiltinsArrayBuffer::SetValueInBufferForBigInt(JSThread *thread, SetTypeData(block, value, byteIndex); } -JSTaggedValue BuiltinsArrayBuffer::FastSetValueInBuffer(JSTaggedValue arrBuf, uint32_t byteIndex, +template +void BuiltinsArrayBuffer::SetValueInBufferForBigInt(JSThread *thread, + double val, uint8_t *block, + uint32_t byteIndex, bool littleEndian) +{ + ASSERT_PRINT((std::is_same_v || std::is_same_v), "T must be int64_t/uint64_t"); + T value = 0; + bool lossless = true; + + JSHandle valHandle(thread, GetTaggedDouble(val)); + if constexpr(std::is_same_v) { + BigInt::BigIntToUint64(thread, valHandle, reinterpret_cast(&value), &lossless); + } else { + BigInt::BigIntToInt64(thread, valHandle, reinterpret_cast(&value), &lossless); + } + RETURN_IF_ABRUPT_COMPLETION(thread); + if (!littleEndian) { + value = LittleEndianToBigEndian64Bit(value); + } + SetTypeData(block, value, byteIndex); +} + +JSTaggedValue BuiltinsArrayBuffer::FastSetValueInBuffer(JSThread *thread, JSTaggedValue arrBuf, uint32_t byteIndex, DataViewType type, double val, bool littleEndian) { void *pointer = GetDataPointFromBuffer(arrBuf); uint8_t *block = reinterpret_cast(pointer); - return SetValueInBuffer(byteIndex, block, type, val, littleEndian); + return SetValueInBuffer(thread, byteIndex, block, type, val, littleEndian); } -JSTaggedValue BuiltinsArrayBuffer::SetValueInBuffer(uint32_t byteIndex, uint8_t *block, DataViewType type, double val, - bool littleEndian) +JSTaggedValue BuiltinsArrayBuffer::SetValueInBuffer(JSThread* thread, uint32_t byteIndex, uint8_t *block, + DataViewType type, double val, bool littleEndian) { switch (type) { case DataViewType::UINT8: @@ -670,6 +705,14 @@ JSTaggedValue BuiltinsArrayBuffer::SetValueInBuffer(uint32_t byteIndex, uint8_t case DataViewType::FLOAT64: SetValueInBufferForFloat(val, block, byteIndex, littleEndian); break; + case DataViewType::BIGINT64: + SetValueInBufferForBigInt(thread, val, block, byteIndex, littleEndian); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + break; + case DataViewType::BIGUINT64: + SetValueInBufferForBigInt(thread, val, block, byteIndex, littleEndian); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + break; default: LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); @@ -681,14 +724,15 @@ void *BuiltinsArrayBuffer::GetDataPointFromBuffer(JSTaggedValue arrBuf, uint32_t { if (arrBuf.IsByteArray()) { return reinterpret_cast(ToUintPtr(ByteArray::Cast(arrBuf.GetTaggedObject())->GetData()) + byteOffset); - } else { - JSArrayBuffer *arrayBuffer = JSArrayBuffer::Cast(arrBuf.GetTaggedObject()); - if (arrayBuffer->GetArrayBufferByteLength() == 0) { - return nullptr; - } - JSTaggedValue data = arrayBuffer->GetArrayBufferData(); - return reinterpret_cast(ToUintPtr(JSNativePointer::Cast(data.GetTaggedObject()) - ->GetExternalPointer()) + byteOffset); } + + JSArrayBuffer *arrayBuffer = JSArrayBuffer::Cast(arrBuf.GetTaggedObject()); + if (arrayBuffer->GetArrayBufferByteLength() == 0) { + return nullptr; + } + + JSTaggedValue data = arrayBuffer->GetArrayBufferData(); + return reinterpret_cast(ToUintPtr(JSNativePointer::Cast(data.GetTaggedObject()) + ->GetExternalPointer()) + byteOffset); } } // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_arraybuffer.h b/ecmascript/builtins/builtins_arraybuffer.h index f373bfc75ebf3a0782269db21ac66613c9147280..80cda3a205571fa773a41980875245ecf7d7727d 100644 --- a/ecmascript/builtins/builtins_arraybuffer.h +++ b/ecmascript/builtins/builtins_arraybuffer.h @@ -71,10 +71,10 @@ public: // es12 25.1.2.7 IsBigIntElementType ( type ) static bool IsBigIntElementType(DataViewType type); - static JSTaggedValue FastSetValueInBuffer(JSTaggedValue arrBuf, uint32_t byteIndex, DataViewType type, double val, - bool littleEndian); - static JSTaggedValue SetValueInBuffer(uint32_t byteIndex, uint8_t *block, DataViewType type, double val, - bool littleEndian); + static JSTaggedValue FastSetValueInBuffer(JSThread* thread, JSTaggedValue arrBuf, uint32_t byteIndex, + DataViewType type, double val, bool littleEndian); + static JSTaggedValue SetValueInBuffer(JSThread* thread, uint32_t byteIndex, uint8_t *block, + DataViewType type, double val, bool littleEndian); static JSTaggedValue GetValueFromBuffer(JSThread *thread, uint32_t byteIndex, uint8_t *block, DataViewType type, bool littleEndian); static void *GetDataPointFromBuffer(JSTaggedValue arrBuf, uint32_t byteOffset = 0); @@ -92,6 +92,8 @@ private: template static JSTaggedValue GetValueFromBufferForFloat(uint8_t *block, uint32_t byteIndex, bool littleEndian); + template + static JSTaggedValue CommonConvert(T1 &value, T2 &res, bool littleEndian); template static JSTaggedValue GetValueFromBufferForBigInt(JSThread *thread, uint8_t *block, uint32_t byteIndex, bool littleEndian); @@ -110,6 +112,10 @@ private: template static void SetValueInBufferForBigInt(JSThread *thread, const JSHandle &val, JSHandle &arrBuf, uint32_t byteIndex, bool littleEndian); + + template + static void SetValueInBufferForBigInt(JSThread *thread, double val, + uint8_t *block, uint32_t byteIndex, bool littleEndian); }; } // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_async_from_sync_iterator.cpp b/ecmascript/builtins/builtins_async_from_sync_iterator.cpp index 8ff19fd15f3e67a38094385597a8f4b984faa179..ae63f7c4b821b67d60a91dd5b80b999cb010e749 100644 --- a/ecmascript/builtins/builtins_async_from_sync_iterator.cpp +++ b/ecmascript/builtins/builtins_async_from_sync_iterator.cpp @@ -44,6 +44,7 @@ JSTaggedValue BuiltinsAsyncFromSyncIterator::Next(EcmaRuntimeCallInfo *argv) // 3.Let promiseCapability be ! NewPromiseCapability(%Promise%). JSHandle pcap = JSPromise::NewPromiseCapability(thread, JSHandle::Cast(env->GetPromiseFunction())); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 4.Let syncIteratorRecord be O.[[SyncIteratorRecord]]. JSHandle asyncIterator(thisValue); JSHandle syncIteratorRecord(thread, asyncIterator->GetSyncIteratorRecord()); @@ -79,6 +80,7 @@ JSTaggedValue BuiltinsAsyncFromSyncIterator::Throw(EcmaRuntimeCallInfo *argv) JSHandle env = vm->GetGlobalEnv(); JSHandle pcap = JSPromise::NewPromiseCapability(thread, JSHandle::Cast(env->GetPromiseFunction())); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 4.Let syncIterator be O.[[SyncIteratorRecord]].[[Iterator]]. JSHandle syncIteratorRecord(thread, asyncIterator->GetSyncIteratorRecord()); JSHandle syncIterator(thread, syncIteratorRecord->GetIterator()); @@ -91,9 +93,9 @@ JSTaggedValue BuiltinsAsyncFromSyncIterator::Throw(EcmaRuntimeCallInfo *argv) // 7.If throw is undefined, then if (throwResult->IsUndefined()) { JSHandle iterResult = JSIterator::CreateIterResultObject(thread, value, true); - JSHandle resolve(thread, pcap->GetResolve()); + JSHandle reject(thread, pcap->GetReject()); EcmaRuntimeCallInfo *info = - EcmaInterpreter::NewRuntimeCallInfo(thread, resolve, undefinedValue, undefinedValue, 1); + EcmaInterpreter::NewRuntimeCallInfo(thread, reject, undefinedValue, undefinedValue, 1); info->SetCallArg(iterResult.GetTaggedValue()); return pcap->GetPromise(); } @@ -102,10 +104,12 @@ JSTaggedValue BuiltinsAsyncFromSyncIterator::Throw(EcmaRuntimeCallInfo *argv) if (value->IsNull()) { EcmaRuntimeCallInfo *callInfo = EcmaInterpreter::NewRuntimeCallInfo(thread, throwResult, syncIterator, undefinedValue, 0); + RETURN_REJECT_PROMISE_IF_ABRUPT(thread, throwResult, pcap); ret = JSFunction::Call(callInfo); } else { EcmaRuntimeCallInfo *callInfo = EcmaInterpreter::NewRuntimeCallInfo(thread, throwResult, syncIterator, undefinedValue, 1); + RETURN_REJECT_PROMISE_IF_ABRUPT(thread, throwResult, pcap); callInfo->SetCallArg(value.GetTaggedValue()); ret = JSFunction::Call(callInfo); } @@ -148,6 +152,7 @@ JSTaggedValue BuiltinsAsyncFromSyncIterator::Return(EcmaRuntimeCallInfo *argv) JSHandle env = vm->GetGlobalEnv(); JSHandle pcap = JSPromise::NewPromiseCapability(thread, JSHandle::Cast(env->GetPromiseFunction())); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 4.Let syncIterator be O.[[SyncIteratorRecord]].[[Iterator]]. JSHandle asyncIterator(thisValue); JSHandle syncIteratorRecord(thread, asyncIterator->GetSyncIteratorRecord()); @@ -174,10 +179,12 @@ JSTaggedValue BuiltinsAsyncFromSyncIterator::Return(EcmaRuntimeCallInfo *argv) if (value->IsNull()) { EcmaRuntimeCallInfo *callInfo = EcmaInterpreter::NewRuntimeCallInfo(thread, returnResult, syncIterator, undefinedValue, 0); + RETURN_REJECT_PROMISE_IF_ABRUPT(thread, returnResult, pcap); ret = JSFunction::Call(callInfo); } else { EcmaRuntimeCallInfo *callInfo = EcmaInterpreter::NewRuntimeCallInfo(thread, returnResult, syncIterator, undefinedValue, 1); + RETURN_REJECT_PROMISE_IF_ABRUPT(thread, returnResult, pcap); callInfo->SetCallArg(value.GetTaggedValue()); ret = JSFunction::Call(callInfo); } diff --git a/ecmascript/builtins/builtins_async_iterator.cpp b/ecmascript/builtins/builtins_async_iterator.cpp index a71b187534ef30494a7fedd02de5c743e7cef679..f4f31c4e1728779113489392464fceee83402f74 100644 --- a/ecmascript/builtins/builtins_async_iterator.cpp +++ b/ecmascript/builtins/builtins_async_iterator.cpp @@ -48,13 +48,16 @@ JSTaggedValue BuiltinsAsyncIterator::Return(EcmaRuntimeCallInfo *argv) JSHandle promiseFunc = env->GetPromiseFunction(); JSHandle value = GetCallArg(argv, 0); JSHandle pcap = JSPromise::NewPromiseCapability(thread, promiseFunc); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle iterResult = JSIterator::CreateIterResultObject(thread, value, true); JSHandle iterResultVal(iterResult); JSHandle resolve(thread, pcap->GetResolve()); JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); EcmaRuntimeCallInfo* info = EcmaInterpreter::NewRuntimeCallInfo(thread, resolve, undefined, undefined, 1); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); info->SetCallArg(iterResultVal.GetTaggedValue()); JSFunction::Call(info); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return pcap->GetPromise(); } diff --git a/ecmascript/builtins/builtins_atomics.cpp b/ecmascript/builtins/builtins_atomics.cpp index b0e43f65f806cc0d27a3b62fd2650c2e826b8559..6d0e39240b12e40e5551eb6dcebacca95dcb65cf 100644 --- a/ecmascript/builtins/builtins_atomics.cpp +++ b/ecmascript/builtins/builtins_atomics.cpp @@ -183,6 +183,9 @@ JSTaggedValue BuiltinsAtomics::Wait(EcmaRuntimeCallInfo *argv) // 5. Otherwise, let v be ? ToInt32(value). int64_t v = 0; if (array->IsJSBigInt64Array()) { + if (value->IsBoolean()) { + value = JSHandle(thread, JSTaggedValue::ToBigInt64(thread, value)); + } v = JSHandle::Cast(value)->ToInt64(); } else { v = static_cast(JSTaggedValue::ToInt32(thread, value)); @@ -469,6 +472,7 @@ JSTaggedValue BuiltinsAtomics::HandleWithBigInt64(JSThread *thread, uint32_t siz int64_t val = 0; bool lossless = true; BigInt::BigIntToInt64(thread, value, &val, &lossless); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); if (size == 3) { // the number of parameters is 3 auto result = op(reinterpret_cast(block + indexedPosition), &val); return BigInt::Int64ToBigInt(thread, result).GetTaggedValue(); @@ -476,6 +480,7 @@ JSTaggedValue BuiltinsAtomics::HandleWithBigInt64(JSThread *thread, uint32_t siz JSHandle newValue = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::FOURTH); int64_t newVal = 0; BigInt::BigIntToInt64(thread, newValue, &newVal, &lossless); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); int64_t arg[ARGS_NUMBER] = {0}; arg[0] = val; arg[1] = newVal; diff --git a/ecmascript/builtins/builtins_atomics.h b/ecmascript/builtins/builtins_atomics.h index 3645a7b4d6444a1f0745e4b25e83807122d0fb87..943990edcfd058eea1c96061d17fee62823bcbaf 100644 --- a/ecmascript/builtins/builtins_atomics.h +++ b/ecmascript/builtins/builtins_atomics.h @@ -20,6 +20,38 @@ #include "ecmascript/js_dataview.h" #include "ecmascript/waiter_list.h" +// List of functions in Atomics. +// V(name, func, length, stubIndex) +// where BuiltinsAtomics::func refers to the native implementation of Atomics[name]. +// kungfu::BuiltinsStubCSigns::stubIndex refers to the builtin stub index, or INVALID if no stub available. +// The following functions are not implemented yet: +// - Atomics.waitAsync ( typedArray, index, value, timeout ) +#define BUILTIN_ATOMICS_FUNCTIONS(V) \ + /* Atomics.add ( typedArray, index, value ) */ \ + V("add", Add, 3, INVALID) \ + /* Atomics.and ( typedArray, index, value ) */ \ + V("and", And, 3, INVALID) \ + /* Atomics.compareExchange ( typedArray, index, expectedValue, replacementValue ) */ \ + V("compareExchange", CompareExchange, 4, INVALID) \ + /* Atomics.exchange ( typedArray, index, value ) */ \ + V("exchange", Exchange, 3, INVALID) \ + /* Atomics.isLockFree ( size ) */ \ + V("isLockFree", IsLockFree, 1, INVALID) \ + /* Atomics.load ( typedArray, index ) */ \ + V("load", Load, 2, INVALID) \ + /* Atomics.notify ( typedArray, index, count ) */ \ + V("notify", Notify, 3, INVALID) \ + /* Atomics.or ( typedArray, index, value ) */ \ + V("or", Or, 3, INVALID) \ + /* Atomics.store ( typedArray, index, value ) */ \ + V("store", Store, 3, INVALID) \ + /* Atomics.sub ( typedArray, index, value ) */ \ + V("sub", Sub, 3, INVALID) \ + /* Atomics.wait ( typedArray, index, value, timeout ) */ \ + V("wait", Wait, 4, INVALID) \ + /* Atomics.xor ( typedArray, index, value ) */ \ + V("xor", Xor, 3, INVALID) + namespace panda::ecmascript::builtins { enum class WaitResult: uint8_t {OK = 0, NOT_EQ, TIME_OUT}; @@ -50,7 +82,20 @@ public: // 25.4.13 Atomics.xor ( typedArray, index, value ) static JSTaggedValue Xor(EcmaRuntimeCallInfo *argv); + static Span GetAtomicsFunctions() + { + return Span(ATOMICS_FUNCTIONS); + } + private: +#define BUILTINS_ATOMICS_FUNCTION_ENTRY(name, method, length, id) \ + base::BuiltinFunctionEntry::Create(name, BuiltinsAtomics::method, length, kungfu::BuiltinsStubCSigns::id), + + static constexpr std::array ATOMICS_FUNCTIONS = { + BUILTIN_ATOMICS_FUNCTIONS(BUILTINS_ATOMICS_FUNCTION_ENTRY) + }; +#undef BUILTINS_ATOMICS_FUNCTION_ENTRY + static uint32_t Signal(JSHandle &arrayBuffer, const size_t &index, double wakeCount); template static WaitResult DoWait(JSThread *thread, JSHandle &arrayBuffer, @@ -91,4 +136,4 @@ private: static constexpr int ARGS_NUMBER = 2; }; } // namespace panda::ecmascript::builtins -#endif // ECMASCRIPT_BUILTINS_BUILTINS_MATH_H \ No newline at end of file +#endif // ECMASCRIPT_BUILTINS_BUILTINS_MATH_H diff --git a/ecmascript/builtins/builtins_cjs_module.cpp b/ecmascript/builtins/builtins_cjs_module.cpp index 0cb9085ebb87a8203af45b6bbe7c98970178aad5..043bbb00a0c5e18214204dd345bdfee33cb43fde 100644 --- a/ecmascript/builtins/builtins_cjs_module.cpp +++ b/ecmascript/builtins/builtins_cjs_module.cpp @@ -16,14 +16,14 @@ #include "ecmascript/builtins/builtins_cjs_module.h" #include "ecmascript/base/builtins_base.h" -#include "ecmascript/base/path_helper.h" #include "ecmascript/interpreter/interpreter-inl.h" +#include "ecmascript/module/module_path_helper.h" #include "ecmascript/platform/file.h" #include "ecmascript/require/js_cjs_module.h" #include "ecmascript/require/js_require_manager.h" namespace panda::ecmascript::builtins { -using PathHelper = base::PathHelper; + JSTaggedValue BuiltinsCjsModule::CjsModuleConstructor(EcmaRuntimeCallInfo *argv) { JSThread *thread = argv->GetThread(); @@ -63,7 +63,7 @@ JSTaggedValue BuiltinsCjsModule::ResolveFilename(EcmaRuntimeCallInfo *argv) JSMutableHandle parent(thread, JSTaggedValue::Undefined()); JSMutableHandle dirname(thread, JSTaggedValue::Undefined()); const JSPandaFile *jsPandaFile = EcmaInterpreter::GetNativeCallPandafile(thread); - PathHelper::ResolveCurrentPath(thread, parent, dirname, jsPandaFile); + ModulePathHelper::ResolveCurrentPath(thread, parent, dirname, jsPandaFile); if (length != 1) { // strange arg's number LOG_ECMA(FATAL) << "BuiltinsCjsModule::Load : can only accept one argument"; diff --git a/ecmascript/builtins/builtins_cjs_require.cpp b/ecmascript/builtins/builtins_cjs_require.cpp index 9e109db9df3955e2f768063256b1537af2ea72bc..c9e002e982e338b27a7d4b9ff994b5a83fa9a703 100644 --- a/ecmascript/builtins/builtins_cjs_require.cpp +++ b/ecmascript/builtins/builtins_cjs_require.cpp @@ -43,6 +43,7 @@ JSTaggedValue BuiltinsCjsRequire::CjsRequireConstructor(EcmaRuntimeCallInfo *arg } JSHandle requestName = JSHandle::Cast(GetCallArg(argv, 0)); result = CjsModule::Load(thread, requestName); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return result.GetTaggedValue(); } diff --git a/ecmascript/builtins/builtins_dataview.cpp b/ecmascript/builtins/builtins_dataview.cpp index a99ba165982e12cb963c620d4e34aa2ef5735b23..eb356e66245730798c39984178466e40f470cee3 100644 --- a/ecmascript/builtins/builtins_dataview.cpp +++ b/ecmascript/builtins/builtins_dataview.cpp @@ -347,7 +347,8 @@ JSTaggedValue BuiltinsDataView::SetBigUint64(EcmaRuntimeCallInfo *argv) // 24.2.1.1 JSTaggedValue BuiltinsDataView::GetViewValue(JSThread *thread, const JSHandle &view, - const JSHandle &requestIndex, JSTaggedValue littleEndian, + const JSHandle &requestIndex, + const JSHandle &littleEndian, DataViewType type) { BUILTINS_API_TRACE(thread, DataView, GetViewValue); @@ -371,10 +372,10 @@ JSTaggedValue BuiltinsDataView::GetViewValue(JSThread *thread, const JSHandle(indexInt); // 7. Let isLittleEndian be ToBoolean(isLittleEndian). bool isLittleEndian = false; - if (littleEndian.IsUndefined()) { + if (littleEndian->IsUndefined()) { isLittleEndian = false; } else { - isLittleEndian = littleEndian.ToBoolean(); + isLittleEndian = littleEndian->ToBoolean(); } // 8. Let buffer be the value of view’s [[ViewedArrayBuffer]] internal slot. JSHandle dataView(view); @@ -401,7 +402,8 @@ JSTaggedValue BuiltinsDataView::GetViewValue(JSThread *thread, const JSHandle &view, - const JSHandle &requestIndex, JSTaggedValue littleEndian, + const JSHandle &requestIndex, + const JSHandle &littleEndian, DataViewType type, const JSHandle &value) { // 1. If Type(view) is not Object, throw a TypeError exception. @@ -426,10 +428,10 @@ JSTaggedValue BuiltinsDataView::SetViewValue(JSThread *thread, const JSHandleIsUndefined()) { isLittleEndian = false; } else { - isLittleEndian = littleEndian.ToBoolean(); + isLittleEndian = littleEndian->ToBoolean(); } // 8. Let buffer be the value of view’s [[ViewedArrayBuffer]] internal slot. JSHandle dataView(view); @@ -461,11 +463,12 @@ JSTaggedValue BuiltinsDataView::GetTypedValue(EcmaRuntimeCallInfo *argv, DataVie [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle thisHandle = GetThis(argv); JSHandle offsetHandle = GetCallArg(argv, 0); + JSHandle trueHandle(thread, JSTaggedValue::True()); if (type == DataViewType::UINT8 || type == DataViewType::INT8) { - return GetViewValue(thread, thisHandle, offsetHandle, JSTaggedValue::True(), type); + return GetViewValue(thread, thisHandle, offsetHandle, trueHandle, type); } JSHandle littleEndianHandle = GetCallArg(argv, 1); - return GetViewValue(thread, thisHandle, offsetHandle, littleEndianHandle.GetTaggedValue(), type); + return GetViewValue(thread, thisHandle, offsetHandle, littleEndianHandle, type); } JSTaggedValue BuiltinsDataView::SetTypedValue(EcmaRuntimeCallInfo *argv, DataViewType type) @@ -476,10 +479,11 @@ JSTaggedValue BuiltinsDataView::SetTypedValue(EcmaRuntimeCallInfo *argv, DataVie JSHandle thisHandle = GetThis(argv); JSHandle offsetHandle = GetCallArg(argv, 0); JSHandle value = GetCallArg(argv, 1); + JSHandle trueHandle(thread, JSTaggedValue::True()); if (type == DataViewType::UINT8 || type == DataViewType::INT8) { - return SetViewValue(thread, thisHandle, offsetHandle, JSTaggedValue::True(), type, value); + return SetViewValue(thread, thisHandle, offsetHandle, trueHandle, type, value); } JSHandle littleEndianHandle = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); - return SetViewValue(thread, thisHandle, offsetHandle, littleEndianHandle.GetTaggedValue(), type, value); + return SetViewValue(thread, thisHandle, offsetHandle, littleEndianHandle, type, value); } } // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_dataview.h b/ecmascript/builtins/builtins_dataview.h index 4337c2b0bf3bb21805b4f8377da6044a324041b8..e2eadc6563ad36f1d95dabac442eebd2d3fbf875 100644 --- a/ecmascript/builtins/builtins_dataview.h +++ b/ecmascript/builtins/builtins_dataview.h @@ -19,6 +19,40 @@ #include "ecmascript/base/builtins_base.h" #include "ecmascript/js_dataview.h" +// List of functions in DataView, excluding the constructor and '@@' properties. +// V(name, func, length, stubIndex) +// where BuiltinsDataView::func refers to the native implementation of DataView.prototype[name]. +// kungfu::BuiltinsStubCSigns::stubIndex refers to the builtin stub index, or INVALID if no stub available. +#define BUILTIN_DATA_VIEW_PROTOTYPE_FUNCTIONS(V) \ + /* For %Type% of 1 byte: */ \ + /* DataView.prototype.get%Type% ( byteOffset ) */ \ + /* For %Type% of 2 or more bytes: */ \ + /* DataView.prototype.get%Type% ( byteOffset [ , littleEndian ] ) */ \ + V("getFloat32", GetFloat32, 1, INVALID) \ + V("getFloat64", GetFloat64, 1, INVALID) \ + V("getInt8", GetInt8, 1, INVALID) \ + V("getInt16", GetInt16, 1, INVALID) \ + V("getInt32", GetInt32, 1, INVALID) \ + V("getBigInt64", GetBigInt64, 1, INVALID) \ + V("getUint16", GetUint16, 1, INVALID) \ + V("getUint32", GetUint32, 1, INVALID) \ + V("getUint8", GetUint8, 1, INVALID) \ + V("getBigUint64", GetBigUint64, 1, INVALID) \ + /* For %Type% of 1 bytes: */ \ + /* DataView.prototype.setInt8 ( byteOffset, value ) */ \ + /* For %Type% of 2 or more bytes: */ \ + /* DataView.prototype.setInt16 ( byteOffset, value [ , littleEndian ] ) */ \ + V("setFloat32", SetFloat32, 2, INVALID) \ + V("setFloat64", SetFloat64, 2, INVALID) \ + V("setInt8", SetInt8, 2, INVALID) \ + V("setInt16", SetInt16, 2, INVALID) \ + V("setInt32", SetInt32, 2, INVALID) \ + V("setBigInt64", SetBigInt64, 2, INVALID) \ + V("setUint8", SetUint8, 2, INVALID) \ + V("setUint16", SetUint16, 2, INVALID) \ + V("setUint32", SetUint32, 2, INVALID) \ + V("setBigUint64", SetBigUint64, 2, INVALID) + namespace panda::ecmascript::builtins { using DataViewType = ecmascript::DataViewType; class BuiltinsDataView : public base::BuiltinsBase { @@ -72,13 +106,30 @@ public: // 25.3.4.16 DataView.prototype.setBigUint64 ( byteOffset, value [ , littleEndian ] ) static JSTaggedValue SetBigUint64(EcmaRuntimeCallInfo *argv); + // Excluding the '@@' internal properties. + static Span GetDataViewPrototypeFunctions() + { + return Span(DATA_VIEW_PROTOTYPE_FUNCTIONS); + } + private: +#define BUILTIN_DATA_VIEW_FUNCTION_ENTRY(name, func, length, id) \ + base::BuiltinFunctionEntry::Create(name, BuiltinsDataView::func, length, kungfu::BuiltinsStubCSigns::id), + + static constexpr std::array DATA_VIEW_PROTOTYPE_FUNCTIONS = { + BUILTIN_DATA_VIEW_PROTOTYPE_FUNCTIONS(BUILTIN_DATA_VIEW_FUNCTION_ENTRY) + }; + +#undef BUILTIN_DATA_VIEW_FUNCTION_ENTRY + // 24.2.1.1 GetViewValue ( view, requestIndex, isLittleEndian, type ) static JSTaggedValue GetViewValue(JSThread *thread, const JSHandle &view, - const JSHandle &requestIndex, JSTaggedValue littleEndian, + const JSHandle &requestIndex, + const JSHandle &littleEndian, DataViewType type); static JSTaggedValue SetViewValue(JSThread *thread, const JSHandle &view, - const JSHandle &requestIndex, JSTaggedValue littleEndian, + const JSHandle &requestIndex, + const JSHandle &littleEndian, DataViewType type, const JSHandle &value); static JSTaggedValue GetTypedValue(EcmaRuntimeCallInfo *argv, DataViewType type); diff --git a/ecmascript/builtins/builtins_date.h b/ecmascript/builtins/builtins_date.h index 7d83a3a211d6f59270d662923f0aa74cab1284ba..8ff19aa1126026e20292d0c5895a7bef9bb285ab 100644 --- a/ecmascript/builtins/builtins_date.h +++ b/ecmascript/builtins/builtins_date.h @@ -19,9 +19,114 @@ #include "ecmascript/base/builtins_base.h" #include "ecmascript/js_date.h" +// List of functions in Date, excluding the '@@' properties. +// V(name, func, length, stubIndex) +// where BuiltinsDate::func refers to the native implementation of Date[name]. +// kungfu::BuiltinsStubCSigns::stubIndex refers to the builtin stub index, or INVALID if no stub available. +#define BUILTIN_DATE_FUNCTIONS(V) \ + /* Date.now ( ) */ \ + V("now", Now, 0, INVALID) \ + /* Date.parse ( string ) */ \ + V("parse", Parse, 1, INVALID) \ + /* Date.UTC ( year [ , month [ , date [ , hours [ , minutes [ , seconds [ , ms ] ] ] ] ] ] ) */ \ + V("UTC", UTC, ::panda::ecmascript::builtins::BuiltinsDate::UTC_LENGTH, INVALID) + +// List of functions in Date.prototype, excluding the constructor and '@@' properties. +// V(name, func, length, stubIndex) +// where BuiltinsDate::func refers to the native implementation of Date.prototype[name]. +#define BUILTIN_DATE_PROTOTYPE_FUNCTIONS(V) \ + /* Date.prototype.getDate ( ) */ \ + V("getDate", GetDate, 0, INVALID) \ + /* Date.prototype.getDay ( ) */ \ + V("getDay", GetDay, 0, INVALID) \ + /* Date.prototype.getFullYear ( ) */ \ + V("getFullYear", GetFullYear, 0, INVALID) \ + /* Date.prototype.getHours ( ) */ \ + V("getHours", GetHours, 0, INVALID) \ + /* Date.prototype.getMilliseconds ( ) */ \ + V("getMilliseconds", GetMilliseconds, 0, INVALID) \ + /* Date.prototype.getMinutes ( ) */ \ + V("getMinutes", GetMinutes, 0, INVALID) \ + /* Date.prototype.getMonth ( ) */ \ + V("getMonth", GetMonth, 0, INVALID) \ + /* Date.prototype.getSeconds ( ) */ \ + V("getSeconds", GetSeconds, 0, INVALID) \ + /* Date.prototype.getTime ( ) */ \ + V("getTime", GetTime, 0, INVALID) \ + /* Date.prototype.getTimezoneOffset ( ) */ \ + V("getTimezoneOffset", GetTimezoneOffset, 0, INVALID) \ + /* Date.prototype.getUTCDate ( ) */ \ + V("getUTCDate", GetUTCDate, 0, INVALID) \ + /* Date.prototype.getUTCDay ( ) */ \ + V("getUTCDay", GetUTCDay, 0, INVALID) \ + /* Date.prototype.getUTCFullYear ( ) */ \ + V("getUTCFullYear", GetUTCFullYear, 0, INVALID) \ + /* Date.prototype.getUTCHours ( ) */ \ + V("getUTCHours", GetUTCHours, 0, INVALID) \ + /* Date.prototype.getUTCMilliseconds ( ) */ \ + V("getUTCMilliseconds", GetUTCMilliseconds, 0, INVALID) \ + /* Date.prototype.getUTCMinutes ( ) */ \ + V("getUTCMinutes", GetUTCMinutes, 0, INVALID) \ + /* Date.prototype.getUTCMonth ( ) */ \ + V("getUTCMonth", GetUTCMonth, 0, INVALID) \ + /* Date.prototype.getUTCSeconds ( ) */ \ + V("getUTCSeconds", GetUTCSeconds, 0, INVALID) \ + /* Date.prototype.setDate ( date ) */ \ + V("setDate", SetDate, 1, INVALID) \ + /* Date.prototype.setFullYear ( year [ , month [ , date ] ] ) */ \ + V("setFullYear", SetFullYear, 3, INVALID) \ + /* Date.prototype.setHours ( hour [ , min [ , sec [ , ms ] ] ] ) */ \ + V("setHours", SetHours, 4, INVALID) \ + /* Date.prototype.setMilliseconds ( ms ) */ \ + V("setMilliseconds", SetMilliseconds, 1, INVALID) \ + /* Date.prototype.setMinutes ( min [ , sec [ , ms ] ] ) */ \ + V("setMinutes", SetMinutes, 3, INVALID) \ + /* Date.prototype.setMonth ( month [ , date ] ) */ \ + V("setMonth", SetMonth, 2, INVALID) \ + /* Date.prototype.setSeconds ( sec [ , ms ] ) */ \ + V("setSeconds", SetSeconds, 2, INVALID) \ + /* Date.prototype.setTime ( time ) */ \ + V("setTime", SetTime, 1, INVALID) \ + /* Date.prototype.setUTCDate ( date ) */ \ + V("setUTCDate", SetUTCDate, 1, INVALID) \ + /* Date.prototype.setUTCFullYear ( year [ , month [ , date ] ] ) */ \ + V("setUTCFullYear", SetUTCFullYear, 3, INVALID) \ + /* Date.prototype.setUTCHours ( hour [ , min [ , sec [ , ms ] ] ] ) */ \ + V("setUTCHours", SetUTCHours, 4, INVALID) \ + /* Date.prototype.setUTCMilliseconds ( ms ) */ \ + V("setUTCMilliseconds", SetUTCMilliseconds, 1, INVALID) \ + /* Date.prototype.setUTCMinutes ( min [ , sec [ , ms ] ] ) */ \ + V("setUTCMinutes", SetUTCMinutes, 3, INVALID) \ + /* Date.prototype.setUTCMonth ( month [ , date ] ) */ \ + V("setUTCMonth", SetUTCMonth, 2, INVALID) \ + /* Date.prototype.setUTCSeconds ( sec [ , ms ] ) */ \ + V("setUTCSeconds", SetUTCSeconds, 2, INVALID) \ + /* Date.prototype.toDateString ( ) */ \ + V("toDateString", ToDateString, 0, INVALID) \ + /* Date.prototype.toISOString ( ) */ \ + V("toISOString", ToISOString, 0, INVALID) \ + /* Date.prototype.toJSON ( key ) */ \ + V("toJSON", ToJSON, 1, INVALID) \ + /* Date.prototype.toLocaleDateString ( [ reserved1 [ , reserved2 ] ] ) */ \ + V("toLocaleDateString", ToLocaleDateString, 0, INVALID) \ + /* Date.prototype.toLocaleString ( [ reserved1 [ , reserved2 ] ] ) */ \ + V("toLocaleString", ToLocaleString, 0, INVALID) \ + /* Date.prototype.toLocaleTimeString ( [ reserved1 [ , reserved2 ] ] ) */ \ + V("toLocaleTimeString", ToLocaleTimeString, 0, INVALID) \ + /* Date.prototype.toString ( ) */ \ + V("toString", ToString, 0, INVALID) \ + /* Date.prototype.toTimeString ( ) */ \ + V("toTimeString", ToTimeString, 0, INVALID) \ + /* Date.prototype.toUTCString ( ) */ \ + V("toUTCString", ToUTCString, 0, INVALID) \ + /* Date.prototype.valueOf ( ) */ \ + V("valueOf", ValueOf, 0, INVALID) + namespace panda::ecmascript::builtins { class BuiltinsDate : public base::BuiltinsBase { public: + static constexpr int UTC_LENGTH = 7; + // 20.4.2 The Date Constructor static JSTaggedValue DateConstructor(EcmaRuntimeCallInfo *argv); @@ -165,7 +270,30 @@ public: // 20.4.4.45 Date.prototype [ @@toPrimitive ] static JSTaggedValue ToPrimitive(EcmaRuntimeCallInfo *argv); + // Excluding the '@@' internal properties + static Span GetDateFunctions() + { + return Span(DATE_FUNCTIONS); + } + + // Excluding the constructor and '@@' internal properties. + static Span GetDatePrototypeFunctions() + { + return Span(DATE_PROTOTYPE_FUNCTIONS); + } + private: +#define BUILTIN_DATE_FUNCTION_ENTRY(name, func, length, builtinId) \ + base::BuiltinFunctionEntry::Create(name, BuiltinsDate::func, length, kungfu::BuiltinsStubCSigns::builtinId), + + static constexpr std::array DATE_FUNCTIONS = { + BUILTIN_DATE_FUNCTIONS(BUILTIN_DATE_FUNCTION_ENTRY) + }; + static constexpr std::array DATE_PROTOTYPE_FUNCTIONS = { + BUILTIN_DATE_PROTOTYPE_FUNCTIONS(BUILTIN_DATE_FUNCTION_ENTRY) + }; +#undef BUILTIN_DATE_FUNCTION_ENTRY + // definition for set data code. static constexpr uint32_t CODE_SET_DATE = 0x32; static constexpr uint32_t CODE_SET_MILLISECONDS = 0x76; diff --git a/ecmascript/builtins/builtins_displaynames.cpp b/ecmascript/builtins/builtins_displaynames.cpp index 3cac9430bcc6520550635cc8f3428cf1447dbda4..02c853165e8814fafc4f5aa95b184d379e652bda 100644 --- a/ecmascript/builtins/builtins_displaynames.cpp +++ b/ecmascript/builtins/builtins_displaynames.cpp @@ -102,6 +102,7 @@ JSTaggedValue BuiltinsDisplayNames::Of(EcmaRuntimeCallInfo *argv) JSHandle displayNames = JSHandle::Cast(thisValue); TypednsOption typeOpt = displayNames->GetType(); JSHandle code = JSDisplayNames::CanonicalCodeForDisplayNames(thread, displayNames, typeOpt, codeTemp); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); std::string codeString = intl::LocaleHelper::ConvertToStdString(code); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); if (codeString.size()) { diff --git a/ecmascript/builtins/builtins_errors.cpp b/ecmascript/builtins/builtins_errors.cpp index 8af25081c31d7043bb8e9ff72cd39d595e6fd0eb..341e3f61ac9c8ec1a560f5c3f00a15c8e7aa98f4 100644 --- a/ecmascript/builtins/builtins_errors.cpp +++ b/ecmascript/builtins/builtins_errors.cpp @@ -159,6 +159,22 @@ JSTaggedValue BuiltinsAggregateError::AggregateErrorConstructor(EcmaRuntimeCallI PropertyDescriptor msgDesc(thread, JSHandle::Cast(handleStr), true, false, true); JSTaggedValue::DefinePropertyOrThrow(thread, taggedObj, msgKey, msgDesc); } + // InstallErrorCause + JSHandle options = BuiltinsBase::GetCallArg(argv, 2); // 2 : Third parameter + // 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); + JSTaggedValue::DefinePropertyOrThrow(thread, taggedObj, causeKey, causeDesc); + } + } // 4. Let errorsList be ? IterableToList(errors). JSHandle errorsList = JSObject::IterableToList(thread, errors); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -168,6 +184,7 @@ JSTaggedValue BuiltinsAggregateError::AggregateErrorConstructor(EcmaRuntimeCallI JSHandle errorsValues(JSArray::CreateArrayFromList(thread, errorsArray)); PropertyDescriptor msgDesc(thread, errorsValues, true, false, true); JSTaggedValue::DefinePropertyOrThrow(thread, taggedObj, errorsKey, msgDesc); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 6. Return O. return taggedObj.GetTaggedValue(); } diff --git a/ecmascript/builtins/builtins_finalization_registry.cpp b/ecmascript/builtins/builtins_finalization_registry.cpp index 60c8cd9acd5af1d7bacbb42f5dcef8109e58a114..ed87c399477a9e2e2a307563b16f0d612424ac46 100644 --- a/ecmascript/builtins/builtins_finalization_registry.cpp +++ b/ecmascript/builtins/builtins_finalization_registry.cpp @@ -75,19 +75,19 @@ JSTaggedValue BuiltinsFinalizationRegistry::Register(EcmaRuntimeCallInfo *argv) THROW_TYPE_ERROR_AND_RETURN(thread, "thisValue is not object or does not have an internalSlot internal slot", JSTaggedValue::Exception()); } - // 3. If Type(target) is not Object, throw a TypeError exception. - if (!target->IsECMAObject()) { - THROW_TYPE_ERROR_AND_RETURN(thread, "target is not object", JSTaggedValue::Exception()); + // 3. If CanBeHeldWeakly(target) is false, throw a TypeError exception. + if (!JSTaggedValue::CanBeHeldWeakly(thread, target)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "target invalid", JSTaggedValue::Exception()); } // 4. If SameValue(target, heldValue) is true, throw a TypeError exception. if (JSTaggedValue::SameValue(target, heldValue)) { THROW_TYPE_ERROR_AND_RETURN(thread, "target and heldValue should not be equal", JSTaggedValue::Exception()); } - // 5. If Type(unregisterToken) is not Object, then + // 5. If CanBeHeldWeakly(unregisterToken) is false, then // a. If unregisterToken is not undefined, throw a TypeError exception. // b. Set unregisterToken to empty. - if (!unregisterToken->IsECMAObject() && !unregisterToken->IsUndefined()) { - THROW_TYPE_ERROR_AND_RETURN(thread, "unregisterToken should be object", JSTaggedValue::Exception()); + if (!JSTaggedValue::CanBeHeldWeakly(thread, unregisterToken) && !unregisterToken->IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "unregisterToken invalid", JSTaggedValue::Exception()); } // 6. Let cell be the Record { [[WeakRefTarget]]: target, // [[HeldValue]]: heldValue, [[UnregisterToken]]: unregisterToken }. @@ -112,9 +112,9 @@ JSTaggedValue BuiltinsFinalizationRegistry::Unregister(EcmaRuntimeCallInfo *argv THROW_TYPE_ERROR_AND_RETURN(thread, "thisValue is not object or does not have an internalSlot internal slot", JSTaggedValue::Exception()); } - // 3. If Type(unregisterToken) is not Object, throw a TypeError exception. - if (!unregisterToken->IsECMAObject()) { - THROW_TYPE_ERROR_AND_RETURN(thread, "unregisterToken should be object", JSTaggedValue::Exception()); + // 3. If CanBeHeldWeakly(unregisterToken) is false, throw a TypeError exception. + if (!JSTaggedValue::CanBeHeldWeakly(thread, unregisterToken)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "unregisterToken invalid", JSTaggedValue::Exception()); } // 4. Let removed be false. // 5. For each Record { [[WeakRefTarget]], [[HeldValue]], [[UnregisterToken]] } cell of diff --git a/ecmascript/builtins/builtins_function.cpp b/ecmascript/builtins/builtins_function.cpp index becda3bf9182733078023a2e202d5ab3c8d74555..f1f0e01c3809e3cc503e7de990c2e941ea36569b 100644 --- a/ecmascript/builtins/builtins_function.cpp +++ b/ecmascript/builtins/builtins_function.cpp @@ -121,18 +121,19 @@ JSTaggedValue BuiltinsFunction::FunctionPrototypeApply(EcmaRuntimeCallInfo *argv JSHandle arrayObj = GetCallArg(argv, 1); std::pair argumentsList = BuildArgumentsListFast(thread, arrayObj); if (!argumentsList.first) { - JSHandle argList = JSHandle::Cast( - JSObject::CreateListFromArrayLike(thread, arrayObj)); + JSHandle num = JSObject::CreateListFromArrayLike(thread, arrayObj); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle argList = JSHandle::Cast(num); // 4. ReturnIfAbrupt(argList). RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - const int32_t argsLength = static_cast(argList->GetLength()); + const uint32_t argsLength = argList->GetLength(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, thisArg, undefined, argsLength); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); info->SetCallArg(argsLength, argList); return JSFunction::Call(info); } // 6. Return Call(func, thisArg, argList). - const int32_t argsLength = static_cast(argumentsList.second); + const uint32_t argsLength = static_cast(argumentsList.second); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, thisArg, undefined, argsLength); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); info->SetCallArg(argsLength, argumentsList.first); @@ -206,6 +207,7 @@ JSTaggedValue BuiltinsFunction::FunctionPrototypeBind(EcmaRuntimeCallInfo *argv) PropertyDescriptor desc(thread, JSHandle(thread, JSTaggedValue(lengthValue)), false, false, true); [[maybe_unused]] bool status = JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(boundFunction), lengthKey, desc); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 11. Assert: status is not an abrupt completion. ASSERT_PRINT(status, "DefinePropertyOrThrow failed"); diff --git a/ecmascript/builtins/builtins_generator.cpp b/ecmascript/builtins/builtins_generator.cpp index 30850bb61e9b04529e74b09924bd40b20985c8f6..4d2e1a152293c65d81e7c9101a65b717f6803450 100644 --- a/ecmascript/builtins/builtins_generator.cpp +++ b/ecmascript/builtins/builtins_generator.cpp @@ -40,6 +40,7 @@ JSTaggedValue BuiltinsGenerator::GeneratorPrototypeNext(EcmaRuntimeCallInfo *arg THROW_TYPE_ERROR_AND_RETURN(thread, "Not a generator object.", JSTaggedValue::Exception()); } JSHandle generator(thread, JSGeneratorObject::Cast(*JSTaggedValue::ToObject(thread, msg))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle value = GetCallArg(argv, 0); // 2.Return ? GeneratorResume(g, value). @@ -60,7 +61,7 @@ JSTaggedValue BuiltinsGenerator::GeneratorPrototypeReturn(EcmaRuntimeCallInfo *a THROW_TYPE_ERROR_AND_RETURN(thread, "Not a generator object.", JSTaggedValue::Exception()); } JSHandle generator(thread, JSGeneratorObject::Cast(*JSTaggedValue::ToObject(thread, msg))); - + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 2.Let C be Completion { [[Type]]: return, [[Value]]: value, [[Target]]: empty }. JSHandle value = GetCallArg(argv, 0); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); @@ -85,7 +86,7 @@ JSTaggedValue BuiltinsGenerator::GeneratorPrototypeThrow(EcmaRuntimeCallInfo *ar THROW_TYPE_ERROR_AND_RETURN(thread, "Not a generator object.", JSTaggedValue::Exception()); } JSHandle generator(thread, JSGeneratorObject::Cast(*JSTaggedValue::ToObject(thread, msg))); - + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 2.Let C be ThrowCompletion(exception). JSHandle exception = GetCallArg(argv, 0); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); diff --git a/ecmascript/builtins/builtins_global.cpp b/ecmascript/builtins/builtins_global.cpp index 2534ef4c4d0fb4e91bb234dcf4a79c8bb6abf817..918631474f6af660f3b2baf893b4b0520c3ed3f2 100644 --- a/ecmascript/builtins/builtins_global.cpp +++ b/ecmascript/builtins/builtins_global.cpp @@ -25,12 +25,18 @@ #include "ecmascript/ecma_macros.h" #include "ecmascript/js_function.h" #include "ecmascript/mem/c_containers.h" +#include "ecmascript/module/js_module_deregister.h" #include "ecmascript/stubs/runtime_stubs.h" #include "ecmascript/tagged_array-inl.h" namespace panda::ecmascript::builtins { using NumberHelper = base::NumberHelper; using StringHelper = base::StringHelper; +std::u16string g_asciiWordChars(u"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"); +std::u16string g_escapeWordChars(u"@*+-./"); +constexpr std::uint16_t CHAR16_PERCENT_SIGN = 0x0025; // u'%' +constexpr std::uint16_t CHAR16_LATIN_SMALL_LETTER_U = 0x0075; // u'u'; +constexpr std::uint16_t CHAR16_LETTER_NULL = u'\0'; // 18.2.1 JSTaggedValue BuiltinsGlobal::NotSupportEval(EcmaRuntimeCallInfo *msg) @@ -289,6 +295,126 @@ uint8_t BuiltinsGlobal::GetValueFromTwoHex(uint16_t front, uint16_t behind) return res; } +uint16_t BuiltinsGlobal::GetValueFromHexString(const JSHandle &string) +{ + uint32_t size = EcmaStringAccessor(string).GetLength(); + ASSERT(size > 0 && size <= 4); // NOLINT 4: means 4 hex digits + std::u16string hexString(u"0123456789ABCDEF"); + + uint16_t ret = 0; + for (uint32_t i = 0; i < size; ++i) { + uint16_t ch = EcmaStringAccessor(string).Get(i); + size_t idx = StringHelper::FindFromU16ToUpper(hexString, &ch); + ret = ((ret << 4U) | idx) & BIT_MASK_4F; // NOLINT 4: means shift left by 4 + } + return ret; +} + +// 22.1.3.17.2 StringPad ( S, maxLength, fillString, placement ) +EcmaString *BuiltinsGlobal::StringPad(JSThread *thread, const JSHandle &source, + uint32_t maxLength, const JSHandle &fillString, + Placement placement) +{ + // 1. Let stringLength be the length of S. + uint32_t stringLength = EcmaStringAccessor(source).GetLength(); + // 2. If maxLength ≤ stringLength, return S. + if (maxLength <= stringLength) { + return *source; + } + // 3. If fillString is the empty String, return S. + uint32_t targetStrLen = EcmaStringAccessor(fillString).GetLength(); + if (targetStrLen == 0) { + return *source; + } + // 4. Let fillLen be maxLength - stringLength. + uint32_t fillLen = maxLength - stringLength; + EcmaVM *vm = thread->GetEcmaVM(); + //5. Let truncatedStringFiller be the String value consisting of repeated concatenations + // of fillString truncated to length fillLen. + uint32_t repeatTimes = std::ceil(fillLen / targetStrLen); + EcmaString *p = nullptr; + JSHandle stringFiller = vm->GetFactory()->NewFromStdString(std::string("\0")); + for (uint32_t k = 0; k < repeatTimes; ++k) { + p = EcmaStringAccessor::Concat(vm, stringFiller, fillString); + stringFiller = JSHandle(thread, p); + } + JSHandle truncatedStringFiller(thread, + EcmaStringAccessor::FastSubString(vm, stringFiller, 0, fillLen)); + // 6. If placement is start, return the string-concatenation of truncatedStringFiller and S. + // 7. Else, return the string-concatenation of S and truncatedStringFiller. + if (placement == Placement::START) { + return EcmaStringAccessor::Concat(vm, truncatedStringFiller, source); + } else { + return EcmaStringAccessor::Concat(vm, source, truncatedStringFiller); + } +} + +// Static Semantics: UTF16SurrogatePairToCodePoint ( lead, trail ) +uint16_t BuiltinsGlobal::UTF16SurrogatePairToCodePoint(uint16_t lead, uint16_t trail) +{ + // 1. Assert: lead is a leading surrogate and trail is a trailing surrogate. + ASSERT(IsUTF16HighSurrogate(lead) && IsUTF16LowSurrogate(trail)); + // 2. Let cp be (lead - 0xD800) × 0x400 + (trail - 0xDC00) + 0x10000. + uint16_t cp = ((lead - 0xD800) << 10UL) + (trail - 0xDC00) + 0x10000; + // 3. Return the code point cp. + return cp; +} + +// 11.1.5 Static Semantics: StringToCodePoints ( string ) +EcmaString *BuiltinsGlobal::StringToCodePoints(JSThread *thread, const JSHandle &string) +{ + // 1. Let codePoints be a new empty List. + std::u16string codePoints; + // 2. Let size be the length of string. + uint32_t size = EcmaStringAccessor(string).GetLength(); + // 3. Let position be 0. + uint32_t position = 0; + // 4. Repeat, while position < size, + // a. Let cp be CodePointAt(string, position). + // b. Append cp.[[CodePoint]] to codePoints. + // c. Set position to position + cp.[[CodeUnitCount]]. + while (position < size) { + // i.Let first be the code unit at index position within string. + uint16_t first = EcmaStringAccessor(string).Get(position); + uint16_t cp = first - CHAR16_LETTER_NULL; + uint8_t codeUnitCount = 0; + bool isUnpairedSurrogate = false; + // ii. If first is neither a leading surrogate nor a trailing surrogate, then + // a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: false }. + if (!IsUTF16HighSurrogate(first) && !IsUTF16LowSurrogate(first)) { + codeUnitCount = 1; // 1 means: code unit count + isUnpairedSurrogate = false; + } else if (IsUTF16HighSurrogate(first) || position + 1 == size) { + // iii. If first is a trailing surrogate or position + 1 = size, then + // a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }. + codeUnitCount = 1; + isUnpairedSurrogate = true; + } else { + // iv. Let second be the code unit at index position + 1 within string. + uint16_t second = EcmaStringAccessor(string).Get(position + 1); + // v. If second is not a trailing surrogate, then + // a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }. + if (!IsUTF16LowSurrogate(second)) { + codeUnitCount = 1; // 1 means: code unit count + isUnpairedSurrogate = true; + } else { + // vi. Set cp to UTF16SurrogatePairToCodePoint(first, second). + // vii. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 2, [[IsUnpairedSurrogate]]: false }. + cp = UTF16SurrogatePairToCodePoint(first, second); + codeUnitCount = 2; // 2 means: code unit count + isUnpairedSurrogate = false; + } + } + codePoints.push_back(cp); + position = position + codeUnitCount; + } + // 5. Return codePoints. + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + uint16_t *ptr = reinterpret_cast(codePoints.data()); + JSHandle codePointsString = factory->NewFromUtf16Literal(ptr, codePoints.size()); + return *codePointsString; +} + // Runtime Semantics JSTaggedValue BuiltinsGlobal::Decode(JSThread *thread, const JSHandle &str, judgURIFunc IsInURISet) { @@ -501,6 +627,26 @@ JSTaggedValue BuiltinsGlobal::PrintEntrypoint(EcmaRuntimeCallInfo *msg) return JSTaggedValue::Undefined(); } +JSTaggedValue BuiltinsGlobal::MarkModuleCollectable(EcmaRuntimeCallInfo *msg) +{ + ASSERT(msg); + JSThread *thread = msg->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + uint32_t numArgs = msg->GetArgsNumber(); + if (numArgs != 1) { + LOG_FULL(ERROR) << "The number of parameters received by markModuleCollectable is incorrect."; + return JSTaggedValue::False(); + } + JSHandle module = GetCallArg(msg, 0); + if (!module->IsModuleNamespace()) { + return JSTaggedValue::False(); + } + + ModuleDeregister::ProcessModuleReference(thread, module); + return JSTaggedValue::True(); +} + JSTaggedValue BuiltinsGlobal::CallJsBoundFunction(EcmaRuntimeCallInfo *msg) { JSThread *thread = msg->GetThread(); @@ -574,4 +720,150 @@ JSTaggedValue BuiltinsGlobal::PrintFunctionCallStat(EcmaRuntimeCallInfo *msg) return JSTaggedValue::Undefined(); } #endif + +// B.2.1.1 escape ( string ) +JSTaggedValue BuiltinsGlobal::Escape(EcmaRuntimeCallInfo *msg) +{ + ASSERT(msg); + JSThread *thread = msg->GetThread(); + BUILTINS_API_TRACE(thread, Global, Escape); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Set string to ? ToString(string). + JSHandle string = JSTaggedValue::ToString(thread, GetCallArg(msg, 0)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + EcmaVM *vm = thread->GetEcmaVM(); + // 2. Let len be the length of string. + uint32_t len = EcmaStringAccessor(string).GetLength(); + // 3. Let R be the empty String. + std::u16string r; + // 4. Let unescapedSet be the string-concatenation of the ASCII word characters and "@*+-./". + std::u16string unescapedSet = g_asciiWordChars + g_escapeWordChars; + // 5. Let k be 0. + uint32_t k = 0; + // 6. Repeat, while k < len, + // a. Let C be the code unit at index k within string. + // b. If unescapedSet contains C, then + // i. Let S be C. + // c. Else, + // i. Let n be the numeric value of C. + // ii. If n < 256, then + // 1. Let hex be the String representation of n, formatted as an uppercase hexadecimal number. + // 2. Let S be the string-concatenation of "%" and StringPad(hex, 2, "0", start). + // iii. Else, + // 1. Let hex be the String representation of n, formatted as an uppercase hexadecimal number. + // 2. Let S be the string-concatenation of "%u" and StringPad(hex, 4, "0", start). + // d. Set R to the string-concatenation of R and S. + // e. Set k to k + 1. + while (k < len) { + uint16_t c = EcmaStringAccessor(string).Get(k); + if (unescapedSet.find(c) != std::u16string::npos) { + r.push_back(c); + } else { + uint16_t n = c - CHAR16_LETTER_NULL; + std::ostringstream oss; + oss << std::uppercase << std::hex << n; + JSHandle hex = factory->NewFromStdString(oss.str()); + JSHandle fillString = factory->NewFromStdString(std::string("0")); + EcmaString *temp = nullptr; + JSHandle hexStringHandle = factory->NewFromStdString(std::string("\0")); + if (n <= std::numeric_limits::max()) { + EcmaString *hexEcmaString = + StringPad(thread, hex, 2, fillString, Placement::START); // NOLINT 2: means max string length + hexStringHandle = JSHandle(thread, hexEcmaString); + temp = EcmaStringAccessor::Concat(vm, factory->NewFromStdString("%"), hexStringHandle); + } else { + EcmaString *hexEcmaString = + StringPad(thread, hex, 4, fillString, Placement::START); // NOLINT 4: means max string length + hexStringHandle = JSHandle(thread, hexEcmaString); + temp = EcmaStringAccessor::Concat(vm, factory->NewFromStdString("%u"), hexStringHandle); + } + JSHandle s = JSHandle(thread, temp); + r = r + EcmaStringAccessor(s).ToU16String(); + } + ++k; + } + // 7. Return R. + auto *returnData = reinterpret_cast(r.data()); + uint32_t retSize = r.size(); + return factory->NewFromUtf16Literal(returnData, retSize).GetTaggedValue(); +} + +// B.2.1.2 unescape ( string ) +JSTaggedValue BuiltinsGlobal::Unescape(EcmaRuntimeCallInfo *msg) +{ + ASSERT(msg); + JSThread *thread = msg->GetThread(); + BUILTINS_API_TRACE(thread, Global, Unescape); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + // 1. Set string to ? ToString(string). + JSHandle string = JSTaggedValue::ToString(thread, GetCallArg(msg, 0)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 2. Let len be the length of string. + uint32_t len = EcmaStringAccessor(string).GetLength(); + // 3. Let R be the empty String. + EcmaVM *vm = thread->GetEcmaVM(); + ObjectFactory *factory = vm->GetFactory(); + std::u16string r; + // 4. Let k be 0. + uint32_t k = 0; + // 5. Repeat, while k < len, + // a. Let C be the code unit at index k within string. + // b. If C is the code unit 0x0025 (PERCENT SIGN), then + // i. Let hexDigits be the empty String. + // ii. Let optionalAdvance be 0. + // iii. If k + 5 < len and the code unit at index k + 1 within string is the code unit + // 0x0075 (LATIN SMALL LETTER U), then + // 1. Set hexDigits to the substring of string from k + 2 to k + 6. + // 2. Set optionalAdvance to 5. + // iv. Else if k + 3 ≤ len, then + // 1. Set hexDigits to the substring of string from k + 1 to k + 3. + // 2. Set optionalAdvance to 2. + // v. Let parseResult be ParseText(StringToCodePoints(hexDigits), HexDigits[~Sep]). + // vi. If parseResult is a Parse Node, then + // 1. Let n be the MV of parseResult. + // 2. Set C to the code unit whose numeric value is n. + // 3. Set k to k + optionalAdvance. + // c. Set R to the string-concatenation of R and C. + // d. Set k to k + 1. + while (k < len) { + uint16_t c = EcmaStringAccessor(string).Get(k); + JSHandle hexDigitsString; + if (c == CHAR16_PERCENT_SIGN) { + EcmaString *hexDigits = nullptr; + uint16_t optionalAdvance = 0; + if (k + 5 < len && // NOLINT 5: means offset by 5 + EcmaStringAccessor(string).Get(k + 1) == CHAR16_LATIN_SMALL_LETTER_U) { // NOLINT 1: means offset by 1 + hexDigits = EcmaStringAccessor(string).FastSubString(vm, string, + k + 2, 4); // NOLINT 2: means offset 4: means len + optionalAdvance = optionalAdvance + 5; // NOLINT 5: means plus 5 + } else if (k + 3 <= len) { // NOLINT 3: means offset + hexDigits = EcmaStringAccessor(string).FastSubString(vm, string, k + 1, 2); // NOLINT 2:means len + optionalAdvance = optionalAdvance + 2; // NOLINT 2: means plus 2 + } + if (hexDigits != nullptr) { + hexDigitsString = JSHandle(thread, hexDigits); + EcmaString *codePoints = StringToCodePoints(thread, hexDigitsString); + JSHandle codePointString = JSHandle(thread, codePoints); + bool isHex = true; + for (uint32_t i = 0; i < EcmaStringAccessor(codePointString).GetLength(); ++i) { + if (!IsHexDigits(EcmaStringAccessor(codePointString).Get(i))) { + isHex = false; + } + } + if (isHex) { + uint16_t n = GetValueFromHexString(codePointString); + c = n; + k = k + optionalAdvance; + } + } + } + r.push_back(c); + ++k; + } + // 7. Return R. + auto *returnData = reinterpret_cast(r.data()); + uint32_t retSize = r.size(); + return factory->NewFromUtf16Literal(returnData, retSize).GetTaggedValue(); +} } // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_global.h b/ecmascript/builtins/builtins_global.h index 433219a931ed4c9deaf27e2adbd8600225cd55f1..39a8a0474d4181ce1df9b139738cc6c88dafb657 100644 --- a/ecmascript/builtins/builtins_global.h +++ b/ecmascript/builtins/builtins_global.h @@ -19,16 +19,93 @@ #include "ecmascript/base/builtins_base.h" #include "ecmascript/js_thread.h" +#define BUILTIN_GLOBAL_CONSTANTS(V) \ + V("Infinity", INFINITY_VALUE) \ + V("NaN", NAN_VALUE) \ + V("undefined", UNDEFINED_VALUE) + +// List of functions in the global object. +// V(name, func, length, stubIndex) +// where BuiltinsGlobal::func refers to the native implementation of globalThis[name]. +// kungfu::BuiltinsStubCSigns::stubIndex refers to the builtin stub index, or INVALID if no stub available. +// The following global object properties are not implemented yet: +// - Encode ( string, extraUnescaped ) +// - Decode ( string, preserveEscapeSet ) +// - ParseHexOctet ( string, position ) +// The following global object properties are not listed here: +// - parseFloat ( string ), listed in builtins_number.h instead. +// - parseInt ( string ), listed in builtins_number.h instead. +#define BUILTIN_GLOBAL_FUNCTIONS_COMMON(V) \ + /* decodeURI ( encodedURI ) */ \ + V("decodeURI", DecodeURI, 1, INVALID) \ + /* decodeURIComponent ( encodedURIComponent ) */ \ + V("decodeURIComponent", DecodeURIComponent, 1, INVALID) \ + /* encodeURI ( uri ) */ \ + V("encodeURI", EncodeURI, 1, INVALID) \ + /* encodeURIComponent ( uriComponent ) */ \ + V("encodeURIComponent", EncodeURIComponent, 1, INVALID) \ + /* escape ( string ), defined in B.2.1 */ \ + V("escape", Escape, 1, INVALID) \ + /* eval ( x ), which is NOT supported in ArkTS engine */ \ + V("eval", NotSupportEval, 1, INVALID) \ + /* isFinite ( number ) */ \ + V("isFinite", IsFinite, 1, INVALID) \ + /* isNaN ( number ) */ \ + V("isNaN", IsNaN, 1, INVALID) \ + /* unescape ( string )*/ \ + V("unescape", Unescape, 1, INVALID) \ + /* The following are ArkTS extensions */ \ + V("markModuleCollectable", MarkModuleCollectable, 0, INVALID) \ + V("print", PrintEntrypoint, 0, INVALID) + +#if ECMASCRIPT_ENABLE_RUNTIME_STAT +#define BUILTIN_GLOBAL_FUNCTIONS_RUNTIME_STAT(V) \ + V("startRuntimeStat", StartRuntimeStat, 0, INVALID) \ + V("stopRuntimeStat", StopRuntimeStat, 0, INVALID) +#else +#define BUILTIN_GLOBAL_FUNCTIONS_RUNTIME_STAT(V) // Nothing +#endif + +#if ECMASCRIPT_ENABLE_OPT_CODE_PROFILER +#define BUILTIN_GLOBAL_FUNCTIONS_OPT_CODE_PROFILER(V) \ + V("printOptStat", PrintOptStat, 0, INVALID) +#else +#define BUILTIN_GLOBAL_FUNCTIONS_OPT_CODE_PROFILER(V) // Nothing +#endif + +#if ECMASCRIPT_ENABLE_FUNCTION_CALL_TIMER +#define BUILTIN_GLOBAL_FUNCTIONS_FUNCTION_CALL_TIMER(V) \ + V("printFunctionCallStat", PrintFunctionCallStat, 0, INVALID) +#else +#define BUILTIN_GLOBAL_FUNCTIONS_FUNCTION_CALL_TIMER(V) // Nothing +#endif + +#define BUILTIN_GLOBAL_FUNCTIONS(V) \ + BUILTIN_GLOBAL_FUNCTIONS_COMMON(V) \ + BUILTIN_GLOBAL_FUNCTIONS_RUNTIME_STAT(V) \ + BUILTIN_GLOBAL_FUNCTIONS_OPT_CODE_PROFILER(V) \ + BUILTIN_GLOBAL_FUNCTIONS_FUNCTION_CALL_TIMER(V) + namespace panda::ecmascript::builtins { static constexpr uint8_t BIT_MASK = 0x0F; static constexpr uint8_t BIT_MASK_FF = 0xFF; +static constexpr uint16_t BIT_MASK_4F = 0xFFFF; 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); +enum class Placement { + START = 0, + END, +}; + class BuiltinsGlobal : public base::BuiltinsBase { public: + static const inline JSTaggedValue INFINITY_VALUE = JSTaggedValue(base::POSITIVE_INFINITY); + static const inline JSTaggedValue NAN_VALUE = JSTaggedValue(base::NAN_VALUE); + static const inline JSTaggedValue UNDEFINED_VALUE = JSTaggedValue::Undefined(); + // 18.2.1 static JSTaggedValue NotSupportEval(EcmaRuntimeCallInfo *msg); // 18.2.2 @@ -42,6 +119,7 @@ public: static JSTaggedValue EncodeURIComponent(EcmaRuntimeCallInfo *msg); static JSTaggedValue PrintEntrypoint(EcmaRuntimeCallInfo *msg); + static JSTaggedValue MarkModuleCollectable(EcmaRuntimeCallInfo *msg); static JSTaggedValue CallJsBoundFunction(EcmaRuntimeCallInfo *msg); static JSTaggedValue CallJsProxy(EcmaRuntimeCallInfo *msg); #if ECMASCRIPT_ENABLE_RUNTIME_STAT @@ -56,8 +134,36 @@ public: #if ECMASCRIPT_ENABLE_FUNCTION_CALL_TIMER static JSTaggedValue PrintFunctionCallStat(EcmaRuntimeCallInfo *msg); #endif + // B.2.1.1 escape ( string ) + static JSTaggedValue Escape(EcmaRuntimeCallInfo *msg); + // B.2.1.2 unescape ( string ) + static JSTaggedValue Unescape(EcmaRuntimeCallInfo *msg); + + static Span GetGlobalConstants() + { + return Span(GLOBAL_CONSTANTS); + } + + static Span GetGlobalFunctions() + { + return Span(GLOBAL_FUNCTIONS); + } private: +#define BUILTIN_GLOBAL_CONSTANT_ENTRY(name, var) \ + base::BuiltinConstantEntry::Create(name, BuiltinsGlobal::var), +#define BUILTIN_GLOBAL_FUNCTION_ENTRY(name, func, length, id) \ + base::BuiltinFunctionEntry::Create(name, BuiltinsGlobal::func, length, kungfu::BuiltinsStubCSigns::id), + + static inline std::array GLOBAL_CONSTANTS = { + BUILTIN_GLOBAL_CONSTANTS(BUILTIN_GLOBAL_CONSTANT_ENTRY) + }; + static constexpr std::array GLOBAL_FUNCTIONS = { + BUILTIN_GLOBAL_FUNCTIONS(BUILTIN_GLOBAL_FUNCTION_ENTRY) + }; +#undef BUILTIN_GLOBAL_CONSTANT_ENTRY +#undef BUILTIN_GLOBAL_FUNCTION_ENTRY + static void PrintString(JSThread *thread, EcmaString *string); static void PrintValue(int64_t value, int64_t tag); static JSTaggedValue Encode(JSThread *thread, const JSHandle &str, judgURIFunc IsInURISet); @@ -69,6 +175,27 @@ private: static bool IsInMarkURISet(uint16_t ch); static bool IsHexDigits(uint16_t ch); static uint8_t GetValueFromTwoHex(uint16_t front, uint16_t behind); + static uint16_t GetValueFromHexString(const JSHandle &string); + // 22.1.3.17.2 StringPad ( S, maxLength, fillString, placement ) + static EcmaString *StringPad(JSThread *thread, + const JSHandle &string, + uint32_t maxLength, + const JSHandle &fillString, + Placement placement = Placement::START); + static bool IsUTF16HighSurrogate(uint16_t ch) + { + return base::utf_helper::DECODE_LEAD_LOW <= ch && ch <= base::utf_helper::DECODE_LEAD_HIGH; + } + + static bool IsUTF16LowSurrogate(uint16_t ch) + { + return base::utf_helper::DECODE_TRAIL_LOW <= ch && ch <= base::utf_helper::DECODE_TRAIL_HIGH; + } + + // 11.1.3 Static Semantics: UTF16SurrogatePairToCodePoint ( lead, trail ) + static uint16_t UTF16SurrogatePairToCodePoint(uint16_t lead, uint16_t trail); + // 11.1.5 Static Semantics: StringToCodePoints ( string ) + static EcmaString *StringToCodePoints(JSThread *thread, const JSHandle &string); }; } // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_json.cpp b/ecmascript/builtins/builtins_json.cpp index 9f28884818f1b957e35899bdf1420bbb63e99a71..22989faeec41eb847fe6aa393957378d893bcb74 100644 --- a/ecmascript/builtins/builtins_json.cpp +++ b/ecmascript/builtins/builtins_json.cpp @@ -15,6 +15,7 @@ #include "ecmascript/builtins/builtins_json.h" +#include "ecmascript/base/fast_json_stringifier.h" #include "ecmascript/base/json_parser.h" #include "ecmascript/base/json_stringifier.h" #include "ecmascript/base/number_helper.h" @@ -47,11 +48,11 @@ JSTaggedValue BuiltinsJson::Parse(EcmaRuntimeCallInfo *argv) RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle result; if (EcmaStringAccessor(parseString).IsUtf8()) { - panda::ecmascript::base::JsonParser parser(thread); - result = parser.ParseUtf8(*parseString); + panda::ecmascript::base::Utf8JsonParser parser(thread); + result = parser.Parse(*parseString); } else { - panda::ecmascript::base::JsonParser parser(thread); - result = parser.ParseUtf16(*parseString); + panda::ecmascript::base::Utf16JsonParser parser(thread); + result = parser.Parse(*parseString); } RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -86,6 +87,12 @@ JSTaggedValue BuiltinsJson::Stringify(EcmaRuntimeCallInfo *argv) uint32_t argc = argv->GetArgsNumber(); JSTaggedValue value = GetCallArg(argv, 0).GetTaggedValue(); + if (argc == 1 && thread->GetCurrentEcmaContext()->IsAotEntry()) { + JSHandle handleValue(thread, value); + panda::ecmascript::base::FastJsonStringifier stringifier(thread); + JSHandle result = stringifier.Stringify(handleValue); + return result.GetTaggedValue(); + } JSTaggedValue replacer = JSTaggedValue::Undefined(); JSTaggedValue gap = JSTaggedValue::Undefined(); diff --git a/ecmascript/builtins/builtins_list_format.cpp b/ecmascript/builtins/builtins_list_format.cpp index 781aacfaf8fbcb660fb089090e15cad33816e820..b9608aae523db04b4c6b7d8d5dd5b13b27a6e620 100644 --- a/ecmascript/builtins/builtins_list_format.cpp +++ b/ecmascript/builtins/builtins_list_format.cpp @@ -12,7 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - + #include "ecmascript/builtins/builtins_list_format.h" #include "ecmascript/intl/locale_helper.h" @@ -130,6 +130,7 @@ JSTaggedValue BuiltinsListFormat::FormatToParts(EcmaRuntimeCallInfo *argv) JSHandle array = JSHandle::Cast(listArray); JSHandle result = JSListFormat::FormatListToParts(thread, listFormat, array); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return result.GetTaggedValue(); } diff --git a/ecmascript/builtins/builtins_locale.cpp b/ecmascript/builtins/builtins_locale.cpp index 7ef01c85bb87fe266ba951d35d828b9a30c9b9b8..ff0d044feaedcf12f5aa43276abc7171fe66e588 100644 --- a/ecmascript/builtins/builtins_locale.cpp +++ b/ecmascript/builtins/builtins_locale.cpp @@ -19,7 +19,7 @@ #include "ecmascript/ecma_vm.h" #include "ecmascript/global_env.h" #include "ecmascript/js_locale.h" -#include "ecmascript/object_factory.h" +#include "ecmascript/object_factory-inl.h" namespace panda::ecmascript::builtins { // 10.1.3 Intl.Locale( tag [, options] ) @@ -56,9 +56,11 @@ JSTaggedValue BuiltinsLocale::LocaleConstructor(EcmaRuntimeCallInfo *argv) JSHandle localeString = factory->GetEmptyString(); if (!tag->IsJSLocale()) { localeString = JSTaggedValue::ToString(thread, tag); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); } else { icu::Locale *icuLocale = (JSHandle::Cast(tag))->GetIcuLocale(); localeString = intl::LocaleHelper::ToLanguageTag(thread, *icuLocale); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); } // 10. If options is undefined, then // a.Let options be ! ObjectCreate(null). diff --git a/ecmascript/builtins/builtins_map.cpp b/ecmascript/builtins/builtins_map.cpp index 47b1cf9f1007a0c56320a3d933962d3814a49491..7700f093ee3bfb59692724a3d96572fdf9362d80 100644 --- a/ecmascript/builtins/builtins_map.cpp +++ b/ecmascript/builtins/builtins_map.cpp @@ -133,7 +133,7 @@ JSTaggedValue BuiltinsMap::Has(EcmaRuntimeCallInfo *argv) if (!self->IsJSMap()) { THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSMap", JSTaggedValue::Exception()); } - JSMap *jsMap = JSMap::Cast(*JSTaggedValue::ToObject(thread, self)); + JSMap *jsMap = JSMap::Cast(self.GetTaggedValue().GetTaggedObject()); JSHandle key = GetCallArg(argv, 0); bool flag = jsMap->Has(key.GetTaggedValue()); return GetTaggedBoolean(flag); @@ -150,7 +150,7 @@ JSTaggedValue BuiltinsMap::Get(EcmaRuntimeCallInfo *argv) if (!self->IsJSMap()) { THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSMap", JSTaggedValue::Exception()); } - JSMap *jsMap = JSMap::Cast(*JSTaggedValue::ToObject(thread, self)); + JSMap *jsMap = JSMap::Cast(self.GetTaggedValue().GetTaggedObject()); JSHandle key = GetCallArg(argv, 0); JSTaggedValue value = jsMap->Get(key.GetTaggedValue()); return value; @@ -178,7 +178,7 @@ JSTaggedValue BuiltinsMap::ForEach(EcmaRuntimeCallInfo *argv) JSHandle thisArg = GetCallArg(argv, 1); JSMutableHandle hashMap(thread, map->GetLinkedMap()); - const int32_t argsLength = 3; + const uint32_t argsLength = 3; int index = 0; int totalElements = hashMap->NumberOfElements() + hashMap->NumberOfDeletedElements(); JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); @@ -227,8 +227,8 @@ JSTaggedValue BuiltinsMap::GetSize(EcmaRuntimeCallInfo *argv) 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(); + JSMap *jsMap = JSMap::Cast(self.GetTaggedValue().GetTaggedObject()); + uint32_t count = jsMap->GetSize(); return JSTaggedValue(count); } @@ -239,6 +239,7 @@ JSTaggedValue BuiltinsMap::Entries(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); JSHandle iter = JSMapIterator::CreateMapIterator(thread, self, IterationKind::KEY_AND_VALUE); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return iter.GetTaggedValue(); } @@ -249,6 +250,7 @@ JSTaggedValue BuiltinsMap::Keys(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); JSHandle iter = JSMapIterator::CreateMapIterator(thread, self, IterationKind::KEY); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return iter.GetTaggedValue(); } @@ -259,6 +261,7 @@ JSTaggedValue BuiltinsMap::Values(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); JSHandle iter = JSMapIterator::CreateMapIterator(thread, self, IterationKind::VALUE); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return iter.GetTaggedValue(); } @@ -308,7 +311,7 @@ JSTaggedValue BuiltinsMap::AddEntriesFromIterable(JSThread *thread, const JSHand if (thread->HasPendingException()) { return JSIterator::IteratorCloseAndReturn(thread, iter); } - const int32_t argsLength = 2; // 2: key and value pair + const uint32_t argsLength = 2; // 2: key and value pair JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, adder, JSHandle(target), undefined, argsLength); diff --git a/ecmascript/builtins/builtins_map.h b/ecmascript/builtins/builtins_map.h index 60346f2ec5eb2eaf859c0b4e5fd0e51aad940e29..76343a82be5085355893a6e82e69c216793e7409 100644 --- a/ecmascript/builtins/builtins_map.h +++ b/ecmascript/builtins/builtins_map.h @@ -19,6 +19,30 @@ #include "ecmascript/base/builtins_base.h" #include "ecmascript/ecma_runtime_call_info.h" +// List of functions in Map.prototype, excluding the constructor and '@@' properties. +// V(name, func, length, stubIndex) +// where BuiltinsMap::func refers to the native implementation of Map.prototype[name]. +// kungfu::BuiltinsStubCSigns::stubIndex refers to the builtin stub index, or INVALID if no stub available. +#define BUILTIN_MAP_PROTOTYPE_FUNCTIONS(V) \ + /* Map.prototype.clear ( ) */ \ + V("clear", Clear, 0, INVALID) \ + /* Map.prototype.delete ( key ) */ \ + V("delete", Delete, 1, MapDelete) \ + /* Map.prototype.entries ( ) */ \ + V("entries", Entries, 0, INVALID) \ + /* Map.prototype.forEach ( callbackfn [ , thisArg ] ) */ \ + V("forEach", ForEach, 1, MapForEach) \ + /* Map.prototype.get ( key ) */ \ + V("get", Get, 1, INVALID) \ + /* Map.prototype.has ( key ) */ \ + V("has", Has, 1, INVALID) \ + /* Map.prototype.keys ( ) */ \ + V("keys", Keys, 0, INVALID) \ + /* Map.prototype.set ( key, value ) */ \ + V("set", Set, 2, MapSet) \ + /* Map.prototype.values ( ) */ \ + V("values", Values, 0, INVALID) + namespace panda::ecmascript::builtins { class BuiltinsMap : public base::BuiltinsBase { public: @@ -51,6 +75,22 @@ public: static JSTaggedValue AddEntriesFromIterable(JSThread *thread, const JSHandle &target, const JSHandle &iterable, const JSHandle &adder, ObjectFactory *factory); + + // Excluding the constructor and '@@' internal properties. + static Span GetMapPrototypeFunctions() + { + return Span(MAP_PROTOTYPE_FUNCTIONS); + } + +private: +#define BUILTIN_MAP_FUNCTION_ENTRY(name, func, length, id) \ + base::BuiltinFunctionEntry::Create(name, BuiltinsMap::func, length, kungfu::BuiltinsStubCSigns::id), + + static constexpr std::array MAP_PROTOTYPE_FUNCTIONS = { + BUILTIN_MAP_PROTOTYPE_FUNCTIONS(BUILTIN_MAP_FUNCTION_ENTRY) + }; + +#undef BUILTIN_MAP_FUNCTION_ENTRY }; } // namespace panda::ecmascript::builtins #endif // ECMASCRIPT_BUILTINS_BUILTINS_MAP_H diff --git a/ecmascript/builtins/builtins_math.cpp b/ecmascript/builtins/builtins_math.cpp index 2b16ef09429aebfb17429588cd39278cc9711113..5aef97cd63d3be5d12e6f99654a802f3ff3e4328 100644 --- a/ecmascript/builtins/builtins_math.cpp +++ b/ecmascript/builtins/builtins_math.cpp @@ -34,6 +34,7 @@ JSTaggedValue BuiltinsMath::Abs(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle msg = GetCallArg(argv, 0); JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 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 @@ -52,6 +53,7 @@ JSTaggedValue BuiltinsMath::Acos(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle msg = GetCallArg(argv, 0); JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); double value = numberValue.GetNumber(); double result = base::NAN_VALUE; // value == -NaN , <-1 or > 1,result is NaN @@ -70,6 +72,7 @@ JSTaggedValue BuiltinsMath::Acosh(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle msg = GetCallArg(argv, 0); JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); double value = numberValue.GetNumber(); double result = base::NAN_VALUE; if (value >= 1) { @@ -87,6 +90,7 @@ JSTaggedValue BuiltinsMath::Asin(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle msg = GetCallArg(argv, 0); JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); double value = numberValue.GetNumber(); double result = base::NAN_VALUE; if (value >= -1 && value <= 1) { @@ -104,6 +108,7 @@ JSTaggedValue BuiltinsMath::Asinh(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle msg = GetCallArg(argv, 0); JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); double value = numberValue.GetNumber(); double result = base::NAN_VALUE; // value == -NaN, NaN, result is NaN @@ -122,6 +127,7 @@ JSTaggedValue BuiltinsMath::Atan(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle msg = GetCallArg(argv, 0); JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); double value = numberValue.GetNumber(); double result = base::NAN_VALUE; // value == -NaN, NaN, result is NaN @@ -140,6 +146,7 @@ JSTaggedValue BuiltinsMath::Atanh(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle msg = GetCallArg(argv, 0); JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); double value = numberValue.GetNumber(); double result = base::NAN_VALUE; if (value >= -1 && value <= 1) { @@ -159,7 +166,9 @@ JSTaggedValue BuiltinsMath::Atan2(EcmaRuntimeCallInfo *argv) JSHandle msgX = GetCallArg(argv, 1); double result = base::NAN_VALUE; JSTaggedNumber numberValueY = JSTaggedValue::ToNumber(thread, msgY); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSTaggedNumber numberValueX = JSTaggedValue::ToNumber(thread, msgX); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); double valueY = numberValueY.GetNumber(); double valueX = numberValueX.GetNumber(); // y = +0 and x > +0, return +0 @@ -186,6 +195,7 @@ JSTaggedValue BuiltinsMath::Cbrt(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle msg = GetCallArg(argv, 0); JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); double value = numberValue.GetNumber(); double result = base::NAN_VALUE; // if value == -NaN, NaN, result is NaN @@ -204,6 +214,7 @@ JSTaggedValue BuiltinsMath::Ceil(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle msg = GetCallArg(argv, 0); JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); double value = numberValue.GetNumber(); double result = base::NAN_VALUE; // If value is NaN or -NaN, +infinite, -infinite,return value @@ -228,6 +239,7 @@ JSTaggedValue BuiltinsMath::Clz32(EcmaRuntimeCallInfo *argv) constexpr int defaultValue = 32; JSHandle msg = GetCallArg(argv, 0); JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); double value = numberValue.GetNumber(); auto tmpValue = std::abs(value); auto result = numberValue.ToUint32(); @@ -247,6 +259,7 @@ JSTaggedValue BuiltinsMath::Cos(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle msg = GetCallArg(argv, 0); JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); double value = numberValue.GetNumber(); double result = base::NAN_VALUE; // If value is NaN or -NaN, +infinite, -infinite, result is NaN @@ -265,6 +278,7 @@ JSTaggedValue BuiltinsMath::Cosh(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle msg = GetCallArg(argv, 0); JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); double value = numberValue.GetNumber(); double result = base::NAN_VALUE; // if value is NaN or -NaN, result is NaN @@ -283,6 +297,7 @@ JSTaggedValue BuiltinsMath::Exp(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle msg = GetCallArg(argv, 0); JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); double value = numberValue.GetNumber(); double result = base::NAN_VALUE; // if value is NaN or -NaN, result is NaN @@ -301,6 +316,7 @@ JSTaggedValue BuiltinsMath::Expm1(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle msg = GetCallArg(argv, 0); JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); double value = numberValue.GetNumber(); double result = base::NAN_VALUE; // if value is NaN or -NaN, result is NaN @@ -319,6 +335,7 @@ JSTaggedValue BuiltinsMath::Floor(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle msg = GetCallArg(argv, 0); JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); double value = numberValue.GetNumber(); double result = base::NAN_VALUE; // If value is NaN or -NaN, +infinite, -infinite, +0, -0, return value @@ -345,6 +362,7 @@ JSTaggedValue BuiltinsMath::Fround(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle msg = GetCallArg(argv, 0); JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); double value = numberValue.GetNumber(); double result; if (std::isnan(std::abs(value))) { @@ -370,6 +388,7 @@ JSTaggedValue BuiltinsMath::Hypot(EcmaRuntimeCallInfo *argv) for (uint32_t i = 0; i < argLen; i++) { JSHandle msg = GetCallArg(argv, i); numberValue = JSTaggedValue::ToNumber(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); value = numberValue.GetNumber(); result = std::hypot(result, value); } @@ -386,7 +405,9 @@ JSTaggedValue BuiltinsMath::Imul(EcmaRuntimeCallInfo *argv) JSHandle msg1 = GetCallArg(argv, 0); JSHandle msg2 = GetCallArg(argv, 1); JSTaggedNumber numberValue1 = JSTaggedValue::ToNumber(thread, msg1); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSTaggedNumber numberValue2 = JSTaggedValue::ToNumber(thread, msg2); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); auto value1 = numberValue1.GetNumber(); auto value2 = numberValue2.GetNumber(); if (!std::isfinite(value1) || !std::isfinite(value2)) { @@ -409,6 +430,7 @@ JSTaggedValue BuiltinsMath::Log(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle msg = GetCallArg(argv, 0); JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); double value = numberValue.GetNumber(); double result = base::NAN_VALUE; // If value is NaN , -NaN , or < 0,result is NaN @@ -427,6 +449,7 @@ JSTaggedValue BuiltinsMath::Log1p(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle msg = GetCallArg(argv, 0); JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); double value = numberValue.GetNumber(); double result = base::NAN_VALUE; // If value is NaN , -NaN , or < -1,result is NaN @@ -445,6 +468,7 @@ JSTaggedValue BuiltinsMath::Log10(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle msg = GetCallArg(argv, 0); JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); double value = numberValue.GetNumber(); double result = base::NAN_VALUE; // If value is NaN , -NaN , or < 0,result is NaN @@ -463,6 +487,7 @@ JSTaggedValue BuiltinsMath::Log2(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle msg = GetCallArg(argv, 0); JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); double value = numberValue.GetNumber(); double result = base::NAN_VALUE; // If value is NaN , -NaN , or < 0,result is NaN @@ -493,6 +518,7 @@ JSTaggedValue BuiltinsMath::Max(EcmaRuntimeCallInfo *argv) for (uint32_t i = 0; i < argLen; i++) { JSHandle msg = GetCallArg(argv, i); numberValue = JSTaggedValue::ToNumber(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); value = numberValue.GetNumber(); if (std::isnan(std::abs(value))) { // If any value is NaN, or -NaN, the max result is NaN @@ -527,6 +553,7 @@ JSTaggedValue BuiltinsMath::Min(EcmaRuntimeCallInfo *argv) for (uint32_t i = 0; i < argLen; i++) { JSHandle msg = GetCallArg(argv, i); numberValue = JSTaggedValue::ToNumber(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); value = numberValue.GetNumber(); if (std::isnan(std::abs(value))) { // If any value is NaN or -NaN, the min result is NaN @@ -600,6 +627,7 @@ JSTaggedValue BuiltinsMath::Round(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle msg = GetCallArg(argv, 0); JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); double value = numberValue.GetNumber(); auto result = base::NAN_VALUE; const double diff = 0.5; @@ -637,6 +665,7 @@ JSTaggedValue BuiltinsMath::Sign(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle msg = GetCallArg(argv, 0); JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); double value = numberValue.GetNumber(); if (std::isnan(std::abs(value))) { return GetTaggedDouble(std::abs(value)); @@ -659,6 +688,7 @@ JSTaggedValue BuiltinsMath::Sin(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle msg = GetCallArg(argv, 0); JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); double value = numberValue.GetNumber(); double result = base::NAN_VALUE; // If value is NaN or -NaN, the result is NaN @@ -677,6 +707,7 @@ JSTaggedValue BuiltinsMath::Sinh(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle msg = GetCallArg(argv, 0); JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); double value = numberValue.GetNumber(); double result = base::NAN_VALUE; // If value is NaN or -NaN, the result is NaN @@ -695,6 +726,7 @@ JSTaggedValue BuiltinsMath::Sqrt(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle msg = GetCallArg(argv, 0); JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); double value = numberValue.GetNumber(); double result = base::NAN_VALUE; // If value is negative, include -NaN and -Infinity but not -0.0, the result is NaN @@ -717,6 +749,7 @@ JSTaggedValue BuiltinsMath::Tan(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle msg = GetCallArg(argv, 0); JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); double value = numberValue.GetNumber(); double result = base::NAN_VALUE; // If value is NaN or -NaN, +infinite, -infinite, result is NaN @@ -735,6 +768,7 @@ JSTaggedValue BuiltinsMath::Tanh(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle msg = GetCallArg(argv, 0); JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); double value = numberValue.GetNumber(); double result = base::NAN_VALUE; if (!std::isnan(std::abs(value))) { @@ -752,6 +786,7 @@ JSTaggedValue BuiltinsMath::Trunc(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle msg = GetCallArg(argv, 0); JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); double value = numberValue.GetNumber(); double result = base::NAN_VALUE; if (!std::isfinite(value)) { diff --git a/ecmascript/builtins/builtins_math.h b/ecmascript/builtins/builtins_math.h index 293d6761cfc5d1e3363c4ef83460fda1f2f14b43..c80d1c47dba577662506a9ed3bffadc778a5401a 100644 --- a/ecmascript/builtins/builtins_math.h +++ b/ecmascript/builtins/builtins_math.h @@ -18,6 +18,58 @@ #include "ecmascript/base/builtins_base.h" +// List of constants in Math, excluding '@@' internal properties. +#define BUILTIN_MATH_CONSTANTS(V) \ + V(E) \ + V(LN10) \ + V(LN2) \ + V(LOG10E) \ + V(LOG2E) \ + V(PI) \ + V(SQRT1_2) \ + V(SQRT2) + +// List of functions in Math. +// V(name, func, length, stubIndex) +// where BuiltinsMath::func refers to the native implementation of Math[name]. +// kungfu::BuiltinsStubCSigns::stubIndex refers to the builtin stub index, or INVALID if no stub available. +#define BUILTIN_MATH_FUNCTIONS(V) \ + V("abs", Abs, 1, ABS) /* Math.abs ( x ) */ \ + V("acos", Acos, 1, ACOS) /* Math.acos ( x ) */ \ + V("acosh", Acosh, 1, INVALID) /* Math.acosh ( x ) */ \ + V("asin", Asin, 1, INVALID) /* Math.asin ( x ) */ \ + V("asinh", Asinh, 1, INVALID) /* Math.asinh ( x ) */ \ + V("atan", Atan, 1, ATAN) /* Math.atan ( x ) */ \ + V("atan2", Atan2, 2, INVALID) /* Math.atan2 ( y, x ) */ \ + V("atanh", Atanh, 1, INVALID) /* Math.atanh ( x ) */ \ + V("cbrt", Cbrt, 1, INVALID) /* Math.cbrt ( x ) */ \ + V("ceil", Ceil, 1, INVALID) /* Math.ceil ( x ) */ \ + V("clz32", Clz32, 1, INVALID) /* Math.clz32 ( x ) */ \ + V("cos", Cos, 1, COS) /* Math.cos ( x ) */ \ + V("cosh", Cosh, 1, INVALID) /* Math.cosh ( x ) */ \ + V("exp", Exp, 1, INVALID) /* Math.exp ( x ) */ \ + V("expm1", Expm1, 1, INVALID) /* Math.expm1 ( x ) */ \ + V("floor", Floor, 1, FLOOR) /* Math.floor ( x ) */ \ + V("fround", Fround, 1, INVALID) /* Math.fround ( x ) */ \ + V("hypot", Hypot, 2, INVALID) /* Math.hypot ( ...args ) */ \ + V("imul", Imul, 2, INVALID) /* Math.imul ( x, y ) */ \ + V("log", Log, 1, INVALID) /* Math.log ( x ) */ \ + V("log10", Log10, 1, INVALID) /* Math.log10 ( x ) */ \ + V("log1p", Log1p, 1, INVALID) /* Math.log1p ( x ) */ \ + V("log2", Log2, 1, INVALID) /* Math.log2 ( x ) */ \ + V("max", Max, 2, INVALID) /* Math.max ( ...args ) */ \ + V("min", Min, 2, INVALID) /* Math.min ( ...args ) */ \ + V("pow", Pow, 2, INVALID) /* Math.pow ( base, exponent ) */ \ + V("random", Random, 0, INVALID) /* Math.random ( ) */ \ + V("round", Round, 1, INVALID) /* Math.round ( x ) */ \ + V("sign", Sign, 1, INVALID) /* Math.sign ( x ) */ \ + V("sin", Sin, 1, SIN) /* Math.sin ( x ) */ \ + V("sinh", Sinh, 1, INVALID) /* Math.sinh ( x ) */ \ + V("sqrt", Sqrt, 1, SQRT) /* Math.sqrt ( x ) */ \ + V("tan", Tan, 1, INVALID) /* Math.tan ( x ) */ \ + V("tanh", Tanh, 1, INVALID) /* Math.tanh ( x ) */ \ + V("trunc", Trunc, 1, INVALID) /* Math.trunc ( x ) */ + namespace panda::ecmascript::builtins { class BuiltinsMath : public base::BuiltinsBase { public: @@ -107,6 +159,34 @@ public: static JSTaggedValue Tanh(EcmaRuntimeCallInfo *argv); // 20.2.2.35 static JSTaggedValue Trunc(EcmaRuntimeCallInfo *argv); + + // Excluding the '@@' internal properties. + static Span GetMathConstants() + { + return Span(MATH_CONSTANTS); + } + + static Span GetMathFunctions() + { + return Span(MATH_FUNCTIONS); + } + +private: +#define BUILTIN_MATH_CONSTANT_ENTRY(name) \ + base::BuiltinConstantEntry::Create(#name, JSTaggedValue(BuiltinsMath::name)), + + static inline std::array MATH_CONSTANTS = { + BUILTIN_MATH_CONSTANTS(BUILTIN_MATH_CONSTANT_ENTRY) + }; +#undef BUILTIN_MATH_CONSTANT_ENTRY + +#define BUILTIN_MATH_FUNCTION_ENTRY(name, func, length, builtinId) \ + base::BuiltinFunctionEntry::Create(name, BuiltinsMath::func, length, kungfu::BuiltinsStubCSigns::builtinId), + + static constexpr std::array MATH_FUNCTIONS = { + BUILTIN_MATH_FUNCTIONS(BUILTIN_MATH_FUNCTION_ENTRY) + }; +#undef BUILTIN_MATH_FUNCTION_ENTRY }; } // namespace panda::ecmascript::builtins #endif // ECMASCRIPT_BUILTINS_BUILTINS_MATH_H diff --git a/ecmascript/builtins/builtins_number.cpp b/ecmascript/builtins/builtins_number.cpp index bbd99fcfc828922061c92d07ea982bde7bef60cf..ed7ed4f0660e31108cdc61b7ad4607a00ae974c4 100644 --- a/ecmascript/builtins/builtins_number.cpp +++ b/ecmascript/builtins/builtins_number.cpp @@ -196,6 +196,12 @@ JSTaggedValue BuiltinsNumber::ParseInt(EcmaRuntimeCallInfo *argv) // 1. Let inputString be ToString(string). JSHandle numberString = JSTaggedValue::ToString(thread, msg); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if ((radix == base::DECIMAL || radix == 0)) { + int32_t elementIndex = 0; + if (EcmaStringAccessor(numberString).ToInt(&elementIndex)) { + return GetTaggedInt(elementIndex); + } + } CVector buf; Span str = EcmaStringAccessor(numberString).ToUtf8Span(buf); diff --git a/ecmascript/builtins/builtins_number.h b/ecmascript/builtins/builtins_number.h index fc770f84ed24a71306ebae7e64421655ab070fa0..676cdfdf23d3166741840016a3c9b2b7c150a4e9 100644 --- a/ecmascript/builtins/builtins_number.h +++ b/ecmascript/builtins/builtins_number.h @@ -19,9 +19,75 @@ #include "ecmascript/base/builtins_base.h" #include "ecmascript/js_tagged_value.h" +// List of constants in Number, excluding '@@' internal properties. +#define BUILTIN_NUMBER_CONSTANTS(V) \ + V(EPSILON) /* Number.EPSILON */ \ + V(MAX_SAFE_INTEGER) /* Number.MAX_SAFE_INTEGER */ \ + V(MAX_VALUE) /* Number.MAX_VALUE */ \ + V(MIN_SAFE_INTEGER) /* Number.MIN_SAFE_INTEGER */ \ + V(MIN_VALUE) /* Number.MIN_VALUE */ \ + V(NEGATIVE_INFINITY) /* Number.NEGATIVE_INFINITY */ \ + V(NaN) /* Number.NaN */ \ + V(POSITIVE_INFINITY) /* Number.POSITIVE_INFINITY */ + +// List of functions in Number. +// V(name, func, length, stubIndex) +// where BuiltinsNumber::func refers to the native implementation of Number[name]. +// kungfu::BuiltinsStubCSigns::stubIndex refers to the builtin stub index, or INVALID if no stub available. +#define BUILTIN_NUMBER_NON_GLOBAL_FUNCTIONS(V) \ + V("isFinite", IsFinite, 1, INVALID) /* Number.isFinite ( number ) */ \ + V("isInteger", IsInteger, 1, INVALID) /* Number.isInteger ( number ) */ \ + V("isNaN", IsNaN, 1, INVALID) /* Number.isNaN ( number ) */ \ + V("isSafeInteger", IsSafeInteger, 1, INVALID) /* Number.isSafeInteger ( number ) */ + +// List of functions in Number that can be accessed via globalThis. +// V(name, func, length, stubIndex) +// where BuiltinsNumber::func refers to the native implementation of Number[name]. +#define BUILTIN_NUMBER_GLOBAL_FUNCTIONS(V) \ + V("parseFloat", ParseFloat, 1, INVALID) /* Number.parseFloat ( string ) */ \ + V("parseInt", ParseInt, 2, INVALID) /* Number.parseInt ( string, radix ) */ + +#define BUILTIN_NUMBER_FUNCTIONS(V) \ + BUILTIN_NUMBER_NON_GLOBAL_FUNCTIONS(V) \ + BUILTIN_NUMBER_GLOBAL_FUNCTIONS(V) + +// List of functions in Number.prototype, excluding the constructor and '@@' properties. +// V(name, func, length, stubIndex) +// where BuiltinsNumber::func refers to the native implementation of Number.prototype[name]. +#define BUILTIN_NUMBER_PROTOTYPE_FUNCTIONS(V) \ + /* Number.prototype.toExponential ( fractionDigits ) */ \ + V("toExponential", ToExponential, 1, INVALID) \ + /* Number.prototype.toFixed ( fractionDigits ) */ \ + V("toFixed", ToFixed, 1, INVALID) \ + /* Number.prototype.toLocaleString ( [ reserved1 [ , reserved2 ] ] ) */ \ + V("toLocaleString", ToLocaleString, 0, INVALID) \ + /* Number.prototype.toPrecision ( precision ) */ \ + V("toPrecision", ToPrecision, 1, INVALID) \ + /* Number.prototype.toString ( [ radix ] ) */ \ + V("toString", ToString, 1, INVALID) \ + /* Number.prototype.valueOf ( ) */ \ + V("valueOf", ValueOf, 0, INVALID) + namespace panda::ecmascript::builtins { class BuiltinsNumber : public base::BuiltinsBase { public: + // 21.1.2.1 Number.EPSILON + static constexpr double EPSILON = std::numeric_limits::epsilon(); + // 21.1.2.6 Number.MAX_SAFE_INTEGER (which is 2**53 - 1 = 9007199254740991) + static constexpr int64_t MAX_SAFE_INTEGER = (1LL << 53) - 1; + // 21.1.2.8 Number.MIN_SAFE_INTEGER (which is -(2**53 - 1) = -9007199254740991) + static constexpr int64_t MIN_SAFE_INTEGER = -((1LL << 53) - 1); + // 21.1.2.7 Number.MAX_VALUE + static constexpr double MAX_VALUE = std::numeric_limits::max(); + // 21.1.2.9 Number.MIN_VALUE + static constexpr double MIN_VALUE = std::numeric_limits::denorm_min(); + // 21.1.2.14 Number.POSITIVE_INFINITY + static constexpr double POSITIVE_INFINITY = std::numeric_limits::infinity(); + // 21.1.2.11 Number.NEGATIVE_INFINITY + static constexpr double NEGATIVE_INFINITY = -POSITIVE_INFINITY; + // 21.1.2.10 Number.NaN + static constexpr double NaN = NAN; + // 20.1.1.1 static JSTaggedValue NumberConstructor(EcmaRuntimeCallInfo *argv); @@ -52,7 +118,53 @@ public: // 20.1.3.7 static JSTaggedValue ValueOf(EcmaRuntimeCallInfo *argv); + // Excluding the '@@' internal properties. + static Span GetNumberConstants() + { + return Span(NUMBER_CONSTANTS); + } + + // Excluding the '@@' internal properties. + static Span GetNumberNonGlobalFunctions() + { + return Span(NUMBER_NON_GLOBAL_FUNCTIONS); + } + + // Excluding the '@@' internal properties. + static Span GetNumberGlobalFunctions() + { + return Span(NUMBER_GLOBAL_FUNCTIONS); + } + + // Excluding the constructor and '@@' internal properties. + static Span GetNumberPrototypeFunctions() + { + return Span(NUMBER_PROTOTYPE_FUNCTIONS); + } + private: +#define BUILTIN_NUMBER_CONSTANT_ENTRY(name) \ + base::BuiltinConstantEntry::Create(#name, JSTaggedValue(BuiltinsNumber::name)), + + static inline std::array NUMBER_CONSTANTS = { + BUILTIN_NUMBER_CONSTANTS(BUILTIN_NUMBER_CONSTANT_ENTRY) + }; +#undef BUILTIN_NUMBER_CONSTANT_ENTRY + +#define BUILTIN_NUMBER_FUNCTION_ENTRY(name, func, length, id) \ + base::BuiltinFunctionEntry::Create(name, BuiltinsNumber::func, length, kungfu::BuiltinsStubCSigns::id), + + static constexpr std::array NUMBER_NON_GLOBAL_FUNCTIONS = { + BUILTIN_NUMBER_NON_GLOBAL_FUNCTIONS(BUILTIN_NUMBER_FUNCTION_ENTRY) + }; + static constexpr std::array NUMBER_GLOBAL_FUNCTIONS = { + BUILTIN_NUMBER_GLOBAL_FUNCTIONS(BUILTIN_NUMBER_FUNCTION_ENTRY) + }; + static constexpr std::array NUMBER_PROTOTYPE_FUNCTIONS = { + BUILTIN_NUMBER_PROTOTYPE_FUNCTIONS(BUILTIN_NUMBER_FUNCTION_ENTRY) + }; +#undef BUILTIN_NUMBER_FUNCTION_ENTRY + static JSTaggedNumber ThisNumberValue(JSThread *thread, EcmaRuntimeCallInfo *argv); }; } // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_object.cpp b/ecmascript/builtins/builtins_object.cpp index c8e94dfc35417a30ed777ebb5f839c7cae451392..a942d26e13800adf9eaeab0285b9ca35995a8a05 100644 --- a/ecmascript/builtins/builtins_object.cpp +++ b/ecmascript/builtins/builtins_object.cpp @@ -686,14 +686,22 @@ JSTaggedValue BuiltinsObject::HasOwnProperty(EcmaRuntimeCallInfo *argv) BUILTINS_API_TRACE(thread, Object, HasOwnProperty); [[maybe_unused]] EcmaHandleScope handleScope(thread); // 1. Let P be ToPropertyKey(V). + JSHandle thisValue = GetThis(argv); JSHandle prop = GetCallArg(argv, 0); + + JSTaggedValue result = ObjectFastOperator::HasOwnProperty(thread, thisValue.GetTaggedValue(), + prop.GetTaggedValue()); + if (!result.IsHole()) { + return GetTaggedBoolean(true); + } + JSHandle property = JSTaggedValue::ToPropertyKey(thread, prop); // 2. ReturnIfAbrupt(P). RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 3. Let O be ToObject(this value). - JSHandle object = JSTaggedValue::ToObject(thread, GetThis(argv)); + JSHandle object = JSTaggedValue::ToObject(thread, thisValue); // 4. ReturnIfAbrupt(O). RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -730,6 +738,7 @@ JSTaggedValue BuiltinsObject::IsPrototypeOf(EcmaRuntimeCallInfo *argv) return GetTaggedBoolean(true); } msgValueHandle.Update(JSTaggedValue::GetPrototype(thread, msgValueHandle)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); } return GetTaggedBoolean(false); } @@ -790,44 +799,47 @@ JSTaggedValue BuiltinsObject::ToLocaleString(EcmaRuntimeCallInfo *argv) return JSFunction::Invoke(info, calleeKey); } -JSTaggedValue BuiltinsObject::GetBuiltinTag(JSThread *thread, const JSHandle &object) +JSTaggedValue BuiltinsObject::GetBuiltinObjectToString(JSThread *thread, const JSHandle &object) { - BUILTINS_API_TRACE(thread, Object, GetBuiltinTag); + BUILTINS_API_TRACE(thread, Object, GetBuiltinObjectToString); // 4. Let isArray be IsArray(O). bool isArray = object->IsJSArray(); // 5. ReturnIfAbrupt(isArray). RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - JSHandle builtinTag = factory->NewFromASCII("Object"); - // 6. If isArray is true, let builtinTag be "Array". if (isArray) { - builtinTag = factory->NewFromASCII("Array"); + // 6. If isArray is true, return "[object Array]". + return thread->GlobalConstants()->GetArrayToString(); } else if (object->IsJSPrimitiveRef()) { - // 7. Else, if O is an exotic String object, let builtinTag be "String". + // 7. Else, if O is an exotic String object, return "[object String]". JSPrimitiveRef *primitiveRef = JSPrimitiveRef::Cast(*object); if (primitiveRef->IsString()) { - builtinTag = factory->NewFromASCII("String"); + return thread->GlobalConstants()->GetStringToString(); } else if (primitiveRef->IsBoolean()) { - // 11. Else, if O has a [[BooleanData]] internal slot, let builtinTag be "Boolean". - builtinTag = factory->NewFromASCII("Boolean"); + // 11. Else, if O has a [[BooleanData]] internal slot, return "[object Boolean]". + return thread->GlobalConstants()->GetBooleanToString(); } else if (primitiveRef->IsNumber()) { - // 12. Else, if O has a [[NumberData]] internal slot, let builtinTag be "Number". - builtinTag = factory->NewFromASCII("Number"); + // 12. Else, if O has a [[NumberData]] internal slot, return "[object Number]". + return thread->GlobalConstants()->GetNumberToString(); } } else if (object->IsArguments()) { - builtinTag = factory->NewFromASCII("Arguments"); + // if O has a [[ArgumentsData]] internal slot, return "[object Arguments]". + return thread->GlobalConstants()->GetArgumentsToString(); } else if (object->IsCallable()) { - builtinTag = factory->NewFromASCII("Function"); + // if O has a [[CallableData]] internal slot, return "[object Function]". + return thread->GlobalConstants()->GetFunctionToString(); } else if (object->IsJSError()) { - builtinTag = JSHandle::Cast(thread->GlobalConstants()->GetHandledErrorString()); + // if O has a [[ErrorData]] internal slot, return "[object Error]". + return thread->GlobalConstants()->GetErrorToString(); } else if (object->IsDate()) { - builtinTag = factory->NewFromASCII("Date"); + // if O has a [[DateData]] internal slot, return "[object Date]". + return thread->GlobalConstants()->GetDateToString(); } else if (object->IsJSRegExp()) { - builtinTag = factory->NewFromASCII("RegExp"); + // if O has a [[RegExpData]] internal slot, return "[object JSRegExp]". + return thread->GlobalConstants()->GetRegExpToString(); } - // 15. Else, let builtinTag be "Object". - return builtinTag.GetTaggedValue(); + // 15. Else, return "[Object Object]". + return thread->GlobalConstants()->GetObjectToString(); } // 19.1.3.6 Object.prototype.toString() @@ -841,17 +853,16 @@ JSTaggedValue BuiltinsObject::ToString(EcmaRuntimeCallInfo *argv) JSHandle msg = GetThis(argv); if (msg->IsUndefined()) { - return GetTaggedString(thread, "[object Undefined]"); + return thread->GlobalConstants()->GetUndefinedToString(); } // 2. If the this value is null, return "[object Null]". if (msg->IsNull()) { - return GetTaggedString(thread, "[object Null]"); + return thread->GlobalConstants()->GetNullToString(); } // 3. Let O be ToObject(this value). JSHandle object = JSTaggedValue::ToObject(thread, GetThis(argv)); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - JSHandle builtinTag(thread, GetBuiltinTag(thread, object)); // 16. Let tag be Get (O, @@toStringTag). auto ecmaVm = thread->GetEcmaVM(); @@ -863,9 +874,9 @@ JSTaggedValue BuiltinsObject::ToString(EcmaRuntimeCallInfo *argv) // 17. ReturnIfAbrupt(tag). RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - // 18. If Type(tag) is not String, let tag be builtinTag. + // 18. If Type(tag) is not String, return builtin object to string. if (!tag->IsString()) { - tag = builtinTag; + return GetBuiltinObjectToString(thread, object); } // 19. Return the String that is the result of concatenating "[object ", tag, and "]". @@ -874,6 +885,7 @@ JSTaggedValue BuiltinsObject::ToString(EcmaRuntimeCallInfo *argv) JSHandle newLeftStringHandle = factory->ConcatFromString(leftString, JSTaggedValue::ToString(thread, tag)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); auto result = factory->ConcatFromString(newLeftStringHandle, rightString); return result.GetTaggedValue(); } @@ -1035,8 +1047,35 @@ JSTaggedValue BuiltinsObject::CreateDataPropertyOnObjectFunctions(EcmaRuntimeCal // 5. Perform ! CreateDataPropertyOrThrow(O, propertyKey, value). JSObject::CreateDataPropertyOrThrow(thread, thisObjHandle, propertyKey, value); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 6. Return undefined. return JSTaggedValue::Undefined(); } + +JSTaggedValue BuiltinsObject::HasOwn(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, Object, HasOwn); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let obj be ? ToObject(O). + JSHandle obj = GetCallArg(argv, 0); + JSHandle object = JSTaggedValue::ToObject(thread, obj); + + // 2.ReturnIfAbrupt(obj). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3.Let key be ToPropertyKey(P). + JSHandle prop = GetCallArg(argv, 1); + JSHandle key = JSTaggedValue::ToPropertyKey(thread, prop); + + // 4. ReturnIfAbrupt(4). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 5. Return HasOwnProperty(O, P). + bool res = JSTaggedValue::HasOwnProperty(thread, JSHandle::Cast(object), key); + return GetTaggedBoolean(res); +} } // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_object.h b/ecmascript/builtins/builtins_object.h index fbadc785edbff192b3e219dd67003e230070999b..7517b8d8697e7a138e1e6923c13959cfe3d31c07 100644 --- a/ecmascript/builtins/builtins_object.h +++ b/ecmascript/builtins/builtins_object.h @@ -21,6 +21,74 @@ #include "ecmascript/js_handle.h" #include "ecmascript/js_hclass.h" +// List of functions in Object, excluding the '@@' properties. +// V(name, func, length, stubIndex) +// where BuiltinsObject::func refers to the native implementation of Object[name]. +// kungfu::BuiltinsStubCSigns::stubIndex refers to the builtin stub index, or INVALID if no stub available. +// The following functions are not implemented yet: +// - Object.getOwnPropertyDescriptors ( O ) +#define BUILTIN_OBJECT_FUNCTIONS(V) \ + /* Object.assign ( target, ...sources ) */ \ + V("assign", Assign, 2, INVALID) \ + /* Object.create ( O, Properties ) */ \ + V("create", Create, 2, INVALID) \ + /* Object.defineProperties ( O, Properties ) */ \ + V("defineProperties", DefineProperties, 2, INVALID) \ + /* Object.defineProperty ( O, P, Attributes ) */ \ + V("defineProperty", DefineProperty, 3, INVALID) \ + /* Object.entries ( O ) */ \ + V("entries", Entries, 1, INVALID) \ + /* Object.freeze ( O ) */ \ + V("freeze", Freeze, 1, INVALID) \ + /* Object.fromEntries ( iterable ) */ \ + V("fromEntries", FromEntries, 1, INVALID) \ + /* Object.getOwnPropertyDescriptor ( O, P ) */ \ + V("getOwnPropertyDescriptor", GetOwnPropertyDescriptor, 2, INVALID) \ + /* Object.getOwnPropertyNames ( O ) */ \ + V("getOwnPropertyNames", GetOwnPropertyNames, 1, INVALID) \ + /* Object.getOwnPropertySymbols ( O ) */ \ + V("getOwnPropertySymbols", GetOwnPropertySymbols, 1, INVALID) \ + /* Object.getPrototypeOf ( O ) */ \ + V("getPrototypeOf", GetPrototypeOf, 1, INVALID) \ + /* Object.hasOwn ( O, P ) */ \ + V("hasOwn", HasOwn, 2, INVALID) \ + /* Object.is ( value1, value2 ) */ \ + V("is", Is, 2, INVALID) \ + /* Object.isExtensible ( O ) */ \ + V("isExtensible", IsExtensible, 1, INVALID) \ + /* Object.isFrozen ( O ) */ \ + V("isFrozen", IsFrozen, 1, INVALID) \ + /* Object.isSealed ( O ) */ \ + V("isSealed", IsSealed, 1, INVALID) \ + /* Object.keys ( O ) */ \ + V("keys", Keys, 1, INVALID) \ + /* Object.preventExtensions ( O ) */ \ + V("preventExtensions", PreventExtensions, 1, INVALID) \ + /* Object.seal ( O ) */ \ + V("seal", Seal, 1, INVALID) \ + /* Object.setPrototypeOf ( O, proto ) */ \ + V("setPrototypeOf", SetPrototypeOf, 2, INVALID) \ + /* Object.values ( O ) */ \ + V("values", Values, 1, INVALID) + +// List of functions in Object.prototype, excluding the constructor and '@@' properties. +// V(name, func, length, stubIndex) +// where BuiltinsObject::func refers to the native implementation of Object.prototype[name]. +#define BUILTIN_OBJECT_PROTOTYPE_FUNCTIONS(V) \ + V("createRealm", CreateRealm, 0, INVALID) \ + /* Object.prototype.hasOwnProperty ( V ) */ \ + V("hasOwnProperty", HasOwnProperty, 1, INVALID) \ + /* Object.prototype.isPrototypeOf ( V ) */ \ + V("isPrototypeOf", IsPrototypeOf, 1, INVALID) \ + /* Object.prototype.propertyIsEnumerable ( V ) */ \ + V("propertyIsEnumerable", PropertyIsEnumerable, 1, INVALID) \ + /* Object.prototype.toLocaleString ( [ reserved1 [ , reserved2 ] ] ) */ \ + V("toLocaleString", ToLocaleString, 0, INVALID) \ + /* Object.prototype.toString ( ) */ \ + V("toString", ToString, 0, ObjectToString) \ + /* Object.prototype.valueOf ( ) */ \ + V("valueOf", ValueOf, 0, INVALID) + namespace panda::ecmascript::builtins { enum class KeyType : uint8_t { STRING_TYPE = 0, @@ -93,11 +161,36 @@ public: // 20.1.2.7.1 CreateDataPropertyOnObject Functions static JSTaggedValue CreateDataPropertyOnObjectFunctions(EcmaRuntimeCallInfo *argv); + // 20.1.2.13 Object.hasOwn ( O, P ) + static JSTaggedValue HasOwn(EcmaRuntimeCallInfo *argv); + + static Span GetObjectFunctions() + { + return Span(OBJECT_FUNCTIONS); + } + + // Excluding the constructor and '@@' internal properties + static Span GetObjectPrototypeFunctions() + { + return Span(OBJECT_PROTOTYPE_FUNCTIONS); + } + private: +#define BUILTIN_OBJECT_FUNCTION_ENTRY(name, func, length, id) \ + base::BuiltinFunctionEntry::Create(name, BuiltinsObject::func, length, kungfu::BuiltinsStubCSigns::id), + + static constexpr std::array OBJECT_FUNCTIONS = { + BUILTIN_OBJECT_FUNCTIONS(BUILTIN_OBJECT_FUNCTION_ENTRY) + }; + static constexpr std::array OBJECT_PROTOTYPE_FUNCTIONS = { + BUILTIN_OBJECT_PROTOTYPE_FUNCTIONS(BUILTIN_OBJECT_FUNCTION_ENTRY) + }; +#undef BUILTIN_OBJECT_FUNCTION_ENTRY + static JSTaggedValue ObjectDefineProperties(JSThread *thread, const JSHandle &obj, const JSHandle &prop); static JSTaggedValue GetOwnPropertyKeys(JSThread *thread, const JSHandle &obj, const KeyType &type); - static JSTaggedValue GetBuiltinTag(JSThread *thread, const JSHandle &object); + static JSTaggedValue GetBuiltinObjectToString(JSThread *thread, const JSHandle &object); }; } // namespace panda::ecmascript::builtins #endif // ECMASCRIPT_BUILTINS_BUILTINS_OBJECT_H diff --git a/ecmascript/builtins/builtins_promise.cpp b/ecmascript/builtins/builtins_promise.cpp index b145ef32d8ebb0a788ae3fbbb76c0a45acce3517..58c97ca44d7f8addb5785e07a4cc69f15e2c022a 100644 --- a/ecmascript/builtins/builtins_promise.cpp +++ b/ecmascript/builtins/builtins_promise.cpp @@ -73,7 +73,7 @@ JSTaggedValue BuiltinsPromise::PromiseConstructor(EcmaRuntimeCallInfo *argv) auto resolveFunc = resolvingFunction->GetResolveFunction(); auto rejectFunc = resolvingFunction->GetRejectFunction(); JSHandle undefined = globalConst->GetHandledUndefined(); - const int32_t argsLength = 2; // 2: «resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]]» + const uint32_t argsLength = 2; // 2: «resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]]» EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, executor, undefined, undefined, argsLength); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); info->SetCallArg(resolveFunc, rejectFunc); @@ -152,6 +152,7 @@ JSTaggedValue BuiltinsPromise::All(EcmaRuntimeCallInfo *argv) if (!itRecord->GetDone()) { JSHandle closeVal = JSIterator::IteratorClose(thread, itor, JSHandle::Cast(result)); + RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, capa); if (closeVal.GetTaggedValue().IsRecord()) { result = JSHandle::Cast(closeVal); RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, capa); @@ -220,6 +221,7 @@ JSTaggedValue BuiltinsPromise::Race(EcmaRuntimeCallInfo *argv) if (!iteratorRecord->GetDone()) { JSHandle value = JSIterator::IteratorClose(thread, iterator, JSHandle::Cast(result)); + RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability); if (value.GetTaggedValue().IsCompletionRecord()) { result = JSHandle(value); RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability); @@ -694,6 +696,7 @@ JSTaggedValue BuiltinsPromise::Any(EcmaRuntimeCallInfo *argv) if (!iteratorRecord->GetDone()) { JSHandle resultHandle = JSHandle::Cast(result); JSHandle closeVal = JSIterator::IteratorClose(thread, iterator, resultHandle); + RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability); if (closeVal.GetTaggedValue().IsCompletionRecord()) { result = JSHandle(closeVal); RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability); @@ -759,6 +762,7 @@ JSHandle BuiltinsPromise::PerformPromiseAny(JSThread *thread, PropertyDescriptor msgDesc(thread, errorsValue, true, false, true); JSHandle errorTagged = JSHandle::Cast(error); JSTaggedValue::DefinePropertyOrThrow(thread, errorTagged, errorsKey, msgDesc); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(CompletionRecord, thread); // 3. Return ThrowCompletion(error). JSHandle errorCompletion( factory->NewCompletionRecord(CompletionRecordType::THROW, errorTagged)); @@ -876,6 +880,7 @@ JSTaggedValue BuiltinsPromise::AllSettled(EcmaRuntimeCallInfo *argv) if (!iteratorRecord->GetDone()) { JSHandle resultHandle = JSHandle::Cast(result); JSHandle closeVal = JSIterator::IteratorClose(thread, iterator, resultHandle); + RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability); if (closeVal.GetTaggedValue().IsCompletionRecord()) { result = JSHandle(closeVal); RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability); diff --git a/ecmascript/builtins/builtins_promise.h b/ecmascript/builtins/builtins_promise.h index aa44e32dbfeca01016b9c3a95889d3ebccde8bae..415bf6f361362291e69e54642119efb6521a9a23 100644 --- a/ecmascript/builtins/builtins_promise.h +++ b/ecmascript/builtins/builtins_promise.h @@ -23,6 +23,35 @@ #include "ecmascript/js_tagged_value.h" #include "ecmascript/object_factory.h" +// List of functions in Promise, excluding the '@@' propertiex. +// V(name, func, length, stubIndex) +// where BuiltinsPromise::func refers to the native implementation of Promise[name]. +// kungfu::BuiltinsStubCSigns::stubIndex refers to the builtin stub index, or INVALID if no stub available. +#define BUILTIN_PROMISE_FUNCTIONS(V) \ + /* Promise.all ( iterable ) */ \ + V("all", All, 1, INVALID) \ + /* Promise.allSettled ( iterable ) */ \ + V("allSettled", AllSettled, 1, INVALID) \ + /* Promise.any ( iterable ) */ \ + V("any", Any, 1, INVALID) \ + /* Promise.race ( iterable ) */ \ + V("race", Race, 1, INVALID) \ + /* Promise.reject ( r ) */ \ + V("reject", Reject, 1, INVALID) \ + /* Promise.resolve ( x ) */ \ + V("resolve", Resolve, 1, INVALID) + +// List of functions in Promise.prototype, excluding the constructor and '@@' properties. +// V(name, func, length, stubIndex) +// where BuiltinsPromise::func refers to the native implementation of Promise.prototype[name]. +#define BUILTIN_PROMISE_PROTOTYPE_FUNCTIONS(V) \ + /* Promise.prototype.catch ( onRejected ) */ \ + V("catch", Catch, 1, INVALID) \ + /* Promise.prototype.finally ( onFinally ) */ \ + V("finally", Finally, 1, INVALID) \ + /* Promise.prototype.then ( onFulfilled, onRejected ) */ \ + V("then", Then, 2, INVALID) + namespace panda::ecmascript::builtins { class BuiltinsPromise : public base::BuiltinsBase { public: @@ -56,7 +85,30 @@ public: static JSTaggedValue GetPromiseResolve(JSThread *thread, JSHandle promiseConstructor); + // Excluding the '@@' internal properties + static Span GetPromiseFunctions() + { + return Span(PROMISE_FUNCTIONS); + } + + // Excluding the constructor and '@@' internal properties. + static Span GetPromisePrototypeFunctions() + { + return Span(PROMISE_PROTOTYPE_FUNCTIONS); + } + private: +#define BUILTIN_PROMISE_FUNCTION_ENTRY(name, method, length, id) \ + base::BuiltinFunctionEntry::Create(name, BuiltinsPromise::method, length, kungfu::BuiltinsStubCSigns::id), + + static constexpr std::array PROMISE_FUNCTIONS = { + BUILTIN_PROMISE_FUNCTIONS(BUILTIN_PROMISE_FUNCTION_ENTRY) + }; + static constexpr std::array PROMISE_PROTOTYPE_FUNCTIONS = { + BUILTIN_PROMISE_PROTOTYPE_FUNCTIONS(BUILTIN_PROMISE_FUNCTION_ENTRY) + }; +#undef BUILTIN_PROMISE_FUNCTION_ENTRY + static JSTaggedValue PerformPromiseAll(JSThread *thread, const JSHandle &itRecord, const JSHandle &ctor, diff --git a/ecmascript/builtins/builtins_promise_handler.cpp b/ecmascript/builtins/builtins_promise_handler.cpp index d9fcc4e0b5b4544bbdfc03e13cdc99c5ce055bbe..74a7c561390f566175ef9343cf692633a2c21b36 100644 --- a/ecmascript/builtins/builtins_promise_handler.cpp +++ b/ecmascript/builtins/builtins_promise_handler.cpp @@ -274,8 +274,8 @@ JSTaggedValue BuiltinsPromiseHandler::ThenFinally(EcmaRuntimeCallInfo *argv) EcmaRuntimeCallInfo *taggedInfo = EcmaInterpreter::NewRuntimeCallInfo(thread, onFinally, undefined, undefined, 0); JSTaggedValue result = JSFunction::Call(taggedInfo); - JSHandle resultHandle(thread, result); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle resultHandle(thread, result); // 5. Let C be F.[[Constructor]]. // 6. Assert: IsConstructor(C) is true. JSHandle thenFinallyConstructor(thread, thenFinally->GetConstructor()); @@ -315,8 +315,8 @@ JSTaggedValue BuiltinsPromiseHandler::CatchFinally(EcmaRuntimeCallInfo *argv) EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, onFinally, undefined, undefined, 0); JSTaggedValue result = JSFunction::Call(info); - JSHandle resultHandle(thread, result); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle resultHandle(thread, result); // 5. Let C be F.[[Constructor]]. // 6. Assert: IsConstructor(C) is true. JSHandle catchFinallyConstructor(thread, catchFinally->GetConstructor()); @@ -361,6 +361,7 @@ JSHandle BuiltinsPromiseHandler::PromiseResolve(JSThread *thread, // 3. Let promiseCapability be ? NewPromiseCapability(C). // 4. ReturnIfAbrupt(promiseCapability) JSHandle promiseCapability = JSPromise::NewPromiseCapability(thread, constructor); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); JSHandle promiseCapaHandle = JSHandle::Cast(promiseCapability); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, promiseCapaHandle); // 6. Let resolveResult be Call(promiseCapability.[[Resolve]], undefined, «x»). @@ -415,10 +416,12 @@ JSTaggedValue BuiltinsPromiseHandler::AllSettledResolveElementFunction(EcmaRunti JSHandle statusKey = globalConst->GetHandledPromiseStatusString(); JSHandle fulfilledKey = globalConst->GetHandledPromiseFulfilledString(); JSObject::CreateDataPropertyOrThrow(thread, obj, statusKey, fulfilledKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 11. Perform ! CreateDataPropertyOrThrow(obj, "value", x). JSHandle valueKey = globalConst->GetHandledValueString(); JSHandle xValue = GetCallArg(argv, 0); JSObject::CreateDataPropertyOrThrow(thread, obj, valueKey, xValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 12. Set values[index] to obj. JSHandle arrayValues = JSHandle::Cast(JSHandle(thread, values->GetValue())); @@ -478,10 +481,12 @@ JSTaggedValue BuiltinsPromiseHandler::AllSettledRejectElementFunction(EcmaRuntim JSHandle statusKey = globalConst->GetHandledPromiseStatusString(); JSHandle rejectedKey = globalConst->GetHandledPromiseRejectedString(); JSObject::CreateDataPropertyOrThrow(thread, obj, statusKey, rejectedKey); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 11. Perform ! CreateDataPropertyOrThrow(obj, "reason", x). JSHandle xReason = GetCallArg(argv, 0); JSHandle reasonKey = globalConst->GetHandledPromiseReasonString(); JSObject::CreateDataPropertyOrThrow(thread, obj, reasonKey, xReason); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 12. Set values[index] to obj. JSHandle arrayValues = JSHandle::Cast(JSHandle(thread, values->GetValue())); @@ -550,6 +555,7 @@ JSTaggedValue BuiltinsPromiseHandler::AnyRejectElementFunction(EcmaRuntimeCallIn PropertyDescriptor msgDesc(thread, errorsValue, true, false, true); JSHandle errorTagged = JSHandle::Cast(error); JSTaggedValue::DefinePropertyOrThrow(thread, errorTagged, errorsKey, msgDesc); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // c. Return ? Call(promiseCapability.[[Reject]], undefined, « error »). JSHandle capaReject(thread, capa->GetReject()); JSHandle undefined(globalConst->GetHandledUndefined()); diff --git a/ecmascript/builtins/builtins_promise_job.cpp b/ecmascript/builtins/builtins_promise_job.cpp index 51ed2a87a4e8aa8fe797a80eee779d9ad4def9df..d4ddde6a04550dde7a1ea8d11bcb06c3810b6dca 100644 --- a/ecmascript/builtins/builtins_promise_job.cpp +++ b/ecmascript/builtins/builtins_promise_job.cpp @@ -15,7 +15,6 @@ #include "ecmascript/builtins/builtins_promise_job.h" -#include "ecmascript/base/path_helper.h" #include "ecmascript/ecma_macros.h" #include "ecmascript/global_env.h" #include "ecmascript/interpreter/interpreter.h" @@ -26,13 +25,16 @@ #include "ecmascript/js_promise.h" #include "ecmascript/js_tagged_value.h" #include "ecmascript/module/js_dynamic_import.h" +#include "ecmascript/module/js_module_deregister.h" #include "ecmascript/module/js_module_manager.h" +#include "ecmascript/module/module_path_helper.h" #include "ecmascript/platform/file.h" #include "ecmascript/require/js_cjs_module.h" #include "libpandabase/macros.h" namespace panda::ecmascript::builtins { -using PathHelper = base::PathHelper; +using JSRecordInfo = ecmascript::JSPandaFile::JSRecordInfo; + JSTaggedValue BuiltinsPromiseJob::PromiseReactionJob(EcmaRuntimeCallInfo *argv) { ASSERT(argv); @@ -51,10 +53,11 @@ JSTaggedValue BuiltinsPromiseJob::PromiseReactionJob(EcmaRuntimeCallInfo *argv) // 3. Let handler be reaction.[[Handler]]. JSHandle handler(thread, reaction->GetHandler()); JSHandle call(thread, capability->GetResolve()); - const int32_t argsLength = 1; + const uint32_t argsLength = 1; JSHandle undefined = globalConst->GetHandledUndefined(); EcmaRuntimeCallInfo *runtimeInfo = EcmaInterpreter::NewRuntimeCallInfo(thread, call, undefined, undefined, argsLength); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 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, @@ -68,6 +71,7 @@ JSTaggedValue BuiltinsPromiseJob::PromiseReactionJob(EcmaRuntimeCallInfo *argv) // 6. Else, let handlerResult be Call(handler, undefined, «argument»). EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, handler, undefined, undefined, argsLength); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); info->SetCallArg(argument.GetTaggedValue()); JSTaggedValue taggedValue = JSFunction::Call(info); // 7. If handlerResult is an abrupt completion, then @@ -101,7 +105,7 @@ JSTaggedValue BuiltinsPromiseJob::PromiseResolveThenableJob(EcmaRuntimeCallInfo JSHandle then = GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD); // 2. Let thenCallResult be Call(then, thenable, «resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]]»). - const int32_t argsLength = 2; // 2: «resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]]» + const uint32_t argsLength = 2; // 2: «resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]]» JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, then, thenable, undefined, argsLength); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -135,46 +139,46 @@ JSTaggedValue BuiltinsPromiseJob::DynamicImportJob(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle resolve(GetCallArg(argv, 0)); - JSHandle reject(GetCallArg(argv, 1)); // 1 : first argument - JSHandle dirPath(GetCallArg(argv, 2)); // 2 : second argument - JSHandle specifier(GetCallArg(argv, 3)); // 3 : third argument - JSHandle recordName(GetCallArg(argv, 4)); // 4 : fourth recordName + JSHandle reject(GetCallArg(argv, 1)); // 1 : reject method + JSHandle dirPath(GetCallArg(argv, 2)); // 2 : current file path(containing file name) + JSHandle specifier(GetCallArg(argv, 3)); // 3 : request module's path + JSHandle recordName(GetCallArg(argv, 4)); // 4 : js recordName or undefined // Let specifierString be Completion(ToString(specifier)) JSHandle specifierString = JSTaggedValue::ToString(thread, specifier); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, CatchException(thread, reject)); - JSHandle moduleName; + // Resolve request module's ohmurl + JSMutableHandle moduleName(thread, thread->GlobalConstants()->GetUndefined()); CString entryPoint = JSPandaFile::ENTRY_MAIN_FUNCTION; - CString baseFilename = ConvertToString(dirPath.GetTaggedValue()); - CString fileNameStr = ""; + CString fileNameStr = ConvertToString(dirPath.GetTaggedValue()); CString requestPath = ConvertToString(specifierString.GetTaggedValue()); + LOG_ECMA(DEBUG) << "Start importing dynamic module : " << requestPath; // resolve native module auto [isNative, moduleType] = SourceTextModule::CheckNativeModule(requestPath); + ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager(); if (isNative) { return DynamicImport::ExecuteNativeModule(thread, specifierString, moduleType, resolve, reject); } + if (recordName->IsUndefined()) { - moduleName = ResolveFilenameFromNative(thread, dirPath.GetTaggedValue(), - specifierString.GetTaggedValue()); + moduleName.Update(ResolveFilenameFromNative(thread, dirPath.GetTaggedValue(), + specifierString.GetTaggedValue()).GetTaggedValue()); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, CatchException(thread, reject)); fileNameStr = ConvertToString(moduleName.GetTaggedValue()); } else { CString recordNameStr = ConvertToString(recordName.GetTaggedValue()); std::shared_ptr jsPandaFile = - JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, baseFilename, recordNameStr.c_str()); + JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, fileNameStr, recordNameStr.c_str()); if (jsPandaFile == nullptr) { - CString msg = "Load file with filename '" + baseFilename + "' failed, recordName '" + recordNameStr + "'"; - JSTaggedValue error = factory->GetJSError(ErrorType::REFERENCE_ERROR, msg.c_str()).GetTaggedValue(); - THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, CatchException(thread, reject)); + LOG_FULL(FATAL) << "Load current file's panda file failed. Current file is " << recordNameStr; } - entryPoint = - PathHelper::ConcatFileNameWithMerge(thread, jsPandaFile.get(), baseFilename, recordNameStr, requestPath); + ModulePathHelper::ConcatFileNameWithMerge(thread, jsPandaFile.get(), + fileNameStr, recordNameStr, requestPath); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, CatchException(thread, reject)); - fileNameStr = baseFilename; - moduleName = vm->GetFactory()->NewFromUtf8(entryPoint); + moduleName.Update(factory->NewFromUtf8(entryPoint).GetTaggedValue()); } std::shared_ptr jsPandaFile = JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, fileNameStr, entryPoint); @@ -183,34 +187,45 @@ JSTaggedValue BuiltinsPromiseJob::DynamicImportJob(EcmaRuntimeCallInfo *argv) JSTaggedValue error = factory->GetJSError(ErrorType::REFERENCE_ERROR, msg.c_str()).GetTaggedValue(); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, CatchException(thread, reject)); } - bool isModule = jsPandaFile->IsModule(thread, entryPoint); - RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, CatchException(thread, reject)); - ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager(); - JSMutableHandle moduleNamespace(thread, JSTaggedValue::Undefined()); + + // Loading request module. if (!moduleManager->IsImportedModuleLoaded(moduleName.GetTaggedValue())) { if (!JSPandaFileExecutor::ExecuteFromFile(thread, fileNameStr.c_str(), entryPoint.c_str(), false, true)) { CString msg = "Cannot execute request dynamic-imported module : " + entryPoint; JSTaggedValue error = factory->GetJSError(ErrorType::REFERENCE_ERROR, msg.c_str()).GetTaggedValue(); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, CatchException(thread, reject)); } + } else { + ModuleDeregister::ReviseLoadedModuleCount(thread, moduleName.GetTaggedValue()); } RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, CatchException(thread, reject)); - if (!isModule) { + JSRecordInfo recordInfo; + bool hasRecord = jsPandaFile->CheckAndGetRecordInfo(entryPoint, recordInfo); + if (!hasRecord) { + LOG_FULL(ERROR) << "cannot find record '" << entryPoint <<"' in basefileName " << fileNameStr << "."; + CString msg = "cannot find record '" + entryPoint + "', please check the request path."; + THROW_REFERENCE_ERROR_AND_RETURN(thread, msg.c_str(), CatchException(thread, reject)); + } + JSMutableHandle moduleNamespace(thread, JSTaggedValue::Undefined()); + // only support importing es module, or return a default object. + if (!jsPandaFile->IsModule(recordInfo)) { moduleNamespace.Update(vm->GetGlobalEnv()->GetExportOfScript()); } else { // b. Let moduleRecord be ! HostResolveImportedModule(referencingScriptOrModule, specifier). JSHandle moduleRecord = moduleManager->HostGetImportedModule(moduleName.GetTaggedValue()); - // d. Let namespace be ? GetModuleNamespace(moduleRecord). - moduleNamespace.Update(SourceTextModule::GetModuleNamespace(thread, moduleRecord)); + JSHandle nameSp = SourceTextModule::GetModuleNamespace(thread, moduleRecord); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, CatchException(thread, reject)); + // d. Let namespace be ? GetModuleNamespace(moduleRecord). + moduleNamespace.Update(nameSp); } JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, JSHandle(resolve), undefined, undefined, 1); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, CatchException(thread, reject)); info->SetCallArg(moduleNamespace.GetTaggedValue()); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return JSFunction::Call(info); diff --git a/ecmascript/builtins/builtins_proxy.cpp b/ecmascript/builtins/builtins_proxy.cpp index fa7f52944d1807b2d822b64e11ed23f22e430d36..7753f81e827d458868fabac35faf6257053e6363 100644 --- a/ecmascript/builtins/builtins_proxy.cpp +++ b/ecmascript/builtins/builtins_proxy.cpp @@ -85,12 +85,8 @@ JSTaggedValue BuiltinsProxy::InvalidateProxyFunction(EcmaRuntimeCallInfo *argv) JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); - JSHandle revokeObj(GetThis(argv)); - JSHandle revokeKey = thread->GlobalConstants()->GetHandledRevokeString(); - - PropertyDescriptor desc(thread); - JSObject::GetOwnProperty(thread, revokeObj, revokeKey, desc); - JSProxyRevocFunction::ProxyRevocFunctions(thread, JSHandle(desc.GetValue())); + JSHandle proxy = GetConstructor(argv); + JSProxyRevocFunction::ProxyRevocFunctions(thread, JSHandle(proxy)); return JSTaggedValue::Undefined(); } } // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_reflect.cpp b/ecmascript/builtins/builtins_reflect.cpp index 05f037fac98cceb5537885d3656ceec6c5b25007..0cd4459a717734a5d708fde7e3144b5cdcbc2016 100644 --- a/ecmascript/builtins/builtins_reflect.cpp +++ b/ecmascript/builtins/builtins_reflect.cpp @@ -40,7 +40,7 @@ JSTaggedValue BuiltinsReflect::ReflectApply(EcmaRuntimeCallInfo *argv) // 3. Perform PrepareForTailCall(). // 4. Return ? Call(target, thisArgument, args). - const int32_t argsLength = static_cast(args->GetLength()); + const uint32_t argsLength = args->GetLength(); JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, target, thisArgument, undefined, argsLength); @@ -75,7 +75,7 @@ JSTaggedValue BuiltinsReflect::ReflectConstruct(EcmaRuntimeCallInfo *argv) RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle args = JSHandle::Cast(argOrAbrupt); // 5. Return ? Construct(target, args, newTarget). - const int32_t argsLength = static_cast(args->GetLength()); + const uint32_t argsLength = args->GetLength(); JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, target, undefined, newTarget, argsLength); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); diff --git a/ecmascript/builtins/builtins_reflect.h b/ecmascript/builtins/builtins_reflect.h index 9c2e926c5883a5bbc06946a3782aa5c3ccd326f6..3c8b9b3397d8ff0b1e227aa0bca72cf62acc63e4 100644 --- a/ecmascript/builtins/builtins_reflect.h +++ b/ecmascript/builtins/builtins_reflect.h @@ -20,6 +20,38 @@ #include "ecmascript/js_function.h" #include "ecmascript/js_array.h" +// List of functions in Reflect, excluding the '@@' properties. +// V(name, func, length, stubIndex) +// where BuiltinsRefject::func refers to the native implementation of Reflect[name]. +// kungfu::BuiltinsStubCSigns::stubIndex refers to the builtin stub index, or INVALID if no stub available. +#define BUILTIN_REFLECT_FUNCTIONS(V) \ + /* Reflect.apply ( target, thisArgument, argumentsList ) */ \ + V("apply", ReflectApply, 3, INVALID) \ + /* Reflect.construct ( target, argumentsList [ , newTarget ] ) */ \ + V("construct", ReflectConstruct, 2, INVALID) \ + /* Reflect.defineProperty ( target, propertyKey, attributes ) */ \ + V("defineProperty", ReflectDefineProperty, 3, INVALID) \ + /* Reflect.deleteProperty ( target, propertyKey ) */ \ + V("deleteProperty", ReflectDeleteProperty, 2, INVALID) \ + /* Reflect.get ( target, propertyKey [ , receiver ] ) */ \ + V("get", ReflectGet, 2, INVALID) \ + /* Reflect.getOwnPropertyDescriptor ( target, propertyKey ) */ \ + V("getOwnPropertyDescriptor", ReflectGetOwnPropertyDescriptor, 2, INVALID) \ + /* Reflect.getPrototypeOf ( target ) */ \ + V("getPrototypeOf", ReflectGetPrototypeOf, 1, INVALID) \ + /* Reflect.has ( target, propertyKey ) */ \ + V("has", ReflectHas, 2, INVALID) \ + /* Reflect.isExtensible ( target ) */ \ + V("isExtensible", ReflectIsExtensible, 1, INVALID) \ + /* Reflect.ownKeys ( target ) */ \ + V("ownKeys", ReflectOwnKeys, 1, INVALID) \ + /* Reflect.preventExtensions ( target ) */ \ + V("preventExtensions", ReflectPreventExtensions, 1, INVALID) \ + /* Reflect.set ( target, propertyKey, V [ , receiver ] ) */ \ + V("set", ReflectSet, 3, INVALID) \ + /* Reflect.setPrototypeOf ( target, proto ) */ \ + V("setPrototypeOf", ReflectSetPrototypeOf, 2, INVALID) + namespace panda::ecmascript::builtins { class BuiltinsReflect : public base::BuiltinsBase { public: @@ -61,6 +93,21 @@ public: // ecma 26.1.13 static JSTaggedValue ReflectSetPrototypeOf(EcmaRuntimeCallInfo *argv); + + // Excluding the '@@' internal properties. + static Span GetReflectFunctions() + { + return Span(REFLECT_FUNCTIONS); + } + +private: +#define BUILTINS_REFLECT_FUNCTION_ENTRY(name, method, length, id) \ + base::BuiltinFunctionEntry::Create(name, BuiltinsReflect::method, length, kungfu::BuiltinsStubCSigns::id), + + static constexpr std::array REFLECT_FUNCTIONS = { + BUILTIN_REFLECT_FUNCTIONS(BUILTINS_REFLECT_FUNCTION_ENTRY) + }; +#undef BUILTINS_REFLECT_FUNCTION_ENTRY }; } // namespace panda::ecmascript::builtins #endif // ECMASCRIPT_BUILTINS_BUILTINS_REFLECT_H diff --git a/ecmascript/builtins/builtins_regexp.cpp b/ecmascript/builtins/builtins_regexp.cpp index 1fe67fe5e7826da72bf36adb0717b9ccde4cce0b..17db705206083090b4a5fd316a5243db0d2981dc 100644 --- a/ecmascript/builtins/builtins_regexp.cpp +++ b/ecmascript/builtins/builtins_regexp.cpp @@ -95,6 +95,7 @@ JSTaggedValue BuiltinsRegExp::RegExpConstructor(EcmaRuntimeCallInfo *argv) // 5.c Else, let F be flags. flagsTemp = JSHandle(thread, *JSTaggedValue::ToString(thread, flags)); } + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 6. Else if patternIsRegExp is true } else if (patternIsRegExp) { JSHandle sourceString(globalConst->GetHandledSourceString()); @@ -114,6 +115,7 @@ JSTaggedValue BuiltinsRegExp::RegExpConstructor(EcmaRuntimeCallInfo *argv) } else { // 6.d Else, let F be flags. flagsTemp = JSHandle(thread, *JSTaggedValue::ToString(thread, flags)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); } } else { // 7.a Let P be pattern. @@ -123,6 +125,7 @@ JSTaggedValue BuiltinsRegExp::RegExpConstructor(EcmaRuntimeCallInfo *argv) flagsTemp = flags; } else { flagsTemp = JSHandle(thread, *JSTaggedValue::ToString(thread, flags)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); } } // 8. Let O be RegExpAlloc(newTarget). @@ -268,6 +271,18 @@ JSTaggedValue BuiltinsRegExp::GetGlobal(EcmaRuntimeCallInfo *argv) return GetTaggedBoolean(result); } +// 22.2.6.6 +JSTaggedValue BuiltinsRegExp::GetHasIndices(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, RegExp, GetHasIndices); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle thisObj = GetThis(argv); + bool result = GetFlagsInternal(thread, thisObj, RegExpParser::FLAG_HASINDICES); + return GetTaggedBoolean(result); +} + // 20.2.5.5 JSTaggedValue BuiltinsRegExp::GetIgnoreCase(EcmaRuntimeCallInfo *argv) { @@ -377,6 +392,7 @@ JSTaggedValue BuiltinsRegExp::Match(EcmaRuntimeCallInfo *argv) // 3. Let S be ToString(string) JSHandle inputString = GetCallArg(argv, 0); JSHandle stringHandle = JSTaggedValue::ToString(thread, inputString); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); bool useCache = true; JSHandle cacheTable(thread->GetCurrentEcmaContext()->GetRegExpCache()); if (cacheTable->GetLargeStrCount() == 0 || cacheTable->GetConflictCount() == 0) { @@ -410,7 +426,8 @@ JSTaggedValue BuiltinsRegExp::Match(EcmaRuntimeCallInfo *argv) // a. Return RegExpExec(rx, S). if (useCache) { JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, pattern, flags, inputString, - RegExpExecResultCache::EXEC_TYPE, thisObj); + RegExpExecResultCache::EXEC_TYPE, thisObj, + JSTaggedValue(0)); if (!cacheResult.IsUndefined()) { return cacheResult; } @@ -421,7 +438,8 @@ JSTaggedValue BuiltinsRegExp::Match(EcmaRuntimeCallInfo *argv) if (useCache) { JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, pattern, flags, inputString, - RegExpExecResultCache::MATCH_TYPE, thisObj); + RegExpExecResultCache::MATCH_TYPE, thisObj, + JSTaggedValue(0)); if (!cacheResult.IsUndefined()) { return cacheResult; } @@ -444,6 +462,7 @@ JSTaggedValue BuiltinsRegExp::Match(EcmaRuntimeCallInfo *argv) RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // e. Let A be ArrayCreate(0). JSHandle array(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // f. Let n be 0. int resultNum = 0; JSMutableHandle result(thread, JSTaggedValue(0)); @@ -463,7 +482,7 @@ JSTaggedValue BuiltinsRegExp::Match(EcmaRuntimeCallInfo *argv) if (useCache) { RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flags, inputString, JSHandle(array), - RegExpExecResultCache::MATCH_TYPE, 0); + RegExpExecResultCache::MATCH_TYPE, 0, 0); } // 2. Else, return A. return array.GetTaggedValue(); @@ -544,8 +563,8 @@ JSTaggedValue BuiltinsRegExp::MatchAll(EcmaRuntimeCallInfo *argv) RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); runtimeInfo->SetCallArg(thisObj.GetTaggedValue(), flagsStrHandle.GetTaggedValue()); JSTaggedValue taggedMatcher = JSFunction::Construct(runtimeInfo); - JSHandle matcherHandle(thread, taggedMatcher); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle matcherHandle(thread, taggedMatcher); // 7. Let lastIndex be ? ToLength(? Get(R, "lastIndex")). JSHandle lastIndexString(globalConstants->GetHandledLastIndexString()); @@ -628,9 +647,11 @@ JSTaggedValue BuiltinsRegExp::RegExpReplaceFast(JSThread *thread, JSHandle cacheTable->GetStrLenThreshold()) { useCache = true; } + uint32_t lastIndexInput = lastIndex; if (useCache) { JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, pattern, flagsBits, tagInputString, RegExpExecResultCache::REPLACE_TYPE, regexp, + JSTaggedValue(lastIndexInput), globalConst->GetEmptyString()); if (!cacheResult.IsUndefined()) { return cacheResult; @@ -692,7 +713,7 @@ JSTaggedValue BuiltinsRegExp::RegExpReplaceFast(JSThread *thread, JSHandle(resultValue), - RegExpExecResultCache::REPLACE_TYPE, lastIndex, + RegExpExecResultCache::REPLACE_TYPE, lastIndexInput, lastIndex, globalConst->GetEmptyString()); } return resultValue.GetTaggedValue(); @@ -715,6 +736,14 @@ JSTaggedValue BuiltinsRegExp::Replace(EcmaRuntimeCallInfo *argv) // 3. Let S be ToString(string). JSHandle string = GetCallArg(argv, 0); JSHandle inputReplaceValue = GetCallArg(argv, 1); + return ReplaceInternal(thread, thisObj, string, inputReplaceValue); +} + +JSTaggedValue BuiltinsRegExp::ReplaceInternal(JSThread *thread, + JSHandle thisObj, + JSHandle string, + JSHandle inputReplaceValue) +{ JSHandle srcString = JSTaggedValue::ToString(thread, string); const GlobalEnvConstants *globalConst = thread->GlobalConstants(); @@ -760,21 +789,25 @@ JSTaggedValue BuiltinsRegExp::Replace(EcmaRuntimeCallInfo *argv) // Add cache for regexp replace bool useCache = false; + // Add cache for the intermediate result of replace + bool useIntermediateCache = false; JSMutableHandle pattern(thread, JSTaggedValue::Undefined()); JSMutableHandle flagsBits(thread, JSTaggedValue::Undefined()); JSHandle cacheTable(thread->GetCurrentEcmaContext()->GetRegExpCache()); - if (isGlobal && !functionalReplace && thisObj->IsJSRegExp()) { + if (isGlobal && thisObj->IsJSRegExp()) { JSHClass *hclass = JSHandle::Cast(thisObj)->GetJSHClass(); JSHClass *originHClass = JSHClass::Cast(globalConst->GetJSRegExpClass().GetTaggedObject()); if (hclass == originHClass) { - if (EcmaStringAccessor(replaceValueHandle).GetLength() == 0) { + if (!functionalReplace && EcmaStringAccessor(replaceValueHandle).GetLength() == 0) { return RegExpReplaceFast(thread, thisObj, srcString, length); - } else { - JSHandle regexpHandle(thisObj); - if (regexpHandle->IsJSRegExp()) { - pattern.Update(regexpHandle->GetOriginalSource()); - flagsBits.Update(regexpHandle->GetOriginalFlags()); - } + } + JSHandle regexpHandle(thisObj); + if (regexpHandle->IsJSRegExp()) { + useIntermediateCache = true; + pattern.Update(regexpHandle->GetOriginalSource()); + flagsBits.Update(regexpHandle->GetOriginalFlags()); + } + if (!functionalReplace) { uint32_t strLength = EcmaStringAccessor(replaceValueHandle).GetLength(); uint32_t largeStrCount = cacheTable->GetLargeStrCount(); if (largeStrCount != 0) { @@ -788,7 +821,7 @@ JSTaggedValue BuiltinsRegExp::Replace(EcmaRuntimeCallInfo *argv) useCache = true; JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, pattern, flagsBits, string, RegExpExecResultCache::REPLACE_TYPE, - thisObj, + thisObj, JSTaggedValue(0), inputReplaceValue.GetTaggedValue()); if (!cacheResult.IsUndefined()) { return cacheResult; @@ -800,57 +833,75 @@ JSTaggedValue BuiltinsRegExp::Replace(EcmaRuntimeCallInfo *argv) JSHandle matchedStr = globalConst->GetHandledZeroString(); // 11. Let results be a new empty List. - JSHandle resultsList(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + JSMutableHandle resultsList(thread, JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); int resultsIndex = 0; - // 12. Let done be false. - // 13. Repeat, while done is false JSMutableHandle nextIndexHandle(thread, JSTaggedValue(0)); JSMutableHandle execResult(thread, JSTaggedValue(0)); - for (;;) { - // a. Let result be RegExpExec(rx, S). - execResult.Update(RegExpExec(thread, thisObj, inputStr, useCache)); - // b. ReturnIfAbrupt(result). - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - // c. If result is null, set done to true. - if (execResult->IsNull()) { - break; - } - // d. Else result is not null, i. Append result to the end of results. - JSObject::CreateDataProperty(thread, resultsList, resultsIndex, execResult); - resultsIndex++; - // ii. If global is false, set done to true. - if (!isGlobal) { - break; - } - // iii. Else, 1. Let matchStr be ToString(Get(result, "0")). - JSTaggedValue getMatchVal = ObjectFastOperator::FastGetPropertyByValue( - thread, execResult.GetTaggedValue(), matchedStr.GetTaggedValue()); - JSHandle getMatch(thread, getMatchVal); - JSHandle matchString = JSTaggedValue::ToString(thread, getMatch); - // 2. ReturnIfAbrupt(matchStr). - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - // 3. If matchStr is the empty String, then - if (EcmaStringAccessor(matchString).GetLength() == 0) { - // a. Let thisIndex be ToLength(Get(rx, "lastIndex")). - JSTaggedValue thisIndexVal = ObjectFastOperator::FastGetPropertyByValue( - thread, thisObj.GetTaggedValue(), lastIndex.GetTaggedValue()); - JSHandle thisIndexHandle(thread, thisIndexVal); - uint32_t thisIndex = 0; - if (thisIndexHandle->IsInt()) { - thisIndex = static_cast(thisIndexHandle->GetInt()); - } else { - thisIndex = JSTaggedValue::ToLength(thread, thisIndexHandle).GetNumber(); - // b. ReturnIfAbrupt(thisIndex). - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // Add cache for the intermediate result of replace + JSTaggedValue cachedResultsList(JSTaggedValue::VALUE_UNDEFINED); + if (useIntermediateCache) { + cachedResultsList = cacheTable->FindCachedResult(thread, pattern, flagsBits, string, + RegExpExecResultCache::INTERMEDIATE_REPLACE_TYPE, + thisObj, JSTaggedValue(0)); + } + if (!cachedResultsList.IsUndefined()) { + resultsList.Update(cachedResultsList); + resultsIndex = JSArray::Cast(resultsList.GetTaggedValue())->GetArrayLength(); + } else { + // 12. Let done be false. + // 13. Repeat, while done is false + for (;;) { + // a. Let result be RegExpExec(rx, S). + execResult.Update(RegExpExec(thread, thisObj, inputStr, useCache)); + // b. ReturnIfAbrupt(result). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // c. If result is null, set done to true. + if (execResult->IsNull()) { + break; } - // c. Let nextIndex be AdvanceStringIndex(S, thisIndex, fullUnicode). - uint32_t nextIndex = AdvanceStringIndex(inputStr, thisIndex, fullUnicode); - nextIndexHandle.Update(JSTaggedValue(nextIndex)); - // d. Let setStatus be Set(rx, "lastIndex", nextIndex, true). - ObjectFastOperator::FastSetPropertyByValue(thread, thisObj.GetTaggedValue(), lastIndex.GetTaggedValue(), - nextIndexHandle.GetTaggedValue()); - // e. ReturnIfAbrupt(setStatus). + // d. Else result is not null, i. Append result to the end of results. + JSObject::CreateDataProperty(thread, resultsList, resultsIndex, execResult); + resultsIndex++; + // ii. If global is false, set done to true. + if (!isGlobal) { + break; + } + // iii. Else, 1. Let matchStr be ToString(Get(result, "0")). + JSTaggedValue getMatchVal = ObjectFastOperator::FastGetPropertyByValue( + thread, execResult.GetTaggedValue(), matchedStr.GetTaggedValue()); + JSHandle getMatch(thread, getMatchVal); + JSHandle matchString = JSTaggedValue::ToString(thread, getMatch); + // 2. ReturnIfAbrupt(matchStr). RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. If matchStr is the empty String, then + if (EcmaStringAccessor(matchString).GetLength() == 0) { + // a. Let thisIndex be ToLength(Get(rx, "lastIndex")). + JSTaggedValue thisIndexVal = ObjectFastOperator::FastGetPropertyByValue( + thread, thisObj.GetTaggedValue(), lastIndex.GetTaggedValue()); + JSHandle thisIndexHandle(thread, thisIndexVal); + uint32_t thisIndex = 0; + if (thisIndexHandle->IsInt()) { + thisIndex = static_cast(thisIndexHandle->GetInt()); + } else { + thisIndex = JSTaggedValue::ToLength(thread, thisIndexHandle).GetNumber(); + // b. ReturnIfAbrupt(thisIndex). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + // c. Let nextIndex be AdvanceStringIndex(S, thisIndex, fullUnicode). + uint32_t nextIndex = AdvanceStringIndex(inputStr, thisIndex, fullUnicode); + nextIndexHandle.Update(JSTaggedValue(nextIndex)); + // d. Let setStatus be Set(rx, "lastIndex", nextIndex, true). + ObjectFastOperator::FastSetPropertyByValue(thread, thisObj.GetTaggedValue(), lastIndex.GetTaggedValue(), + nextIndexHandle.GetTaggedValue()); + // e. ReturnIfAbrupt(setStatus). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + } + if (useIntermediateCache) { + RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flagsBits, string, + JSHandle(resultsList), + RegExpExecResultCache::INTERMEDIATE_REPLACE_TYPE, 0, 0); } } // 14. Let accumulatedResult be the empty String value. @@ -864,6 +915,7 @@ JSTaggedValue BuiltinsRegExp::Replace(EcmaRuntimeCallInfo *argv) // 16. Repeat, for each result in results, for (int i = 0; i < resultsIndex; i++) { resultValues.Update(ObjectFastOperator::FastGetPropertyByIndex(thread, resultsList.GetTaggedValue(), i)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // a. Let nCaptures be ToLength(Get(result, "length")). JSHandle lengthHandle = globalConst->GetHandledLengthString(); ncapturesHandle.Update(ObjectFastOperator::FastGetPropertyByValue( @@ -954,13 +1006,14 @@ JSTaggedValue BuiltinsRegExp::Replace(EcmaRuntimeCallInfo *argv) replacerArgs->Set(thread, index + 3, namedCaptures.GetTaggedValue()); // 3: position of groups } // iv. Let replValue be Call(replaceValue, undefined, replacerArgs). - const int32_t argsLength = static_cast(replacerArgs->GetLength()); + const uint32_t argsLength = replacerArgs->GetLength(); JSHandle undefined = globalConst->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, inputReplaceValue, undefined, undefined, argsLength); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); info->SetCallArg(argsLength, replacerArgs); JSTaggedValue replaceResult = JSFunction::Call(info); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle replValue(thread, replaceResult); // v. Let replacement be ToString(replValue). JSHandle replacementString = JSTaggedValue::ToString(thread, replValue); @@ -1000,7 +1053,7 @@ JSTaggedValue BuiltinsRegExp::Replace(EcmaRuntimeCallInfo *argv) if (useCache) { RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flagsBits, string, JSHandle(resultValue), - RegExpExecResultCache::REPLACE_TYPE, nextIndexHandle->GetInt(), + RegExpExecResultCache::REPLACE_TYPE, 0, nextIndexHandle->GetInt(), inputReplaceValue.GetTaggedValue()); } return resultValue.GetTaggedValue(); @@ -1014,7 +1067,7 @@ JSTaggedValue BuiltinsRegExp::Replace(EcmaRuntimeCallInfo *argv) if (useCache) { RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flagsBits, string, JSHandle(resultValue), - RegExpExecResultCache::REPLACE_TYPE, nextIndexHandle->GetInt(), + RegExpExecResultCache::REPLACE_TYPE, 0, nextIndexHandle->GetInt(), inputReplaceValue.GetTaggedValue()); } return resultValue.GetTaggedValue(); @@ -1154,7 +1207,8 @@ JSTaggedValue BuiltinsRegExp::Split(EcmaRuntimeCallInfo *argv) JSHandle cacheTable(thread->GetCurrentEcmaContext()->GetRegExpCache()); if (useCache) { JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, pattern, flagsBits, inputString, - RegExpExecResultCache::SPLIT_TYPE, thisObj); + RegExpExecResultCache::SPLIT_TYPE, thisObj, + JSTaggedValue(0)); if (!cacheResult.IsUndefined()) { return cacheResult; } @@ -1174,6 +1228,7 @@ JSTaggedValue BuiltinsRegExp::Split(EcmaRuntimeCallInfo *argv) JSHandle splitter(thread, taggedSplitter); // 15. Let A be ArrayCreate(0). JSHandle array(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 16. Let lengthA be 0. uint32_t aLength = 0; @@ -1248,7 +1303,7 @@ JSTaggedValue BuiltinsRegExp::Split(EcmaRuntimeCallInfo *argv) if (useCache) { RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flagsBits, inputString, JSHandle(array), - RegExpExecResultCache::SPLIT_TYPE, lastIndex); + RegExpExecResultCache::SPLIT_TYPE, 0, lastIndex); } return array.GetTaggedValue(); } @@ -1283,7 +1338,7 @@ JSTaggedValue BuiltinsRegExp::Split(EcmaRuntimeCallInfo *argv) if (useCache) { RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flagsBits, inputString, JSHandle(array), - RegExpExecResultCache::SPLIT_TYPE, lastIndex); + RegExpExecResultCache::SPLIT_TYPE, 0, lastIndex); } return array.GetTaggedValue(); } @@ -1305,7 +1360,7 @@ JSTaggedValue BuiltinsRegExp::Split(EcmaRuntimeCallInfo *argv) if (lim == MAX_SPLIT_LIMIT) { RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flagsBits, inputString, JSHandle(array), RegExpExecResultCache::SPLIT_TYPE, - endIndex); + 0, endIndex); } // 28. Return A. return array.GetTaggedValue(); @@ -1322,7 +1377,7 @@ RegExpExecutor::MatchResult BuiltinsRegExp::Matcher(JSThread *thread, const JSHa void *dynBuf = JSNativePointer::Cast(bufferData.GetTaggedObject())->GetExternalPointer(); auto bytecodeBuffer = reinterpret_cast(dynBuf); // execute - Chunk chunk(thread->GetNativeAreaAllocator()); + RegExpCachedChunk chunk(thread); RegExpExecutor executor(&chunk); if (lastIndex < 0) { lastIndex = 0; @@ -1388,6 +1443,69 @@ bool BuiltinsRegExp::GetFlagsInternal(JSThread *thread, const JSHandle(regexpObj->GetOriginalFlags().GetInt()); return flags & mask; } + +// 22.2.7.8 +JSHandle BuiltinsRegExp::MakeMatchIndicesIndexPairArray(JSThread *thread, + const std::vector>& indices, + const std::vector>& groupNames, bool hasGroups) +{ + // 1. Let n be the number of elements in indices. + uint32_t n = indices.size(); + // Assert: groupNames has n - 1 elements. + ASSERT(groupNames.size() == n - 1); + // 5. Let A be ! ArrayCreate(n). + JSHandle results(JSArray::ArrayCreate(thread, JSTaggedNumber(n))); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // 6. If hasGroups is true, then + // a. Let groups be OrdinaryObjectCreate(null). + // 7. Else, + // a. Let groups be undefined. + JSMutableHandle groups(thread, JSTaggedValue::Undefined()); + if (hasGroups) { + JSHandle nullHandle(thread, JSTaggedValue::Null()); + JSHandle nullObj = factory->OrdinaryNewJSObjectCreate(nullHandle); + groups.Update(nullObj.GetTaggedValue()); + } + // 8. Perform ! CreateDataPropertyOrThrow(A, "groups", groups). + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle groupsKey = globalConst->GetHandledGroupsString(); + JSObject::CreateDataProperty(thread, results, groupsKey, groups); + // 9. For each integer i such that 0 ≤ i < n, in ascending order, do + // a. Let matchIndices be indices[i]. + // b. If matchIndices is not undefined, then + // i. Let matchIndexPair be GetMatchIndexPair(S, matchIndices). + // c. Else, + // i. Let matchIndexPair be undefined. + // d. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(i)), matchIndexPair). + // e. If i > 0 and groupNames[i - 1] is not undefined, then + // i. Assert: groups is not undefined. + // ii. Perform ! CreateDataPropertyOrThrow(groups, groupNames[i - 1], matchIndexPair). + JSMutableHandle matchIndexPair(thread, JSTaggedValue::Undefined()); + for (uint32_t i = 0; i < n; i++) { + std::pair matchIndices = indices[i]; + if (!matchIndices.first.IsUndefined()) { + JSHandle match = factory->NewTaggedArray(2); // 2 means the length of array + match->Set(thread, 0, matchIndices.first); + match->Set(thread, 1, matchIndices.second); + JSHandle pair(JSArray::CreateArrayFromList(thread, JSHandle::Cast(match))); + matchIndexPair.Update(pair.GetTaggedValue()); + } else { + matchIndexPair.Update(JSTaggedValue::Undefined()); + } + JSObject::CreateDataProperty(thread, results, i, matchIndexPair); + if (i > 0) { + JSHandle groupName = groupNames[i - 1]; + if (!groupName->IsUndefined()) { + JSHandle groupObject = JSHandle::Cast(groups); + JSObject::CreateDataProperty(thread, groupObject, groupName, matchIndexPair); + } + } + } + // 10. Return A. + return JSHandle::Cast(results); +} + // 21.2.5.2.2 JSTaggedValue BuiltinsRegExp::RegExpBuiltinExec(JSThread *thread, const JSHandle ®exp, const JSHandle &inputStr, bool useCache) @@ -1409,23 +1527,28 @@ JSTaggedValue BuiltinsRegExp::RegExpBuiltinExec(JSThread *thread, const JSHandle lastIndex = lastIndexNumber.GetNumber(); } - JSHandle globalHandle = globalConst->GetHandledGlobalString(); - bool global = ObjectFastOperator::FastGetPropertyByValue( - thread, regexp.GetTaggedValue(), globalHandle.GetTaggedValue()).ToBoolean(); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - JSHandle stickyHandle = globalConst->GetHandledStickyString(); - bool sticky = ObjectFastOperator::FastGetPropertyByValue( - thread, regexp.GetTaggedValue(), stickyHandle.GetTaggedValue()).ToBoolean(); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - if (!global && !sticky) { - lastIndex = 0; - } - JSHandle regexpObj(regexp); JSMutableHandle pattern(thread, regexpObj->GetOriginalSource()); JSMutableHandle flags(thread, regexpObj->GetOriginalFlags()); - JSHandle cacheTable(thread->GetCurrentEcmaContext()->GetRegExpCache()); + + uint8_t flagsBits = static_cast(flags->GetInt()); + bool global = (flagsBits & RegExpParser::FLAG_GLOBAL) != 0; + bool sticky = (flagsBits & RegExpParser::FLAG_STICKY) != 0; + bool hasIndices = (flagsBits & RegExpParser::FLAG_HASINDICES) != 0; + if (!global && !sticky) { + lastIndex = 0; + } + uint32_t lastIndexInput = static_cast(lastIndex); + if (useCache) { + JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, pattern, flags, inputStr, + RegExpExecResultCache::EXEC_TYPE, regexp, + JSTaggedValue(lastIndexInput)); + if (!cacheResult.IsUndefined()) { + return cacheResult; + } + } + uint32_t length = EcmaStringAccessor(inputStr->GetTaggedObject()).GetLength(); if (lastIndex > static_cast(length)) { ObjectFastOperator::FastSetPropertyByValue(thread, regexp.GetTaggedValue(), lastIndexHandle.GetTaggedValue(), @@ -1434,6 +1557,7 @@ JSTaggedValue BuiltinsRegExp::RegExpBuiltinExec(JSThread *thread, const JSHandle return JSTaggedValue::Null(); } JSHandle inputString = JSTaggedValue::ToString(thread, inputStr); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); bool isUtf16 = EcmaStringAccessor(inputString).IsUtf16(); auto inputPtr = EcmaStringAccessor(inputString).ToOneByteDataForced(); const uint8_t *strBuffer = inputPtr.get(); @@ -1459,6 +1583,7 @@ JSTaggedValue BuiltinsRegExp::RegExpBuiltinExec(JSThread *thread, const JSHandle } uint32_t capturesSize = matchResult.captures_.size(); JSHandle results(JSArray::ArrayCreate(thread, JSTaggedNumber(capturesSize))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); uint32_t matchIndex = matchResult.index_; // 24. Perform CreateDataProperty(A, "index", matchIndex). JSHandle indexKey = globalConst->GetHandledIndexString(); @@ -1466,44 +1591,58 @@ JSTaggedValue BuiltinsRegExp::RegExpBuiltinExec(JSThread *thread, const JSHandle JSObject::CreateDataProperty(thread, results, indexKey, indexValue); // 25. Perform CreateDataProperty(A, "input", S). JSHandle inputKey = globalConst->GetHandledInputString(); - JSHandle inputValue(thread, static_cast(inputStr->GetTaggedObject())); JSObject::CreateDataProperty(thread, results, inputKey, inputValue); + // 27. Perform CreateDataProperty(A, "0", matched_substr). - JSHandle zeroValue(matchResult.captures_[0].second); + JSHandle zeroValue(matchResult.captures_[0].second.capturedValue); JSObject::CreateDataProperty(thread, results, 0, zeroValue); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + // Let indices be a new empty List. + // Let groupNames be a new empty List. + // Append match to indices. + std::vector> indices; + std::vector> groupNames; + indices.emplace_back(std::make_pair(JSTaggedValue(matchIndex), JSTaggedValue(endIndex))); + // If R contains any GroupName, then + // a. Let groups be OrdinaryObjectCreate(null). + // b. Let hasGroups be true. + // Else, + // a. Let groups be undefined. + // b. Let hasGroups be false. JSHandle groupName(thread, regexpObj->GetGroupName()); JSMutableHandle groups(thread, JSTaggedValue::Undefined()); + bool hasGroups = false; if (!groupName->IsUndefined()) { JSHandle nullHandle(thread, JSTaggedValue::Null()); JSHandle nullObj = factory->OrdinaryNewJSObjectCreate(nullHandle); groups.Update(nullObj.GetTaggedValue()); + hasGroups = true; } + // Perform ! CreateDataPropertyOrThrow(A, "groups", groups). JSHandle groupsKey = globalConst->GetHandledGroupsString(); JSObject::CreateDataProperty(thread, results, groupsKey, groups); // Create a new RegExp on global - JSHandle globalRegExp = JSHandle(env->GetRegExpFunction()); - JSMutableHandle keyString(thread, JSTaggedValue::Undefined()); uint32_t captureIndex = 1; + JSHandle undefined = globalConst->GetHandledUndefined(); + JSHandle globalTable(thread->GetCurrentEcmaContext()->GetRegExpGlobalResult()); // 28. For each integer i such that i > 0 and i <= n for (; captureIndex < capturesSize; captureIndex++) { // a. Let capture_i be ith element of r's captures List JSTaggedValue capturedValue; if (matchResult.captures_[captureIndex].first) { capturedValue = JSTaggedValue::Undefined(); + indices.emplace_back(std::make_pair(JSTaggedValue::Undefined(), JSTaggedValue::Undefined())); } else { - capturedValue = matchResult.captures_[captureIndex].second.GetTaggedValue(); + auto captureI = matchResult.captures_[captureIndex].second; + capturedValue = captureI.capturedValue.GetTaggedValue(); + indices.emplace_back(std::make_pair(JSTaggedValue(captureI.startIndex), JSTaggedValue(captureI.endIndex))); } JSHandle iValue(thread, capturedValue); // add to RegExp.$i and i must <= 9 if (captureIndex <= REGEXP_GLOBAL_ARRAY_SIZE) { - keyString.Update(GetDollarString(thread, static_cast(captureIndex))); - ObjectOperator op(thread, globalRegExp, keyString); - PropertyBox *cell = PropertyBox::Cast(op.GetValue().GetTaggedObject()); - cell->SetValue(thread, iValue); + globalTable->SetCapture(thread, captureIndex, capturedValue); } JSObject::CreateDataProperty(thread, results, captureIndex, iValue); @@ -1513,21 +1652,31 @@ JSTaggedValue BuiltinsRegExp::RegExpBuiltinExec(JSThread *thread, const JSHandle if (groupArray->GetLength() > captureIndex - 1) { JSHandle skey(thread, groupArray->Get(captureIndex - 1)); JSObject::CreateDataProperty(thread, groupObject, skey, iValue); + groupNames.emplace_back(skey); + } else { + groupNames.emplace_back(undefined); } + } else { + groupNames.emplace_back(undefined); } } + // If hasIndices is true, then + // a. Let indicesArray be MakeMatchIndicesIndexPairArray(S, indices, groupNames, hasGroups). + // b. Perform ! CreateDataPropertyOrThrow(A, "indices", indicesArray). + if (hasIndices) { + auto indicesArray = MakeMatchIndicesIndexPairArray(thread, indices, groupNames, hasGroups); + JSHandle indicesKey = globalConst->GetHandledIndicesString(); + JSObject::CreateDataProperty(thread, results, indicesKey, indicesArray); + } JSHandle emptyString = thread->GlobalConstants()->GetHandledEmptyString(); while (captureIndex <= REGEXP_GLOBAL_ARRAY_SIZE) { - keyString.Update(GetDollarString(thread, static_cast(captureIndex))); - ObjectOperator op(thread, globalRegExp, keyString); - PropertyBox *cell = PropertyBox::Cast(op.GetValue().GetTaggedObject()); - cell->SetValue(thread, emptyString); + globalTable->SetCapture(thread, captureIndex, emptyString.GetTaggedValue()); ++captureIndex; } - if (lastIndex == 0 && useCache) { + if (useCache) { RegExpExecResultCache::AddResultInCache(thread, cacheTable, pattern, flags, inputStr, JSHandle(results), RegExpExecResultCache::EXEC_TYPE, - endIndex); + lastIndexInput, endIndex); } // 29. Return A. return results.GetTaggedValue(); @@ -1544,11 +1693,23 @@ JSTaggedValue BuiltinsRegExp::RegExpExec(JSThread *thread, const JSHandleIsString()); // 3. Let exec be Get(R, "exec"). JSHandle inputStr = JSTaggedValue::ToString(thread, inputString); - + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); const GlobalEnvConstants *globalConst = thread->GlobalConstants(); JSHandle execHandle = globalConst->GetHandledExecString(); JSTaggedValue execVal = ObjectFastOperator::FastGetPropertyByValue(thread, regexp.GetTaggedValue(), execHandle.GetTaggedValue()); + if (execVal == env->GetTaggedRegExpExecFunction()) { + JSTaggedValue result = RegExpBuiltinExec(thread, regexp, JSHandle(inputStr), useCache); + // b. ReturnIfAbrupt(result). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (!result.IsECMAObject() && !result.IsNull()) { + // throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "exec result is null or is not Object", JSTaggedValue::Exception()); + } + return result; + } + JSHandle exec(thread, execVal); // 4. ReturnIfAbrupt(exec). RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -1618,6 +1779,9 @@ uint32_t BuiltinsRegExp::UpdateExpressionFlags(JSThread *thread, const CString & case 'y': flagsBitsTemp = RegExpParser::FLAG_STICKY; break; + case 'd': + flagsBitsTemp = RegExpParser::FLAG_HASINDICES; + break; default: { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle syntaxError = @@ -1636,39 +1800,16 @@ uint32_t BuiltinsRegExp::UpdateExpressionFlags(JSThread *thread, const CString & return flagsBits; } -JSHandle BuiltinsRegExp::GetDollarString(JSThread *thread, RegExpGlobalArrayIndex index) -{ - BUILTINS_API_TRACE(thread, RegExp, GetDollarString); - switch (index) { - case DOLLAR_ONE: - return thread->GlobalConstants()->GetHandledDollarStringOne(); - case DOLLAR_TWO: - return thread->GlobalConstants()->GetHandledDollarStringTwo(); - case DOLLAR_THREE: - return thread->GlobalConstants()->GetHandledDollarStringThree(); - case DOLLAR_FOUR: - return thread->GlobalConstants()->GetHandledDollarStringFour(); - case DOLLAR_FIVE: - return thread->GlobalConstants()->GetHandledDollarStringFive(); - case DOLLAR_SIX: - return thread->GlobalConstants()->GetHandledDollarStringSix(); - case DOLLAR_SEVEN: - return thread->GlobalConstants()->GetHandledDollarStringSeven(); - case DOLLAR_EIGHT: - return thread->GlobalConstants()->GetHandledDollarStringEight(); - case DOLLAR_NINE: - return thread->GlobalConstants()->GetHandledDollarStringNine(); - default: - return thread->GlobalConstants()->GetHandledEmptyString(); - } -} - JSTaggedValue BuiltinsRegExp::FlagsBitsToString(JSThread *thread, uint8_t flags) { - ASSERT((flags & 0xC0) == 0); // 0xC0: first 2 bits of flags must be 0 + ASSERT((flags & 0x80) == 0); // 0x80: first bit of flags must be 0 BUILTINS_API_TRACE(thread, RegExp, FlagsBitsToString); uint8_t *flagsStr = new uint8_t[7]; // 7: maximum 6 flags + '\0' size_t flagsLen = 0; + if (flags & RegExpParser::FLAG_HASINDICES) { + flagsStr[flagsLen] = 'd'; + flagsLen++; + } if (flags & RegExpParser::FLAG_GLOBAL) { flagsStr[flagsLen] = 'g'; flagsLen++; @@ -1729,7 +1870,7 @@ JSTaggedValue BuiltinsRegExp::RegExpInitialize(JSThread *thread, const JSHandle< // 6. ReturnIfAbrupt(F). RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); /** - * 7. If F contains any code unit other than "g", "i", "m", "u", or "y" or if it contains the same code + * 7. If F contains any code unit other than "d", "g", "i", "m", "u", or "y" or if it contains the same code * unit more than once, throw a SyntaxError exception. **/ CString checkStr = ConvertToString(*flagsStrHandle, StringConvertedUsage::LOGICOPERATION); @@ -1825,6 +1966,32 @@ EcmaString *BuiltinsRegExp::EscapeRegExpPattern(JSThread *thread, const JSHandle return *factory->NewFromUtf8(srcStdStr); } +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define SET_GET_CAPTURE_IMPL(index) \ + JSTaggedValue BuiltinsRegExp::GetCapture##index(JSThread *thread, [[maybe_unused]] const JSHandle &obj) \ + { \ + JSHandle globalTable(thread->GetCurrentEcmaContext()->GetRegExpGlobalResult()); \ + return globalTable->GetCapture(); \ + } \ + bool BuiltinsRegExp::SetCapture##index([[maybe_unused]] JSThread *thread, \ + [[maybe_unused]] const JSHandle &obj, \ + [[maybe_unused]] const JSHandle &value, \ + [[maybe_unused]] bool mayThrow) \ + { \ + return true; \ + } + + SET_GET_CAPTURE_IMPL(1) + SET_GET_CAPTURE_IMPL(2) + SET_GET_CAPTURE_IMPL(3) + SET_GET_CAPTURE_IMPL(4) + SET_GET_CAPTURE_IMPL(5) + SET_GET_CAPTURE_IMPL(6) + SET_GET_CAPTURE_IMPL(7) + SET_GET_CAPTURE_IMPL(8) + SET_GET_CAPTURE_IMPL(9) +#undef SET_GET_CAPTURE_IMPL + JSTaggedValue RegExpExecResultCache::CreateCacheTable(JSThread *thread) { int length = CACHE_TABLE_HEADER_SIZE + INITIAL_CACHE_NUMBER * ENTRY_SIZE; @@ -1840,24 +2007,27 @@ JSTaggedValue RegExpExecResultCache::CreateCacheTable(JSThread *thread) return JSTaggedValue(table); } -JSTaggedValue RegExpExecResultCache::FindCachedResult(JSThread *thread, const JSHandle &pattern, +JSTaggedValue RegExpExecResultCache::FindCachedResult(JSThread *thread, + const JSHandle &pattern, const JSHandle &flags, const JSHandle &input, CacheType type, - const JSHandle ®exp, JSTaggedValue extend) + const JSHandle ®exp, + JSTaggedValue lastIndexInput, JSTaggedValue extend) { JSTaggedValue patternValue = pattern.GetTaggedValue(); JSTaggedValue flagsValue = flags.GetTaggedValue(); JSTaggedValue inputValue = input.GetTaggedValue(); - if (!pattern->IsString() || !flags->IsInt() || !input->IsString()) { + if (!pattern->IsString() || !flags->IsInt() || !input->IsString() || !lastIndexInput.IsInt()) { return JSTaggedValue::Undefined(); } - uint32_t hash = pattern->GetKeyHashCode() + static_cast(flags->GetInt()) + input->GetKeyHashCode(); + uint32_t hash = pattern->GetKeyHashCode() + static_cast(flags->GetInt()) + + input->GetKeyHashCode() + static_cast(lastIndexInput.GetInt()); uint32_t entry = hash & static_cast(GetCacheLength() - 1); - if (!Match(entry, patternValue, flagsValue, inputValue, extend)) { + if (!Match(entry, patternValue, flagsValue, inputValue, lastIndexInput, extend)) { uint32_t entry2 = (entry + 1) & static_cast(GetCacheLength() - 1); - if (!Match(entry2, patternValue, flagsValue, inputValue, extend)) { + if (!Match(entry2, patternValue, flagsValue, inputValue, lastIndexInput, extend)) { return JSTaggedValue::Undefined(); } entry = entry2; @@ -1865,6 +2035,11 @@ JSTaggedValue RegExpExecResultCache::FindCachedResult(JSThread *thread, const JS ASSERT((static_cast(CACHE_TABLE_HEADER_SIZE) + static_cast(entry) * static_cast(ENTRY_SIZE)) <= static_cast(UINT32_MAX)); uint32_t index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE; + // update cached value if input value is changed + JSTaggedValue cachedStr = Get(index + INPUT_STRING_INDEX); + if (!cachedStr.IsUndefined() && cachedStr != inputValue) { + Set(thread, index + INPUT_STRING_INDEX, inputValue); + } JSTaggedValue result; switch (type) { case REPLACE_TYPE: @@ -1879,6 +2054,9 @@ JSTaggedValue RegExpExecResultCache::FindCachedResult(JSThread *thread, const JS case EXEC_TYPE: result = Get(index + RESULT_EXEC_INDEX); break; + case INTERMEDIATE_REPLACE_TYPE: + result = Get(index + RESULT_INTERMEDIATE_REPLACE_INDEX); + break; default: LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); @@ -1900,7 +2078,7 @@ void RegExpExecResultCache::AddResultInCache(JSThread *thread, JSHandle &pattern, const JSHandle &flags, const JSHandle &input, const JSHandle &resultArray, CacheType type, - uint32_t lastIndex, JSTaggedValue extend) + uint32_t lastIndexInput, uint32_t lastIndex, JSTaggedValue extend) { if (!pattern->IsString() || !flags->IsInt() || !input->IsString()) { return; @@ -1918,19 +2096,21 @@ void RegExpExecResultCache::AddResultInCache(JSThread *thread, JSHandle(flagsValue.GetInt()) + - inputValue.GetKeyHashCode(); + inputValue.GetKeyHashCode() + lastIndexInput; uint32_t entry = hash & static_cast(cache->GetCacheLength() - 1); ASSERT((static_cast(CACHE_TABLE_HEADER_SIZE) + static_cast(entry) * static_cast(ENTRY_SIZE)) <= static_cast(UINT32_MAX)); uint32_t index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE; if (cache->Get(index).IsUndefined()) { cache->SetCacheCount(thread, cache->GetCacheCount() + 1); - cache->SetEntry(thread, entry, patternValue, flagsValue, inputValue, lastIndexValue, extend); + cache->SetEntry(thread, entry, patternValue, flagsValue, inputValue, + lastIndexInputValue, lastIndexValue, extend); cache->UpdateResultArray(thread, entry, resultArrayCopy.GetTaggedValue(), type); - } else if (cache->Match(entry, patternValue, flagsValue, inputValue, extend)) { + } else if (cache->Match(entry, patternValue, flagsValue, inputValue, lastIndexInputValue, extend)) { cache->UpdateResultArray(thread, entry, resultArrayCopy.GetTaggedValue(), type); } else { uint32_t entry2 = (entry + 1) & static_cast(cache->GetCacheLength() - 1); @@ -1952,15 +2132,17 @@ void RegExpExecResultCache::AddResultInCache(JSThread *thread, JSHandleGet(index2).IsUndefined()) { cache->SetCacheCount(thread, cache->GetCacheCount() + 1); - cache->SetEntry(thread, entry2, patternValue, flagsValue, inputValue, lastIndexValue, extendValue); + cache->SetEntry(thread, entry2, patternValue, flagsValue, inputValue, + lastIndexInputValue, lastIndexValue, extendValue); cache->UpdateResultArray(thread, entry2, resultArrayCopy.GetTaggedValue(), type); - } else if (cache->Match(entry2, patternValue, flagsValue, inputValue, extendValue)) { + } else if (cache->Match(entry2, patternValue, flagsValue, inputValue, lastIndexInputValue, extendValue)) { cache->UpdateResultArray(thread, entry2, resultArrayCopy.GetTaggedValue(), type); } else { cache->SetConflictCount(thread, cache->GetConflictCount() > 1 ? (cache->GetConflictCount() - 1) : 0); cache->SetCacheCount(thread, cache->GetCacheCount() - 1); cache->ClearEntry(thread, entry2); - cache->SetEntry(thread, entry, patternValue, flagsValue, inputValue, lastIndexValue, extendValue); + cache->SetEntry(thread, entry, patternValue, flagsValue, inputValue, + lastIndexInputValue, lastIndexValue, extendValue); cache->UpdateResultArray(thread, entry, resultArrayCopy.GetTaggedValue(), type); } } @@ -1975,7 +2157,8 @@ void RegExpExecResultCache::GrowRegexpCache(JSThread *thread, JSHandle(CACHE_TABLE_HEADER_SIZE) + static_cast(entry) * static_cast(ENTRY_SIZE)) <= static_cast(INT_MAX)); @@ -1983,6 +2166,7 @@ void RegExpExecResultCache::SetEntry(JSThread *thread, int entry, JSTaggedValue Set(thread, index + PATTERN_INDEX, pattern); Set(thread, index + FLAG_INDEX, flags); Set(thread, index + INPUT_STRING_INDEX, input); + Set(thread, index + LAST_INDEX_INPUT_INDEX, lastIndexInputValue); Set(thread, index + LAST_INDEX_INDEX, lastIndexValue); Set(thread, index + EXTEND_INDEX, extendValue); } @@ -2006,6 +2190,9 @@ void RegExpExecResultCache::UpdateResultArray(JSThread *thread, int entry, JSTag case EXEC_TYPE: Set(thread, index + RESULT_EXEC_INDEX, resultArray); break; + case INTERMEDIATE_REPLACE_TYPE: + Set(thread, index + RESULT_INTERMEDIATE_REPLACE_INDEX, resultArray); + break; default: LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); @@ -2025,7 +2212,7 @@ void RegExpExecResultCache::ClearEntry(JSThread *thread, int entry) } bool RegExpExecResultCache::Match(int entry, JSTaggedValue &pattern, JSTaggedValue &flags, JSTaggedValue &input, - JSTaggedValue &extend) + JSTaggedValue &lastIndexInputValue, JSTaggedValue &extend) { ASSERT((static_cast(CACHE_TABLE_HEADER_SIZE) + static_cast(entry) * static_cast(ENTRY_SIZE)) <= static_cast(INT_MAX)); @@ -2033,6 +2220,7 @@ bool RegExpExecResultCache::Match(int entry, JSTaggedValue &pattern, JSTaggedVal JSTaggedValue keyPattern = Get(index + PATTERN_INDEX); JSTaggedValue keyFlags = Get(index + FLAG_INDEX); JSTaggedValue keyInput = Get(index + INPUT_STRING_INDEX); + JSTaggedValue keyLastIndexInput = Get(index + LAST_INDEX_INPUT_INDEX); JSTaggedValue keyExtend = Get(index + EXTEND_INDEX); if (keyPattern.IsUndefined()) { @@ -2042,9 +2230,11 @@ bool RegExpExecResultCache::Match(int entry, JSTaggedValue &pattern, JSTaggedVal EcmaString *patternStr = EcmaString::Cast(pattern.GetTaggedObject()); uint8_t flagsBits = static_cast(flags.GetInt()); EcmaString *inputStr = EcmaString::Cast(input.GetTaggedObject()); + uint32_t lastIndexInputInt = static_cast(lastIndexInputValue.GetInt()); EcmaString *keyPatternStr = EcmaString::Cast(keyPattern.GetTaggedObject()); uint8_t keyFlagsBits = static_cast(keyFlags.GetInt()); EcmaString *keyInputStr = EcmaString::Cast(keyInput.GetTaggedObject()); + uint32_t keyLastIndexInputInt = static_cast(keyLastIndexInput.GetInt()); bool extendEqual = false; if (extend.IsString() && keyExtend.IsString()) { EcmaString *extendStr = EcmaString::Cast(extend.GetTaggedObject()); @@ -2056,6 +2246,20 @@ bool RegExpExecResultCache::Match(int entry, JSTaggedValue &pattern, JSTaggedVal return false; } return EcmaStringAccessor::StringsAreEqual(patternStr, keyPatternStr) && flagsBits == keyFlagsBits && - EcmaStringAccessor::StringsAreEqual(inputStr, keyInputStr) && extendEqual; + EcmaStringAccessor::StringsAreEqual(inputStr, keyInputStr) && lastIndexInputInt == keyLastIndexInputInt && + extendEqual; +} + +JSTaggedValue RegExpGlobalResult::CreateGloablResultTable(JSThread *thread) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + auto table = static_cast( + *factory->NewTaggedArray(GLOBAL_TABLE_SIZE, JSTaggedValue::Undefined())); + // initialize dollars with empty string + JSTaggedValue emptyString = factory->GetEmptyString().GetTaggedValue(); + for (int i = 1; i <= DOLLAR_NUMBER; i++) { + table->SetCapture(thread, CAPTURE_START_INDEX + i, emptyString); + } + return JSTaggedValue(table); } } // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_regexp.h b/ecmascript/builtins/builtins_regexp.h index 6830d9d070cc01d60d425f0799b85f0ce71f2e98..1801396338e147dd1a7fb40a646248b2a2a81cc8 100644 --- a/ecmascript/builtins/builtins_regexp.h +++ b/ecmascript/builtins/builtins_regexp.h @@ -26,19 +26,6 @@ namespace panda::ecmascript::builtins { class BuiltinsRegExp : public base::BuiltinsBase { public: - enum RegExpGlobalArrayIndex { - DUMP_HEAD, - DOLLAR_ONE, - DOLLAR_TWO, - DOLLAR_THREE, - DOLLAR_FOUR, - DOLLAR_FIVE, - DOLLAR_SIX, - DOLLAR_SEVEN, - DOLLAR_EIGHT, - DOLLAR_NINE - }; - // 21.2.3.1 RegExp ( pattern, flags ) static JSTaggedValue RegExpConstructor(EcmaRuntimeCallInfo *argv); @@ -86,6 +73,29 @@ public: // 21.2.5.2.3 AdvanceStringIndex ( S, index, unicode ) static uint32_t AdvanceStringIndex(const JSHandle &inputStr, uint32_t index, bool unicode); + // 22.2.6.6 get RegExp.prototype.hasIndices + static JSTaggedValue GetHasIndices(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue ReplaceInternal(JSThread *thread, + JSHandle thisObj, + JSHandle string, + JSHandle inputReplaceValue); +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define SET_GET_CAPTURE(index) \ + static JSTaggedValue GetCapture##index(JSThread *thread, const JSHandle &obj); \ + static bool SetCapture##index(JSThread *thread, const JSHandle &obj, \ + const JSHandle &value, bool mayThrow); + + SET_GET_CAPTURE(1) + SET_GET_CAPTURE(2) + SET_GET_CAPTURE(3) + SET_GET_CAPTURE(4) + SET_GET_CAPTURE(5) + SET_GET_CAPTURE(6) + SET_GET_CAPTURE(7) + SET_GET_CAPTURE(8) + SET_GET_CAPTURE(9) +#undef SET_GET_CAPTURE private: static constexpr uint32_t MIN_REPLACE_STRING_LENGTH = 1000; @@ -106,8 +116,6 @@ private: static uint32_t UpdateExpressionFlags(JSThread *thread, const CString &checkStr); - static JSHandle GetDollarString(JSThread *thread, RegExpGlobalArrayIndex index); - // 21.2.3.2.2 Runtime Semantics: RegExpInitialize ( obj, pattern, flags ) static JSTaggedValue RegExpInitialize(JSThread *thread, const JSHandle &obj, const JSHandle &pattern, const JSHandle &flags); @@ -116,6 +124,10 @@ private: const JSHandle &flags); static JSTaggedValue RegExpReplaceFast(JSThread *thread, JSHandle ®exp, JSHandle inputString, uint32_t inputLength); + // 22.2.7.8 MakeMatchIndicesIndexPairArray ( S, indices, groupNames, hasGroups ) + static JSHandle MakeMatchIndicesIndexPairArray(JSThread* thread, + const std::vector>& indices, + const std::vector>& groupNames, bool hasGroups); }; class RegExpExecResultCache : public TaggedArray { @@ -124,7 +136,8 @@ public: REPLACE_TYPE, SPLIT_TYPE, MATCH_TYPE, - EXEC_TYPE + EXEC_TYPE, + INTERMEDIATE_REPLACE_TYPE }; static RegExpExecResultCache *Cast(TaggedObject *object) { @@ -135,21 +148,22 @@ public: JSTaggedValue FindCachedResult(JSThread *thread, const JSHandle &patten, const JSHandle &flags, const JSHandle &input, CacheType type, const JSHandle ®exp, - JSTaggedValue extend = JSTaggedValue::Undefined()); + JSTaggedValue lastIndexInput, JSTaggedValue extend = JSTaggedValue::Undefined()); // extend as an additional parameter to judge cached static void AddResultInCache(JSThread *thread, JSHandle cache, const JSHandle &patten, const JSHandle &flags, const JSHandle &input, const JSHandle &resultArray, - CacheType type, uint32_t lastIndex, JSTaggedValue extend = JSTaggedValue::Undefined()); + CacheType type, uint32_t lastIndexInput, uint32_t lastIndex, + JSTaggedValue extend = JSTaggedValue::Undefined()); static void GrowRegexpCache(JSThread *thread, JSHandle cache); void ClearEntry(JSThread *thread, int entry); void SetEntry(JSThread *thread, int entry, JSTaggedValue &patten, JSTaggedValue &flags, JSTaggedValue &input, - JSTaggedValue &lastIndexValue, JSTaggedValue &extendValue); + JSTaggedValue &lastIndexInputValue, JSTaggedValue &lastIndexValue, JSTaggedValue &extendValue); void UpdateResultArray(JSThread *thread, int entry, JSTaggedValue resultArray, CacheType type); bool Match(int entry, JSTaggedValue &pattenStr, JSTaggedValue &flagsStr, JSTaggedValue &inputStr, - JSTaggedValue &extend); + JSTaggedValue &lastIndexInputValue, JSTaggedValue &extend); inline void SetHitCount(JSThread *thread, int hitCount) { Set(thread, CACHE_HIT_COUNT_INDEX, JSTaggedValue(hitCount)); @@ -231,14 +245,43 @@ private: static constexpr int PATTERN_INDEX = 0; static constexpr int FLAG_INDEX = 1; static constexpr int INPUT_STRING_INDEX = 2; - static constexpr int LAST_INDEX_INDEX = 3; - static constexpr int RESULT_REPLACE_INDEX = 4; - static constexpr int RESULT_SPLIT_INDEX = 5; - static constexpr int RESULT_MATCH_INDEX = 6; - static constexpr int RESULT_EXEC_INDEX = 7; + static constexpr int LAST_INDEX_INPUT_INDEX = 3; + static constexpr int LAST_INDEX_INDEX = 4; + static constexpr int RESULT_REPLACE_INDEX = 5; + static constexpr int RESULT_SPLIT_INDEX = 6; + static constexpr int RESULT_MATCH_INDEX = 7; + static constexpr int RESULT_EXEC_INDEX = 8; + static constexpr int RESULT_INTERMEDIATE_REPLACE_INDEX = 9; // Extend index used for saving an additional parameter to judge cached - static constexpr int EXTEND_INDEX = 8; - static constexpr int ENTRY_SIZE = 9; + static constexpr int EXTEND_INDEX = 10; + static constexpr int ENTRY_SIZE = 11; }; + +class RegExpGlobalResult : public TaggedArray { +public: + static RegExpGlobalResult *Cast(TaggedObject *object) + { + return reinterpret_cast(object); + } + static JSTaggedValue CreateGloablResultTable(JSThread *thread); + + void SetCapture(JSThread *thread, int index, JSTaggedValue value) + { + ASSERT(CAPTURE_START_INDEX + index - 1 < GLOBAL_TABLE_SIZE); + Set(thread, CAPTURE_START_INDEX + index - 1, value); + } + + template + JSTaggedValue GetCapture() + { + return Get(CAPTURE_START_INDEX + N - 1); + } + +private: + static constexpr int GLOBAL_TABLE_SIZE = 9; + static constexpr int DOLLAR_NUMBER = 9; + static constexpr int CAPTURE_START_INDEX = 0; +}; + } // namespace panda::ecmascript::builtins #endif // ECMASCRIPT_BUILTINS_BUILTINS_REGEXP_H diff --git a/ecmascript/builtins/builtins_set.cpp b/ecmascript/builtins/builtins_set.cpp index 9a3d709f757cb4eef1fa0e17f1d1689f7208b36f..2b8883d5ffd71cbe3faeb9352bde52bd4896068e 100644 --- a/ecmascript/builtins/builtins_set.cpp +++ b/ecmascript/builtins/builtins_set.cpp @@ -117,8 +117,7 @@ JSTaggedValue BuiltinsSet::Add(EcmaRuntimeCallInfo *argv) } JSHandle value(GetCallArg(argv, 0)); - JSHandle set(JSTaggedValue::ToObject(thread, self)); - + JSHandle set(self); JSSet::Add(thread, set, value); return set.GetTaggedValue(); } @@ -136,7 +135,7 @@ JSTaggedValue BuiltinsSet::Clear(EcmaRuntimeCallInfo *argv) if (!self->IsJSSet()) { THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSSet", JSTaggedValue::Exception()); } - JSHandle set(thread, JSSet::Cast(*JSTaggedValue::ToObject(thread, self))); + JSHandle set(self); JSSet::Clear(thread, set); return JSTaggedValue::Undefined(); } @@ -154,7 +153,7 @@ JSTaggedValue BuiltinsSet::Delete(EcmaRuntimeCallInfo *argv) THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSSet", JSTaggedValue::Exception()); } - JSHandle set(thread, JSSet::Cast(*JSTaggedValue::ToObject(thread, self))); + JSHandle set(self); JSHandle value = GetCallArg(argv, 0); bool flag = JSSet::Delete(thread, set, value); return GetTaggedBoolean(flag); @@ -172,7 +171,7 @@ JSTaggedValue BuiltinsSet::Has(EcmaRuntimeCallInfo *argv) if (!self->IsJSSet()) { THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSSet", JSTaggedValue::Exception()); } - JSSet *jsSet = JSSet::Cast(*JSTaggedValue::ToObject(thread, self)); + JSSet* jsSet = JSSet::Cast(self.GetTaggedValue().GetTaggedObject()); JSHandle value = GetCallArg(argv, 0); bool flag = jsSet->Has(value.GetTaggedValue()); return GetTaggedBoolean(flag); @@ -202,7 +201,7 @@ JSTaggedValue BuiltinsSet::ForEach(EcmaRuntimeCallInfo *argv) // 6.Let entries be the List that is the value of S’s [[SetData]] internal slot. JSMutableHandle hashSet(thread, set->GetLinkedSet()); - const int32_t argsLength = 3; + const uint32_t argsLength = 3; int index = 0; int totalElements = hashSet->NumberOfElements() + hashSet->NumberOfDeletedElements(); JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); @@ -249,8 +248,8 @@ JSTaggedValue BuiltinsSet::GetSize(EcmaRuntimeCallInfo *argv) 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(); + JSSet* jsSet = JSSet::Cast(self.GetTaggedValue().GetTaggedObject()); + uint32_t count = jsSet->GetSize(); return JSTaggedValue(count); } @@ -262,6 +261,7 @@ JSTaggedValue BuiltinsSet::Entries(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); JSHandle iter = JSSetIterator::CreateSetIterator(thread, self, IterationKind::KEY_AND_VALUE); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return iter.GetTaggedValue(); } @@ -273,6 +273,7 @@ JSTaggedValue BuiltinsSet::Values(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle self = GetThis(argv); JSHandle iter = JSSetIterator::CreateSetIterator(thread, self, IterationKind::VALUE); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return iter.GetTaggedValue(); } } // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_set.h b/ecmascript/builtins/builtins_set.h index aa00a122ff5c3fd33b8a8538fcb7cc89e09444eb..b7ee92088d8c2a45e78542fd34fcc6d472dc7795 100644 --- a/ecmascript/builtins/builtins_set.h +++ b/ecmascript/builtins/builtins_set.h @@ -19,6 +19,28 @@ #include "ecmascript/base/builtins_base.h" #include "ecmascript/ecma_runtime_call_info.h" +// List of functions in Set, excluding the constructor and '@@' properties. +// V(name, func, length, stubIndex) +// where BuiltinsSet::func refers to the native implementation of Set.prototype[name]. +// kungfu::BuiltinsStubCSigns::stubIndex refers to the builtin stub index, or INVALID if no stub available. +// The following functions are not listed: +// - Set.prototype.keys ( ), which is strictly equal to Set.prototype.values +#define BUILTIN_SET_PROTOTYPE_FUNCTIONS(V) \ + /* Set.prototype.add ( value ) */ \ + V("add", Add, 1, SetAdd) \ + /* Set.prototype.clear ( ) */ \ + V("clear", Clear, 0, INVALID) \ + /* Set.prototype.delete ( value ) */ \ + V("delete", Delete, 1, SetDelete) \ + /* Set.prototype.entries ( ) */ \ + V("entries", Entries, 0, INVALID) \ + /* Set.prototype.forEach ( callbackfn [ , thisArg ] ) */ \ + V("forEach", ForEach, 1, SetForEach) \ + /* Set.prototype.has ( value ) */ \ + V("has", Has, 1, INVALID) \ + /* Set.prototype.values ( ) */ \ + V("values", Values, 0, INVALID) + namespace panda::ecmascript::builtins { class BuiltinsSet : public base::BuiltinsBase { public: @@ -42,6 +64,22 @@ public: static JSTaggedValue GetSize(EcmaRuntimeCallInfo *argv); // 23.2.3.10 static JSTaggedValue Values(EcmaRuntimeCallInfo *argv); + + // Excluding the '@@' internal properties + static Span GetSetPrototypeFunctions() + { + return Span(SET_PROTOTYPE_FUNCTIONS); + } + +private: +#define BUILTIN_SET_FUNCTION_ENTRY(name, func, length, id) \ + base::BuiltinFunctionEntry::Create(name, BuiltinsSet::func, length, kungfu::BuiltinsStubCSigns::id), + + static constexpr std::array SET_PROTOTYPE_FUNCTIONS = { + BUILTIN_SET_PROTOTYPE_FUNCTIONS(BUILTIN_SET_FUNCTION_ENTRY) + }; + +#undef BUILTIN_SET_FUNCTION_ENTRY }; } // namespace panda::ecmascript::builtins #endif // ECMASCRIPT_BUILTINS_BUILTINS_SET_H diff --git a/ecmascript/builtins/builtins_string.cpp b/ecmascript/builtins/builtins_string.cpp index 12983d41bedafaa7f13303467111cfeb0d8211a4..91fd3272c0a847214ba6cdce5ede3088bfde6453 100644 --- a/ecmascript/builtins/builtins_string.cpp +++ b/ecmascript/builtins/builtins_string.cpp @@ -40,6 +40,7 @@ #include "ecmascript/js_tagged_value-inl.h" #include "ecmascript/mem/c_containers.h" #include "ecmascript/object_factory.h" +#include "ecmascript/property_detector-inl.h" #include "ecmascript/tagged_array-inl.h" #include "ecmascript/tagged_array.h" #ifdef ARK_SUPPORT_INTL @@ -116,12 +117,7 @@ JSTaggedValue BuiltinsString::FromCharCode(EcmaRuntimeCallInfo *argv) valueTable.emplace_back(nextCv); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); } - std::u16string u16str = base::StringHelper::Utf16ToU16String(valueTable.data(), argLength); - const char16_t *constChar16tData = u16str.data(); - auto *char16tData = const_cast(constChar16tData); - auto *uint16tData = reinterpret_cast(char16tData); - uint32_t u16strSize = u16str.size(); - return factory->NewFromUtf16Literal(uint16tData, u16strSize).GetTaggedValue(); + return factory->NewFromUtf16Literal(valueTable.data(), valueTable.size()).GetTaggedValue(); } // 21.1.2.2 @@ -160,13 +156,13 @@ JSTaggedValue BuiltinsString::FromCodePoint(EcmaRuntimeCallInfo *argv) ((static_cast(cp) - ENCODE_SECOND_FACTOR) % ENCODE_FIRST_FACTOR) + ENCODE_TRAIL_LOW; std::u16string nextU16str1 = base::StringHelper::Utf16ToU16String(&cu1, 1); std::u16string nextU16str2 = base::StringHelper::Utf16ToU16String(&cu2, 1); - u16str = base::StringHelper::Append(u16str, nextU16str1); - u16str = base::StringHelper::Append(u16str, nextU16str2); + base::StringHelper::InplaceAppend(u16str, nextU16str1); + base::StringHelper::InplaceAppend(u16str, nextU16str2); u16strSize++; } else { auto u16tCp = static_cast(cp); std::u16string nextU16str = base::StringHelper::Utf16ToU16String(&u16tCp, 1); - u16str = base::StringHelper::Append(u16str, nextU16str); + base::StringHelper::InplaceAppend(u16str, nextU16str); } } const char16_t *constChar16tData = u16str.data(); @@ -247,6 +243,7 @@ JSTaggedValue BuiltinsString::CharAt(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle thisFlat(thread, EcmaStringAccessor::Flatten(thread->GetEcmaVM(), thisHandle)); @@ -277,6 +274,7 @@ JSTaggedValue BuiltinsString::CharCodeAt(EcmaRuntimeCallInfo *argv) JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle thisFlat(thread, EcmaStringAccessor::Flatten(thread->GetEcmaVM(), thisHandle)); @@ -307,6 +305,7 @@ JSTaggedValue BuiltinsString::CodePointAt(EcmaRuntimeCallInfo *argv) JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle thisFlat(thread, EcmaStringAccessor::Flatten(thread->GetEcmaVM(), thisHandle)); @@ -338,36 +337,24 @@ JSTaggedValue BuiltinsString::Concat(EcmaRuntimeCallInfo *argv) BUILTINS_API_TRACE(argv->GetThread(), String, Concat); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + auto ecmaVm = thread->GetEcmaVM(); JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); uint32_t argLength = argv->GetArgsNumber(); if (argLength == 0) { return thisHandle.GetTaggedValue(); } - std::u16string u16strThis; - std::u16string u16strNext; - bool canBeCompress = true; - u16strThis = EcmaStringAccessor(thisHandle).ToU16String(); - if (EcmaStringAccessor(thisHandle).IsUtf16()) { - canBeCompress = false; - } for (uint32_t i = 0; i < argLength; i++) { JSHandle nextTag = BuiltinsString::GetCallArg(argv, i); JSHandle nextHandle = JSTaggedValue::ToString(thread, nextTag); - u16strNext = EcmaStringAccessor(nextHandle).ToU16String(); - if (EcmaStringAccessor(nextHandle).IsUtf16()) { - canBeCompress = false; - } - u16strThis = base::StringHelper::Append(u16strThis, u16strNext); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + EcmaString *tempStr = EcmaStringAccessor::Concat(ecmaVm, thisHandle, nextHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + thisHandle = JSHandle(thread, tempStr); } - const char16_t *constChar16tData = u16strThis.data(); - auto *char16tData = const_cast(constChar16tData); - auto *uint16tData = reinterpret_cast(char16tData); - uint32_t u16strSize = u16strThis.size(); - return canBeCompress ? factory->NewFromUtf16LiteralCompress(uint16tData, u16strSize).GetTaggedValue() : - factory->NewFromUtf16LiteralNotCompress(uint16tData, u16strSize).GetTaggedValue(); + return thisHandle.GetTaggedValue(); } // 21.1.3.5 String.prototype.constructor @@ -379,6 +366,7 @@ JSTaggedValue BuiltinsString::EndsWith(EcmaRuntimeCallInfo *argv) JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle searchTag = BuiltinsString::GetCallArg(argv, 0); JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -421,6 +409,7 @@ JSTaggedValue BuiltinsString::Includes(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle searchTag = BuiltinsString::GetCallArg(argv, 0); JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); bool isRegexp = JSObject::IsRegExp(thread, searchTag); @@ -456,6 +445,7 @@ JSTaggedValue BuiltinsString::IndexOf(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle searchTag = BuiltinsString::GetCallArg(argv, 0); JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); uint32_t thisLen = EcmaStringAccessor(thisHandle).GetLength(); @@ -489,6 +479,7 @@ JSTaggedValue BuiltinsString::LastIndexOf(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle searchTag = BuiltinsString::GetCallArg(argv, 0); JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); int32_t thisLen = static_cast(EcmaStringAccessor(thisHandle).GetLength()); @@ -525,6 +516,7 @@ JSTaggedValue BuiltinsString::LocaleCompare(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle thatTag = BuiltinsString::GetCallArg(argv, 0); JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); [[maybe_unused]] JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); [[maybe_unused]] JSHandle thatHandle = JSTaggedValue::ToString(thread, thatTag); @@ -585,6 +577,7 @@ JSTaggedValue BuiltinsString::Match(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); const GlobalEnvConstants *globalConst = thread->GlobalConstants(); JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle regexp = BuiltinsString::GetCallArg(argv, 0); JSHandle matchTag = thread->GetEcmaVM()->GetGlobalEnv()->GetMatchSymbol(); JSHandle undefined = globalConst->GetHandledUndefined(); @@ -594,7 +587,8 @@ JSTaggedValue BuiltinsString::Match(EcmaRuntimeCallInfo *argv) JSHandle pattern(thread, re->GetOriginalSource()); JSHandle flags(thread, re->GetOriginalFlags()); JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, pattern, flags, thisTag, - RegExpExecResultCache::MATCH_TYPE, regexp); + RegExpExecResultCache::MATCH_TYPE, regexp, + JSTaggedValue(0)); if (!cacheResult.IsUndefined()) { return cacheResult; } @@ -633,6 +627,7 @@ JSTaggedValue BuiltinsString::MatchAll(EcmaRuntimeCallInfo *argv) const GlobalEnvConstants *globalConst = thread->GlobalConstants(); // 1. Let O be ? RequireObjectCoercible(this value). JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle regexp = BuiltinsString::GetCallArg(argv, 0); JSHandle matchAllTag = thread->GetEcmaVM()->GetGlobalEnv()->GetMatchAllSymbol(); JSHandle undefined = globalConst->GetHandledUndefined(); @@ -704,6 +699,7 @@ JSTaggedValue BuiltinsString::Normalize(EcmaRuntimeCallInfo *argv) auto vm = thread->GetEcmaVM(); ObjectFactory *factory = vm->GetFactory(); JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); JSHandle formValue; @@ -769,6 +765,7 @@ JSTaggedValue BuiltinsString::Repeat(EcmaRuntimeCallInfo *argv) JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); uint32_t thisLen = EcmaStringAccessor(thisHandle).GetLength(); @@ -835,6 +832,13 @@ JSTaggedValue BuiltinsString::Replace(EcmaRuntimeCallInfo *argv) } } + if (searchTag->IsJSRegExp() && PropertyDetector::IsRegExpReplaceDetectorValid(env)) { + JSTaggedValue proto = JSObject::GetPrototype(JSHandle(searchTag)); + if (proto == env->GetTaggedRegExpPrototype()) { + return BuiltinsRegExp::ReplaceInternal(thread, searchTag, thisTag, replaceTag); + } + } + // If searchValue is neither undefined nor null, then if (searchTag->IsECMAObject()) { JSHandle replaceKey = env->GetReplaceSymbol(); @@ -845,7 +849,7 @@ JSTaggedValue BuiltinsString::Replace(EcmaRuntimeCallInfo *argv) // If replacer is not undefined, then if (!replaceMethod->IsUndefined()) { // Return Call(replacer, searchValue, «O, replaceValue»). - const int32_t argsLength = 2; + const uint32_t argsLength = 2; JSHandle undefined = globalConst->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, replaceMethod, searchTag, undefined, argsLength); @@ -883,12 +887,13 @@ JSTaggedValue BuiltinsString::Replace(EcmaRuntimeCallInfo *argv) // If functionalReplace is true, then if (replaceTag->IsCallable()) { // Let replValue be Call(replaceValue, undefined,«matched, pos, and string»). - const int32_t argsLength = 3; // 3: «matched, pos, and string» + const uint32_t argsLength = 3; // 3: «matched, pos, and string» EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, replaceTag, undefined, undefined, argsLength); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); info->SetCallArg(searchString.GetTaggedValue(), JSTaggedValue(pos), thisString.GetTaggedValue()); JSTaggedValue replStrDeocodeValue = JSFunction::Call(info); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); replHandle.Update(replStrDeocodeValue); } else { // Let captures be an empty List. @@ -899,6 +904,7 @@ JSTaggedValue BuiltinsString::Replace(EcmaRuntimeCallInfo *argv) replHandle.Update(GetSubstitution(thread, searchString, thisString, pos, capturesList, undefined, replacement)); } JSHandle realReplaceStr = JSTaggedValue::ToString(thread, replHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // Let tailPos be pos + the number of code units in matched. int32_t tailPos = pos + static_cast(EcmaStringAccessor(searchString).GetLength()); // Let newString be the String formed by concatenating the first pos code units of string, @@ -911,8 +917,12 @@ JSTaggedValue BuiltinsString::Replace(EcmaRuntimeCallInfo *argv) auto thisLen = EcmaStringAccessor(thisString).GetLength(); JSHandle suffixString(thread, EcmaStringAccessor::FastSubString(ecmaVm, thisString, tailPos, thisLen - tailPos)); - JSHandle tempString(thread, EcmaStringAccessor::Concat(ecmaVm, prefixString, realReplaceStr)); - return JSTaggedValue(EcmaStringAccessor::Concat(ecmaVm, tempString, suffixString)); + EcmaString *tempStr = EcmaStringAccessor::Concat(ecmaVm, prefixString, realReplaceStr); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle tempString(thread, tempStr); + EcmaString *resultStr = EcmaStringAccessor::Concat(ecmaVm, tempString, suffixString); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue(resultStr); } JSTaggedValue BuiltinsString::ReplaceAll(EcmaRuntimeCallInfo *argv) @@ -967,6 +977,7 @@ JSTaggedValue BuiltinsString::ReplaceAll(EcmaRuntimeCallInfo *argv) JSHandle undefined = globalConst->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, replaceMethod, searchTag, undefined, argsLength); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); info->SetCallArg(thisTag.GetTaggedValue(), replaceTag.GetTaggedValue()); return JSFunction::Call(info); } @@ -1005,11 +1016,13 @@ JSTaggedValue BuiltinsString::ReplaceAll(EcmaRuntimeCallInfo *argv) // If functionalReplace is true, then if (replaceTag->IsCallable()) { // Let replValue be Call(replaceValue, undefined,«matched, pos, and string»). - const int32_t argsLength = 3; // 3: «matched, pos, and string» + const uint32_t argsLength = 3; // 3: «matched, pos, and string» EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, replaceTag, undefined, undefined, argsLength); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); info->SetCallArg(searchString.GetTaggedValue(), JSTaggedValue(pos), thisString.GetTaggedValue()); JSTaggedValue replStrDeocodeValue = JSFunction::Call(info); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); replHandle.Update(replStrDeocodeValue); } else { // Let captures be an empty List. @@ -1021,6 +1034,7 @@ JSTaggedValue BuiltinsString::ReplaceAll(EcmaRuntimeCallInfo *argv) capturesList, undefined, replacement)); } JSHandle realReplaceStr = JSTaggedValue::ToString(thread, replHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // Let tailPos be pos + the number of code units in matched. // Let newString be the String formed by concatenating the first pos code units of string, // replStr, and the trailing substring of string starting at index tailPos. @@ -1254,6 +1268,7 @@ JSTaggedValue BuiltinsString::Search(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); const GlobalEnvConstants *globalConst = thread->GlobalConstants(); JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle regexp = BuiltinsString::GetCallArg(argv, 0); JSHandle searchTag = thread->GetEcmaVM()->GetGlobalEnv()->GetSearchSymbol(); JSHandle undefined = globalConst->GetHandledUndefined(); @@ -1291,6 +1306,7 @@ JSTaggedValue BuiltinsString::Slice(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); int32_t thisLen = static_cast(EcmaStringAccessor(thisHandle).GetLength()); @@ -1335,6 +1351,7 @@ JSTaggedValue BuiltinsString::Split(EcmaRuntimeCallInfo *argv) // Let O be RequireObjectCoercible(this value). JSHandle thisTag = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle thisObj(thisTag); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle seperatorTag = BuiltinsString::GetCallArg(argv, 0); @@ -1347,7 +1364,7 @@ JSTaggedValue BuiltinsString::Split(EcmaRuntimeCallInfo *argv) RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); if (!splitter->IsUndefined()) { // Return Call(splitter, separator, «‍O, limit»). - const int32_t argsLength = 2; + const uint32_t argsLength = 2; JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, splitter, seperatorTag, undefined, argsLength); @@ -1361,6 +1378,7 @@ JSTaggedValue BuiltinsString::Split(EcmaRuntimeCallInfo *argv) RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // Let A be ArrayCreate(0). JSHandle resultArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); uint32_t arrayLength = 0; // If limit is undefined, let lim = 2^53–1; else let lim = ToLength(limit). uint32_t lim = 0; @@ -1412,7 +1430,12 @@ JSTaggedValue BuiltinsString::Split(EcmaRuntimeCallInfo *argv) int32_t index = 0; int32_t pos = EcmaStringAccessor::IndexOf(ecmaVm, thisString, seperatorString); while (pos != -1) { - EcmaString *elementString = EcmaStringAccessor::FastSubString(ecmaVm, thisString, index, pos - index); + EcmaString *elementString; + if (static_cast(pos - index) >= SlicedString::MIN_SLICED_ECMASTRING_LENGTH) { + elementString = EcmaStringAccessor::GetSlicedString(ecmaVm, thisString, index, pos - index); + } else { + elementString = EcmaStringAccessor::FastSubString(ecmaVm, thisString, index, pos - index); + } JSHandle elementTag(thread, elementString); JSObject::CreateDataProperty(thread, resultArray, arrayLength, elementTag); ASSERT_PRINT(!thread->HasPendingException(), "CreateDataProperty can't throw exception"); @@ -1423,7 +1446,12 @@ JSTaggedValue BuiltinsString::Split(EcmaRuntimeCallInfo *argv) index = pos + seperatorLength; pos = EcmaStringAccessor::IndexOf(ecmaVm, thisString, seperatorString, index); } - EcmaString *elementString = EcmaStringAccessor::FastSubString(ecmaVm, thisString, index, thisLength - index); + EcmaString *elementString; + if (static_cast(thisLength - index) >= SlicedString::MIN_SLICED_ECMASTRING_LENGTH) { + elementString = EcmaStringAccessor::GetSlicedString(ecmaVm, thisString, index, thisLength - index); + } else { + elementString = EcmaStringAccessor::FastSubString(ecmaVm, thisString, index, thisLength - index); + } JSHandle elementTag(thread, elementString); JSObject::CreateDataProperty(thread, resultArray, arrayLength, elementTag); ASSERT_PRINT(!thread->HasPendingException(), "CreateDataProperty can't throw exception"); @@ -1440,6 +1468,7 @@ JSTaggedValue BuiltinsString::StartsWith(EcmaRuntimeCallInfo *argv) JSHandle searchTag = BuiltinsString::GetCallArg(argv, 0); JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); bool isRegexp = JSObject::IsRegExp(thread, searchTag); @@ -1482,6 +1511,7 @@ JSTaggedValue BuiltinsString::Substring(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); int32_t thisLen = static_cast(EcmaStringAccessor(thisHandle).GetLength()); @@ -1503,6 +1533,9 @@ JSTaggedValue BuiltinsString::Substring(EcmaRuntimeCallInfo *argv) int32_t from = std::min(start, end); int32_t to = std::max(start, end); int32_t len = to - from; + if (static_cast(len) >= SlicedString::MIN_SLICED_ECMASTRING_LENGTH) { + return JSTaggedValue(EcmaStringAccessor::GetSlicedString(thread->GetEcmaVM(), thisHandle, from, len)); + } return JSTaggedValue(EcmaStringAccessor::FastSubString(thread->GetEcmaVM(), thisHandle, from, len)); } @@ -1517,7 +1550,7 @@ JSTaggedValue BuiltinsString::ToLocaleLowerCase(EcmaRuntimeCallInfo *argv) // Let O be RequireObjectCoercible(this value). JSHandle obj(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); - + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // Let S be ? ToString(O). JSHandle string = JSTaggedValue::ToString(thread, obj); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -1578,7 +1611,7 @@ JSTaggedValue BuiltinsString::ToLocaleUpperCase(EcmaRuntimeCallInfo *argv) // Let O be RequireObjectCoercible(this value). JSHandle obj(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); - + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // Let S be ? ToString(O). JSHandle string = JSTaggedValue::ToString(thread, obj); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -1631,6 +1664,7 @@ JSTaggedValue BuiltinsString::ToLowerCase(EcmaRuntimeCallInfo *argv) JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); EcmaString *result = EcmaStringAccessor::ToLower(thread->GetEcmaVM(), thisHandle); @@ -1653,6 +1687,7 @@ JSTaggedValue BuiltinsString::ToUpperCase(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); EcmaString *result = EcmaStringAccessor::ToUpper(thread->GetEcmaVM(), thisHandle); @@ -1667,6 +1702,7 @@ JSTaggedValue BuiltinsString::Trim(EcmaRuntimeCallInfo *argv) JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); EcmaString *res = EcmaStringAccessor::Trim(thread, thisHandle, EcmaString::TrimMode::TRIM); @@ -1680,6 +1716,7 @@ JSTaggedValue BuiltinsString::TrimStart(EcmaRuntimeCallInfo *argv) JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle thisTag = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); EcmaString *res = EcmaStringAccessor::Trim(thread, thisHandle, EcmaString::TrimMode::TRIM_START); @@ -1693,6 +1730,7 @@ JSTaggedValue BuiltinsString::TrimEnd(EcmaRuntimeCallInfo *argv) JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle thisTag = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); EcmaString *res = EcmaStringAccessor::Trim(thread, thisHandle, EcmaString::TrimMode::TRIM_END); @@ -1706,6 +1744,7 @@ JSTaggedValue BuiltinsString::TrimLeft(EcmaRuntimeCallInfo *argv) JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle thisTag = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); EcmaString *res = EcmaStringAccessor::Trim(thread, thisHandle, EcmaString::TrimMode::TRIM_START); @@ -1719,6 +1758,7 @@ JSTaggedValue BuiltinsString::TrimRight(EcmaRuntimeCallInfo *argv) JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle thisTag = JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); EcmaString *res = EcmaStringAccessor::Trim(thread, thisHandle, EcmaString::TrimMode::TRIM_END); @@ -1741,6 +1781,7 @@ JSTaggedValue BuiltinsString::GetStringIterator(EcmaRuntimeCallInfo *argv) [[maybe_unused]] EcmaHandleScope handleScope(thread); // 1. Let O be RequireObjectCoercible(this value). JSHandle current(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // Let S be ToString(O). JSHandle string = JSTaggedValue::ToString(thread, current); @@ -1762,6 +1803,7 @@ JSTaggedValue BuiltinsString::SubStr(EcmaRuntimeCallInfo *argv) // 2. Let S be ToString(O). JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle thisString = JSTaggedValue::ToString(thread, thisTag); // 3. ReturnIfAbrupt(S). @@ -1810,6 +1852,7 @@ JSTaggedValue BuiltinsString::At(EcmaRuntimeCallInfo *argv) // 1. Let O be RequireObjectCoercible(this value). // 2. Let S be ToString(O). JSHandle thisTag(JSTaggedValue::RequireObjectCoercible(thread, GetThis(argv))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -1846,6 +1889,7 @@ JSTaggedValue BuiltinsString::GetLength(EcmaRuntimeCallInfo *argv) JSHandle thisHandle = GetThis(argv); JSHandle thisString = JSTaggedValue::ToString(thread, thisHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return GetTaggedInt(EcmaStringAccessor(thisString).GetLength()); } @@ -1875,6 +1919,7 @@ JSTaggedValue BuiltinsString::Pad(EcmaRuntimeCallInfo *argv, bool isStart) [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle thisTag = JSTaggedValue::RequireObjectCoercible(thread, BuiltinsString::GetThis(argv)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle thisHandle = JSTaggedValue::ToString(thread, thisTag); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle lengthTag = GetCallArg(argv, 0); @@ -1889,6 +1934,7 @@ JSTaggedValue BuiltinsString::Pad(EcmaRuntimeCallInfo *argv, bool isStart) stringBuilder = u" "; } else { JSHandle filler = JSTaggedValue::ToString(thread, fillString); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); stringBuilder = EcmaStringAccessor(filler).ToU16String(); } if (stringBuilder.size() == 0) { diff --git a/ecmascript/builtins/builtins_string.h b/ecmascript/builtins/builtins_string.h index dc92575df278104af7f6a30cedc02406bd688e58..c2d05ec184aedd319f94122f6098211bda44b5a9 100644 --- a/ecmascript/builtins/builtins_string.h +++ b/ecmascript/builtins/builtins_string.h @@ -20,6 +20,101 @@ #include "ecmascript/ecma_runtime_call_info.h" #include "ecmascript/js_tagged_value.h" +// List of functions in String, excluding the '@@' properties. +// V(name, func, length, stubIndex) +// where BuiltinsString::func refers to the native implementation of String[name]. +// kungfu::BuiltinsStubCSigns::stubIndex refers to the builtin stub index, or INVALID if no stub available. +#define BUILTIN_STRING_FUNCTIONS(V) \ + /* String.fromCharCode ( ...codeUnits ) */ \ + V("fromCharCode", FromCharCode, 1, StringFromCharCode) \ + /* String.fromCodePoint ( ...codePoints ) */ \ + V("fromCodePoint", FromCodePoint, 1, INVALID) \ + /* String.raw ( template, ...substitutions ) */ \ + V("raw", Raw, 1, INVALID) + +// List of functions in String.prototype, excluding the constructor and '@@' properties. +// V(name, func, length, stubIndex) +// where BuiltinsString::func refers to the native implementation of String.prototype[name]. +// The following functions in String.prototype are not implemented yet: +// - String.prototype.isWellFormed ( ) +// - String.prototype.toWellFormed ( ) +#define BUILTIN_STRING_PROTOTYPE_FUNCTIONS(V) \ + /* String.prototype.at ( index ) */ \ + V("at", At, 1, INVALID) \ + /* String.prototype.charAt ( pos ) */ \ + V("charAt", CharAt, 1, StringCharAt) \ + /* String.prototype.charCodeAt ( pos ) */ \ + V("charCodeAt", CharCodeAt, 1, StringCharCodeAt) \ + /* String.prototype.codePointAt ( pos ) */ \ + V("codePointAt", CodePointAt, 1, INVALID) \ + /* String.prototype.concat ( ...args ) */ \ + V("concat", Concat, 1, INVALID) \ + /* String.prototype.endsWith ( searchString [ , endPosition ] ) */ \ + V("endsWith", EndsWith, 1, INVALID) \ + /* String.prototype.includes ( searchString [ , position ] ) */ \ + V("includes", Includes, 1, INVALID) \ + /* String.prototype.indexOf ( searchString [ , position ] ) */ \ + V("indexOf", IndexOf, 1, StringIndexOf) \ + /* String.prototype.lastIndexOf ( searchString [ , position ] ) */ \ + V("lastIndexOf", LastIndexOf, 1, INVALID) \ + /* String.prototype.localeCompare ( that [ , reserved1 [ , reserved2 ] ] ) */ \ + V("localeCompare", LocaleCompare, 1, LocaleCompare) \ + /* String.prototype.match ( regexp ) */ \ + V("match", Match, 1, INVALID) \ + /* String.prototype.matchAll ( regexp ) */ \ + V("matchAll", MatchAll, 1, INVALID) \ + /* String.prototype.normalize ( [ form ] ) */ \ + V("normalize", Normalize, 0, INVALID) \ + /* String.prototype.padEnd ( maxLength [ , fillString ] ) */ \ + V("padEnd", PadEnd, 1, INVALID) \ + /* String.prototype.padStart ( maxLength [ , fillString ] ) */ \ + V("padStart", PadStart, 1, INVALID) \ + /* String.prototype.repeat ( count ) */ \ + V("repeat", Repeat, 1, INVALID) \ + /* String.prototype.replace ( searchValue, replaceValue ) */ \ + V("replace", Replace, 2, INVALID) \ + /* String.prototype.replaceAll ( searchValue, replaceValue ) */ \ + V("replaceAll", ReplaceAll, 2, INVALID) \ + /* String.prototype.search ( regexp ) */ \ + V("search", Search, 1, INVALID) \ + /* String.prototype.slice ( start, end ) */ \ + V("slice", Slice, 2, INVALID) \ + /* String.prototype.split ( separator, limit ) */ \ + V("split", Split, 2, INVALID) \ + /* String.prototype.startsWith ( searchString [ , position ] ) */ \ + V("startsWith", StartsWith, 1, INVALID) \ + /* In Annex B.2.2: Additional Properties of the String.prototype Object */ \ + /* String.prototype.substr ( start, length ) */ \ + V("substr", SubStr, 2, INVALID) \ + /* String.prototype.substring ( start, end ) */ \ + V("substring", Substring, 2, StringSubstring) \ + /* String.prototype.toLocaleLowerCase ( [ reserved1 [ , reserved2 ] ] ) */ \ + V("toLocaleLowerCase", ToLocaleLowerCase, 0, INVALID) \ + /* String.prototype.toLocaleUpperCase ( [ reserved1 [ , reserved2 ] ] ) */ \ + V("toLocaleUpperCase", ToLocaleUpperCase, 0, INVALID) \ + /* String.prototype.toLowerCase ( ) */ \ + V("toLowerCase", ToLowerCase, 0, INVALID) \ + /* String.prototype.toString ( ) */ \ + V("toString", ToString, 0, INVALID) \ + /* String.prototype.toUpperCase ( ) */ \ + V("toUpperCase", ToUpperCase, 0, INVALID) \ + /* String.prototype.trim ( ) */ \ + V("trim", Trim, 0, INVALID) \ + /* String.prototype.trimEnd ( ) */ \ + V("trimEnd", TrimEnd, 0, INVALID) \ + /* In Annex B.2.2: Additional Properties of the String.prototype Object */ \ + /* Equivalent to trimStart. For compatibility only. */ \ + /* String.prototype.trimLeft ( ) */ \ + V("trimLeft", TrimLeft, 0, INVALID) \ + /* In Annex B.2.2: Additional Properties of the String.prototype Object */ \ + /* Equivalent to trimEnd. For compatibility only. */ \ + /* String.prototype.trimEnd ( ) */ \ + V("trimRight", TrimRight, 0, INVALID) \ + /* String.prototype.trimStart ( ) */ \ + V("trimStart", TrimStart, 0, INVALID) \ + /* String.prototype.valueOf ( ) */ \ + V("valueOf", ValueOf, 0, INVALID) + namespace panda::ecmascript::builtins { constexpr int32_t ENCODE_MAX_UTF16 = 0X10FFFF; constexpr uint16_t ENCODE_LEAD_LOW = 0xD800; @@ -127,7 +222,30 @@ public: static JSTaggedValue GetLength(EcmaRuntimeCallInfo *argv); + // Excluding the '@@' internal properties + static Span GetStringFunctions() + { + return Span(STRING_FUNCTIONS); + } + + // Excluding the constructor and '@@' internal properties. + static Span GetStringPrototypeFunctions() + { + return Span(STRING_PROTOTYPE_FUNCTIONS); + } + private: +#define BUILTIN_STRING_FUNCTION_ENTRY(name, method, length, builtinId) \ + base::BuiltinFunctionEntry::Create(name, BuiltinsString::method, length, kungfu::BuiltinsStubCSigns::builtinId), + + static constexpr std::array STRING_FUNCTIONS = { + BUILTIN_STRING_FUNCTIONS(BUILTIN_STRING_FUNCTION_ENTRY) + }; + static constexpr std::array STRING_PROTOTYPE_FUNCTIONS = { + BUILTIN_STRING_PROTOTYPE_FUNCTIONS(BUILTIN_STRING_FUNCTION_ENTRY) + }; +#undef BUILTIN_STRING_FUNCTION_ENTRY + static JSTaggedValue Pad(EcmaRuntimeCallInfo *argv, bool isStart); static int32_t ConvertDoubleToInt(double d); // 21.1.3.17.1 diff --git a/ecmascript/builtins/builtins_symbol.cpp b/ecmascript/builtins/builtins_symbol.cpp index d629e11eefc7cf71fcc297f1e1ef2e99e0fbf141..c58a7926fd0c52ee82455f7eba14d96df5864a22 100644 --- a/ecmascript/builtins/builtins_symbol.cpp +++ b/ecmascript/builtins/builtins_symbol.cpp @@ -110,6 +110,7 @@ JSTaggedValue BuiltinsSymbol::SymbolDescriptiveString(JSThread *thread, JSTagged JSHandle rightHandle(factory->NewFromASCII(")")); JSHandle stringLeft = factory->ConcatFromString(leftHandle, JSTaggedValue::ToString(thread, descHandle)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle str = factory->ConcatFromString(stringLeft, rightHandle); return str.GetTaggedValue(); } @@ -194,35 +195,29 @@ JSTaggedValue BuiltinsSymbol::KeyFor(EcmaRuntimeCallInfo *argv) // 19.4.3.4 Symbol.prototype [ @@toPrimitive ] ( hint ) JSTaggedValue BuiltinsSymbol::ToPrimitive(EcmaRuntimeCallInfo *argv) { - // The allowed values for hint are "default", "number", and "string". ASSERT(argv); BUILTINS_API_TRACE(argv->GetThread(), Symbol, ToPrimitive); JSThread *thread = argv->GetThread(); [[maybe_unused]] EcmaHandleScope handleScope(thread); - // 1.Let s be the this value. + // Let s be the this value. JSHandle sym = GetThis(argv); - // 2.If Type(s) is Symbol, return s. + // 1.If value is a Symbol, return value. if (sym->IsSymbol()) { return sym.GetTaggedValue(); } - // 3.If Type(s) is not Object, throw a TypeError exception. - if (!sym->IsHeapObject()) { - // return TypeError - THROW_TYPE_ERROR_AND_RETURN(thread, "ToPrimitive: s is not Object", JSTaggedValue::Exception()); - } - ASSERT(sym->IsHeapObject()); - // 4.If s does not have a [[SymbolData]] internal slot, throw a TypeError exception. - // 5.Return the value of s's [[SymbolData]] internal slot. - if (!sym->IsJSPrimitiveRef()) { - // If s does not have a [[SymbolData]] internal slot, throw a TypeError exception. - THROW_TYPE_ERROR_AND_RETURN(thread, "ToPrimitive: no [[SymbolData]]", JSTaggedValue::Exception()); + + // 2.If value is an Object and value has a [[SymbolData]] internal slot, then + if (sym->IsJSPrimitiveRef()) { + // Let sym be the value of s's [[SymbolData]] internal slot. + JSTaggedValue primitive = JSPrimitiveRef::Cast(sym->GetTaggedObject())->GetValue(); + if (primitive.IsSymbol()) { + return primitive; + } } - // Let sym be the value of s's [[SymbolData]] internal slot. - JSTaggedValue primitive = JSPrimitiveRef::Cast(sym->GetTaggedObject())->GetValue(); - ASSERT(primitive.IsSymbol()); - return primitive; -} + // 3.If s does not have a [[SymbolData]] internal slot, throw a TypeError exception. + THROW_TYPE_ERROR_AND_RETURN(thread, "ToPrimitive: s is not Object", JSTaggedValue::Exception()); +} JSTaggedValue BuiltinsSymbol::DescriptionGetter(EcmaRuntimeCallInfo *argv) { ASSERT(argv); diff --git a/ecmascript/builtins/builtins_symbol.h b/ecmascript/builtins/builtins_symbol.h index cc5be06ed7822881abe1ecf97b8ee121e71cea18..9d91d6af79d76ea5493ec3a90e9dad38b6f0280b 100644 --- a/ecmascript/builtins/builtins_symbol.h +++ b/ecmascript/builtins/builtins_symbol.h @@ -20,6 +20,44 @@ #include "ecmascript/ecma_runtime_call_info.h" #include "ecmascript/js_tagged_value.h" +#define BUILTIN_WELL_KNOWN_SYMBOLS(V) \ + V(hasInstance, HasInstance) \ + V(isConcatSpreadable, IsConcatSpreadable) \ + V(toStringTag, ToStringTag) + +#define BUILTIN_PUBLIC_SYMBOLS(V) \ + V(asyncIterator, AsyncIterator) \ + V(attach, Attach) \ + V(detach, Detach) \ + V(iterator, Iterator) \ + V(match, Match) \ + V(matchAll, MatchAll) \ + V(replace, Replace) \ + V(search, Search) \ + V(species, Species) \ + V(split, Split) \ + V(toPrimitive, ToPrimitive) \ + V(unscopables, Unscopables) + +#define BUILTIN_ALL_SYMBOLS(V) \ + BUILTIN_WELL_KNOWN_SYMBOLS(V) \ + BUILTIN_PUBLIC_SYMBOLS(V) + +// List of functions in Symbol, excluding the '@@' properties. +// V(name, func, length, stubIndex) +// where BuiltinsSymbol::func refers to the native implementation of Symbol[name]. +// kungfu::BuiltinsStubCSigns::stubIndex refers to the builtin stub index, or INVALID if no stub available. +#define BUILTIN_SYMBOL_FUNCTIONS(V) \ + V("for", For, 1, INVALID) \ + V("keyFor", KeyFor, 1, INVALID) + +// List of get accessors in Symbol.prototype, excluding the '@@' properties. +// V(name, func, length, stubIndex) +// where BuiltinsSymbol::func refers to the native implementation of Symbol.prototype[name]. +#define BUILTIN_SYMBOL_PROTOTYPE_FUNCTIONS(V) \ + V("toString", ToString, 0, INVALID) \ + V("valueOf", ValueOf, 0, INVALID) + namespace panda::ecmascript::builtins { class BuiltinsSymbol : public base::BuiltinsBase { public: @@ -45,6 +83,31 @@ public: static JSTaggedValue ToPrimitive(EcmaRuntimeCallInfo *argv); static JSTaggedValue SymbolDescriptiveString(JSThread *thread, JSTaggedValue sym); + + // Excluding the '@@' internal properties + static Span GetSymbolFunctions() + { + return Span(SYMBOL_FUNCTIONS); + } + + // Excluding the constructor and '@@' internal properties. + static Span GetSymbolPrototypeFunctions() + { + return Span(SYMBOL_PROTOTYPE_FUNCTIONS); + } + +private: +#define BUILTIN_SYMBOL_FUNCTION_ENTRY(name, func, length, id) \ + base::BuiltinFunctionEntry::Create(name, BuiltinsSymbol::func, length, kungfu::BuiltinsStubCSigns::id), + + static constexpr std::array SYMBOL_FUNCTIONS = { + BUILTIN_SYMBOL_FUNCTIONS(BUILTIN_SYMBOL_FUNCTION_ENTRY) + }; + static constexpr std::array SYMBOL_PROTOTYPE_FUNCTIONS = { + BUILTIN_SYMBOL_PROTOTYPE_FUNCTIONS(BUILTIN_SYMBOL_FUNCTION_ENTRY) + }; +#undef BUILTIN_TYPED_ARRAY_FUNCTION_ENTRY +#undef BUILTIN_TYPED_ARRAY_ACCESSOR_ENTRY }; } // namespace panda::ecmascript::builtins #endif // ECMASCRIPT_BUILTINS_BUILTINS_SYMBOL_H diff --git a/ecmascript/builtins/builtins_typedarray.cpp b/ecmascript/builtins/builtins_typedarray.cpp index 6d90d4ad72893f5868922b77ecae4ab3f6a52366..f890205d5a4e6060d0b1b6d02f460b8385496cfb 100644 --- a/ecmascript/builtins/builtins_typedarray.cpp +++ b/ecmascript/builtins/builtins_typedarray.cpp @@ -20,7 +20,7 @@ #include "ecmascript/builtins/builtins_array.h" #include "ecmascript/builtins/builtins_arraybuffer.h" #include "ecmascript/ecma_runtime_call_info.h" -#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_string-inl.h" #include "ecmascript/global_env.h" #include "ecmascript/interpreter/interpreter.h" #include "ecmascript/js_array.h" @@ -184,7 +184,9 @@ JSTaggedValue BuiltinsTypedArray::From(EcmaRuntimeCallInfo *argv) JSHandle usingIterator = JSObject::GetMethod(thread, source, iteratorSymbol); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle arrIter = JSObject::GetMethod(thread, env->GetArrayProtoValuesFunction(), iteratorSymbol); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle typedArrIter = JSObject::GetMethod(thread, env->GetTypedArrayPrototype(), iteratorSymbol); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); bool isArrIter = JSTaggedValue::SameValue(usingIterator, arrIter); bool isTypedArrIter = JSTaggedValue::SameValue(usingIterator, typedArrIter); // 6. If usingIterator is not undefined, then @@ -220,7 +222,7 @@ JSTaggedValue BuiltinsTypedArray::From(EcmaRuntimeCallInfo *argv) // vi. Set k to k + 1. JSMutableHandle tKey(thread, JSTaggedValue::Undefined()); JSMutableHandle mapValue(thread, JSTaggedValue::Undefined()); - const int32_t argsLength = 2; + const uint32_t argsLength = 2; uint32_t k = 0; JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); while (k < len) { @@ -275,7 +277,7 @@ JSTaggedValue BuiltinsTypedArray::From(EcmaRuntimeCallInfo *argv) // e. Perform ? Set(targetObj, Pk, mappedValue, true). // f. Set k to k + 1. JSMutableHandle tKey(thread, JSTaggedValue::Undefined()); - const int32_t argsLength = 2; + const uint32_t argsLength = 2; int64_t k = 0; JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); JSMutableHandle kValue(thread, JSTaggedValue::Undefined()); @@ -337,6 +339,7 @@ JSTaggedValue BuiltinsTypedArray::Of(EcmaRuntimeCallInfo *argv) while (k < len) { tKey.Update(JSTaggedValue(k)); JSHandle kKey(JSTaggedValue::ToString(thread, tKey)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle kValue = GetCallArg(argv, k); JSTaggedValue::SetProperty(thread, JSHandle::Cast(newObj), kKey, kValue, true); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -518,7 +521,7 @@ JSTaggedValue BuiltinsTypedArray::Every(EcmaRuntimeCallInfo *argv) // v. If testResult is false, return false. // e. Increase k by 1. JSMutableHandle key(thread, JSTaggedValue::Undefined()); - const int32_t argsLength = 3; + const uint32_t argsLength = 3; JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); uint32_t k = 0; while (k < len) { @@ -611,9 +614,7 @@ JSTaggedValue BuiltinsTypedArray::Filter(EcmaRuntimeCallInfo *argv) info->SetCallArg(kValue.GetTaggedValue(), tKey.GetTaggedValue(), thisHandle.GetTaggedValue()); JSTaggedValue callResult = JSFunction::Call(info); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - bool testResult = callResult.ToBoolean(); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - if (testResult) { + if (callResult.ToBoolean()) { kept->Set(thread, captured, kValue); captured++; } @@ -707,7 +708,7 @@ JSTaggedValue BuiltinsTypedArray::ForEach(EcmaRuntimeCallInfo *argv) // iv. ReturnIfAbrupt(funcResult). // e. Increase k by 1. JSMutableHandle key(thread, JSTaggedValue::Undefined()); - const int32_t argsLength = 3; + const uint32_t argsLength = 3; JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); uint32_t k = 0; while (k < len) { @@ -935,7 +936,7 @@ JSTaggedValue BuiltinsTypedArray::Map(EcmaRuntimeCallInfo *argv) JSMutableHandle key(thread, JSTaggedValue::Undefined()); JSMutableHandle mapValue(thread, JSTaggedValue::Undefined()); JSMutableHandle kValue(thread, JSTaggedValue::Undefined()); - const int32_t argsLength = 3; + const uint32_t argsLength = 3; JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); for (uint32_t k = 0; k < len; k++) { key.Update(JSTaggedValue(k)); @@ -1017,13 +1018,13 @@ JSTaggedValue BuiltinsTypedArray::Set(EcmaRuntimeCallInfo *argv) // 5. Assert: target has a [[ViewedArrayBuffer]] internal slot. // 6. Let targetOffset be ToInteger (offset). const JSHandle srcOffset = GetCallArg(argv, 1); - uint32_t targetOffset = 0; + uint64_t targetOffset = 0; if (srcOffset->IsInt()) { if (srcOffset->GetInt() < 0) { THROW_RANGE_ERROR_AND_RETURN(thread, "The targetOffset of This value is less than 0.", JSTaggedValue::Exception()); } - targetOffset = static_cast(srcOffset->GetInt()); + targetOffset = static_cast(srcOffset->GetInt()); } else { JSTaggedNumber tTargetOffset = JSTaggedValue::ToInteger(thread, srcOffset); // 7. ReturnIfAbrupt(targetOffset). @@ -1037,7 +1038,7 @@ JSTaggedValue BuiltinsTypedArray::Set(EcmaRuntimeCallInfo *argv) THROW_RANGE_ERROR_AND_RETURN(thread, "The targetOffset is infinty, which is greater than targetLength.", JSTaggedValue::Exception()); } else { - targetOffset = static_cast(rawTargetOffset); + targetOffset = static_cast(rawTargetOffset); } } // 9. Let targetBuffer be the value of target’s [[ViewedArrayBuffer]] internal slot. @@ -1092,7 +1093,7 @@ JSTaggedValue BuiltinsTypedArray::Set(EcmaRuntimeCallInfo *argv) JSTaggedValue::Exception()); } // 21. Let targetByteIndex be targetOffset × targetElementSize + targetByteOffset. - ASSERT((static_cast(targetOffset) * static_cast(targetElementSize) + + ASSERT((targetOffset * static_cast(targetElementSize) + static_cast(targetByteOffset)) <= static_cast(UINT32_MAX)); uint32_t targetByteIndex = static_cast(targetOffset * targetElementSize + targetByteOffset); // 22. Let k be 0. @@ -1116,6 +1117,7 @@ JSTaggedValue BuiltinsTypedArray::Set(EcmaRuntimeCallInfo *argv) while (targetByteIndex < limit) { tKey.Update(JSTaggedValue(k)); JSHandle kKey(JSTaggedValue::ToString(thread, tKey)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); kValue.Update(ObjectFastOperator::FastGetPropertyByValue( thread, JSHandle::Cast(src).GetTaggedValue(), kKey.GetTaggedValue())); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -1190,9 +1192,9 @@ JSTaggedValue BuiltinsTypedArray::Set(EcmaRuntimeCallInfo *argv) srcByteIndex = srcByteOffset; } // 26. Let targetByteIndex be targetOffset × targetElementSize + targetByteOffset. - ASSERT((static_cast(targetOffset) * static_cast(targetElementSize) + + ASSERT((targetOffset * static_cast(targetElementSize) + static_cast(targetByteOffset)) <= static_cast(UINT32_MAX)); - uint32_t targetByteIndex = targetOffset * targetElementSize + targetByteOffset; + uint32_t targetByteIndex = static_cast(targetOffset) * targetElementSize + targetByteOffset; // 27. Let limit be targetByteIndex + targetElementSize × srcLength. ASSERT((static_cast(targetElementSize) * static_cast(srcLength) + static_cast(targetByteIndex)) <= static_cast(UINT32_MAX)); @@ -1406,7 +1408,7 @@ JSTaggedValue BuiltinsTypedArray::Sort(EcmaRuntimeCallInfo *argv) key.GetTaggedValue())); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); while (beginIndex < endIndex) { - uint32_t middleIndex = (beginIndex + endIndex) / 2; // 2 : half + uint32_t middleIndex = beginIndex + (endIndex - beginIndex) / 2; // 2 : half key1.Update(JSTaggedValue(middleIndex)); middleValue.Update(ObjectFastOperator::FastGetPropertyByValue(thread, thisObjHandle.GetTaggedValue(), key1.GetTaggedValue())); @@ -1511,7 +1513,7 @@ JSTaggedValue BuiltinsTypedArray::Subarray(EcmaRuntimeCallInfo *argv) // 21. Let argumentsList be «buffer, beginByteOffset, newLength». // 5. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot. // 22. Return Construct(constructor, argumentsList). - const int32_t argsLength = 3; + const uint32_t argsLength = 3; JSTaggedType args[argsLength] = { buffer.GetRawData(), JSTaggedValue(beginByteOffset).GetRawData(), @@ -1589,6 +1591,210 @@ JSTaggedValue BuiltinsTypedArray::ToStringTag(EcmaRuntimeCallInfo *argv) return name; } +// 23.2.3.1 +JSTaggedValue BuiltinsTypedArray::At(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, At); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + // 2. Perform ? ValidateTypedArray(O). + if (!thisHandle->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 3. Let len be O.[[ArrayLength]]. + uint32_t len = JSHandle::Cast(thisObjHandle)->GetArrayLength(); + // ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 4. Let relativeIndex be ? ToIntegerOrInfinity(index). + JSTaggedNumber indexVal = JSTaggedValue::ToInteger(thread, GetCallArg(argv, 0)); + // ReturnIfAbrupt(indexVal). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int64_t relativeIndex = indexVal.GetNumber(); + int64_t k = 0; + // 5. If relativeIndex ≥ 0, then Let k be relativeIndex. + // 6. Else, Let k be len + relativeIndex. + k = relativeIndex >= 0 ? relativeIndex : static_cast(len) + relativeIndex; + // 7. If k < 0 or k ≥ len, return undefined. + if (k < 0 || k >= len) { + return JSTaggedValue::Undefined(); + } + // 8. Return ! Get(O, ! ToString(𝔽(k))). + JSHandle kValue = JSTypedArray::GetProperty(thread, thisHandle, k).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return kValue.GetTaggedValue(); +} + +// 23.2.3.33 +JSTaggedValue BuiltinsTypedArray::ToSorted(EcmaRuntimeCallInfo* argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, ToSorted); + JSThread* thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. If comparefn is not undefined and IsCallable(comparefn) is false, throw a TypeError exception. + JSHandle comparefnHandle = GetCallArg(argv, 0); + if (!comparefnHandle->IsUndefined() && !comparefnHandle->IsCallable()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "the comparefn is not callable.", JSTaggedValue::Exception()); + } + // 2. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 3. Perform ? ValidateTypedArray(O). + TypedArrayHelper::ValidateTypedArray(thread, thisHandle); + // ReturnIfAbrupt(valid). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSHandle thisObj(thisHandle); + // 4. Let len be O.[[ArrayLength]]. + uint32_t len = thisObj->GetArrayLength(); + + // 5. Let A be ? TypedArrayCreateSameType(O, « 𝔽(len) »). + JSTaggedType args[1] = { JSTaggedValue(len).GetRawData() }; + JSHandle newArrObj = TypedArrayHelper::TypedArrayCreateSameType(thread, thisObj, 1, args); // 1: one arg. + // ReturnIfAbrupt(A). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSHandle buffer = + JSHandle(thread, TypedArrayHelper::ValidateTypedArray(thread, thisHandle)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSMutableHandle presentValue(thread, JSTaggedValue::Undefined()); + JSMutableHandle middleValue(thread, JSTaggedValue::Undefined()); + JSMutableHandle previousValue(thread, JSTaggedValue::Undefined()); + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSMutableHandle key1(thread, JSTaggedValue::Undefined()); + JSMutableHandle key2(thread, JSTaggedValue::Undefined()); + if (len > 0) { + previousValue.Update( + ObjectFastOperator::FastGetPropertyByValue(thread, thisHandle.GetTaggedValue(), JSTaggedValue(0))); + ObjectFastOperator::FastSetPropertyByIndex( + thread, newArrObj.GetTaggedValue(), 0, previousValue.GetTaggedValue()); + } + for (uint32_t i = 1; i < len; i++) { + uint32_t beginIndex = 0; + uint32_t endIndex = i; + key.Update(JSTaggedValue(i)); + presentValue.Update( + ObjectFastOperator::FastGetPropertyByValue(thread, thisHandle.GetTaggedValue(), key.GetTaggedValue())); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + while (beginIndex < endIndex) { + uint32_t middleIndex = beginIndex + (endIndex - beginIndex) / 2; // 2 : half + key1.Update(JSTaggedValue(middleIndex)); + middleValue.Update( + ObjectFastOperator::FastGetPropertyByValue(thread, newArrObj.GetTaggedValue(), key1.GetTaggedValue())); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int32_t compareResult = + TypedArrayHelper::SortCompare(thread, comparefnHandle, buffer, middleValue, presentValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + compareResult > 0 ? (endIndex = middleIndex) : (beginIndex = middleIndex + 1); + } + + if (endIndex < i) { + for (uint32_t j = i; j > endIndex; j--) { + key2.Update(JSTaggedValue(j - 1)); + previousValue.Update(ObjectFastOperator::FastGetPropertyByValue( + thread, newArrObj.GetTaggedValue(), key2.GetTaggedValue())); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + ObjectFastOperator::FastSetPropertyByIndex( + thread, newArrObj.GetTaggedValue(), j, previousValue.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + } + ObjectFastOperator::FastSetPropertyByIndex( + thread, newArrObj.GetTaggedValue(), endIndex, presentValue.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + return newArrObj.GetTaggedValue(); +} + +// 23.2.3.36 +JSTaggedValue BuiltinsTypedArray::With(EcmaRuntimeCallInfo* argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, With); + JSThread* thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be the this value. + JSHandle thisHandle = GetThis(argv); + // 2. Perform ? ValidateTypedArray(O). + TypedArrayHelper::ValidateTypedArray(thread, thisHandle); + // ReturnIfAbrupt(valid). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSHandle thisObj(thisHandle); + // 3. Let len be O.[[ArrayLength]]. + uint32_t len = thisObj->GetArrayLength(); + + // 4. Let relativeIndex be ? ToIntegerOrInfinity(index). + JSTaggedNumber indexVal = JSTaggedValue::ToInteger(thread, GetCallArg(argv, 0)); + // ReturnIfAbrupt(indexVal). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int64_t relativeIndex = indexVal.GetNumber(); + // 5. If relativeIndex ≥ 0, let actualIndex be relativeIndex. + // 6. Else, let actualIndex be len + relativeIndex. + int64_t actualIndex = relativeIndex >= 0 ? relativeIndex : static_cast(len) + relativeIndex; + + // 7. If O.[[ContentType]] is BigInt, let numericValue be ? ToBigInt(value). + // 8. Else, let numericValue be ? ToNumber(value). + JSHandle value = GetCallArg(argv, 1); + ContentType contentType = thisObj->GetContentType(); + JSHandle numericValue; + if (contentType == ContentType::BigInt) { + numericValue = JSHandle(thread, JSTaggedValue::ToBigInt(thread, value)); + } else { + numericValue = JSHandle(thread, JSTaggedValue::ToNumber(thread, value)); + } + // ReturnIfAbrupt(numericValue). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 9. If IsValidIntegerIndex(O, 𝔽(actualIndex)) is false, throw a RangeError exception. + if (!JSTypedArray::IsValidIntegerIndex(thisHandle, JSTaggedValue(actualIndex))) { + THROW_RANGE_ERROR_AND_RETURN(thread, "Invalid typed array index", JSTaggedValue::Exception()); + } + + // 10. Let A be ? TypedArrayCreateSameType(O, « 𝔽(len) »). + JSTaggedType args[1] = { JSTaggedValue(len).GetRawData() }; + JSHandle newArrObj = TypedArrayHelper::TypedArrayCreateSameType(thread, thisObj, 1, args); // 1: one arg. + // ReturnIfAbrupt(A). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 11. Let k be 0. + // 12. Repeat, while k < len, + // a. Let Pk be ! ToString(𝔽(k)). + // b. If k is actualIndex, let fromValue be numericValue. + // c. Else, let fromValue be ! Get(O, Pk). + // d. Perform ! Set(A, Pk, fromValue, true). + // e. Set k to k + 1. + JSMutableHandle tKey(thread, JSTaggedValue::Undefined()); + JSMutableHandle fromValue(thread, JSTaggedValue::Undefined()); + uint32_t k = 0; + while (k < len) { + tKey.Update(JSTaggedValue(k)); + if (k == actualIndex) { + fromValue.Update(numericValue); + } else { + fromValue.Update( + ObjectFastOperator::FastGetPropertyByValue(thread, thisHandle.GetTaggedValue(), tKey.GetTaggedValue())); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } + ObjectFastOperator::FastSetPropertyByValue(thread, newArrObj.GetTaggedValue(), + tKey.GetTaggedValue(), fromValue.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + k++; + } + return newArrObj.GetTaggedValue(); +} + // es12 23.2.3.13 JSTaggedValue BuiltinsTypedArray::Includes(EcmaRuntimeCallInfo *argv) { @@ -1599,4 +1805,74 @@ JSTaggedValue BuiltinsTypedArray::Includes(EcmaRuntimeCallInfo *argv) } return BuiltinsArray::Includes(argv); } + +// 23.2.3.32 +JSTaggedValue BuiltinsTypedArray::ToReversed(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + JSThread *thread = argv->GetThread(); + BUILTINS_API_TRACE(thread, TypedArray, ToReversed); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + + // 1. Let O be ToObject(this value). + JSHandle thisHandle = GetThis(argv); + JSHandle thisObj(thisHandle); + // 2. Perform ? ValidateTypedArray(O). + TypedArrayHelper::ValidateTypedArray(thread, thisHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + // ReturnIfAbrupt(O). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 3. Let len be O.[[ArrayLength]]. + uint32_t len = JSHandle::Cast(thisObjHandle)->GetArrayLength(); + // ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 4. Let A be ? TypedArrayCreateSameType(O, « 𝔽(length) »). + JSTaggedType args[1] = {JSTaggedValue(len).GetRawData()}; + JSHandle newArrayHandle = TypedArrayHelper::TypedArrayCreateSameType(thread, thisObj, 1, args); + // ReturnIfAbrupt(newObj). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 5. Let k be 0. + uint32_t k = 0; + + // 6. Repeat, while k < length, + // a. Let from be ! ToString(𝔽(length - k - 1)). + // b. Let Pk be ! ToString(𝔽(k)). + // c. Let fromValue be ! Get(O, from). + // d. Perform ! Set(A, Pk, fromValue, true). + // e. Set k to k + 1. + while (k < len) { + uint32_t from = len - k - 1; + JSHandle fromValue = JSTypedArray::GetProperty(thread, thisHandle, from).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + ObjectFastOperator::FastSetPropertyByIndex(thread, newArrayHandle.GetTaggedValue(), k, + fromValue.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + ++k; + } + // 7. Return A. + return newArrayHandle.GetTaggedValue(); +} + +// 23.2.3.13 +JSTaggedValue BuiltinsTypedArray::FindLast(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, FindLast); + if (!GetThis(argv)->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + return BuiltinsArray::FindLast(argv); +} + +// 23.2.3.14 +JSTaggedValue BuiltinsTypedArray::FindLastIndex(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), TypedArray, FindLastIndex); + if (!GetThis(argv)->IsTypedArray()) { + THROW_TYPE_ERROR_AND_RETURN(argv->GetThread(), "This is not a TypedArray.", JSTaggedValue::Exception()); + } + return BuiltinsArray::FindLastIndex(argv); +} } // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_typedarray.h b/ecmascript/builtins/builtins_typedarray.h index 7d522c665bcac40b757ee1d5805477115ec5f9b0..ff92912c1ccb182286e168f09c91f40309e695fa 100644 --- a/ecmascript/builtins/builtins_typedarray.h +++ b/ecmascript/builtins/builtins_typedarray.h @@ -18,6 +18,107 @@ #include "ecmascript/base/builtins_base.h" +// All types of %TypedArray%. +// V(Type, TYPE, bytesPerElement) where JSType::JS_##TYPE is the type index. +#define BUILTIN_TYPED_ARRAY_TYPES(V) \ + V(Int8Array, INT8_ARRAY, 1) \ + V(Uint8Array, UINT8_ARRAY, 1) \ + V(Uint8ClampedArray, UINT8_CLAMPED_ARRAY, 1) \ + V(Int16Array, INT16_ARRAY, 2) \ + V(Uint16Array, UINT16_ARRAY, 2) \ + V(Int32Array, INT32_ARRAY, 4) \ + V(Uint32Array, UINT32_ARRAY, 4) \ + V(Float32Array, FLOAT32_ARRAY, 4) \ + V(Float64Array, FLOAT64_ARRAY, 8) \ + V(BigInt64Array, BIGINT64_ARRAY, 8) \ + V(BigUint64Array, BIGUINT64_ARRAY, 8) + +// List of functions in %TypedArray%, excluding the '@@' properties. +// V(name, func, length, stubIndex) +// where BuiltinsTypedArray::func refers to the native implementation of %TypedArray%[name]. +// kungfu::BuiltinsStubCSigns::stubIndex refers to the builtin stub index, or INVALID if no stub available. +#define BUILTIN_TYPED_ARRAY_FUNCTIONS(V) \ + /* %TypedArray%.from ( source [ , mapfn [ , thisArg ] ] ) */ \ + V("from", From, 1, INVALID) \ + /* %TypedArray%.of ( ...items ) */ \ + V("of", Of, 0, INVALID) + +// List of get accessors in %TypedArray%.prototype, excluding the '@@' properties. +// V(name, func, stubIndex) +// where BuiltinsTypedArray::func refers to the native implementation. +#define BUILTIN_TYPED_ARRAY_PROTOTYPE_GETTERS(V) \ + V("buffer", GetBuffer, INVALID) /* get %TypedArray%.prototype.buffer */ \ + V("byteLength", GetByteLength, INVALID) /* get %TypedArray%.prototype.byteLength */ \ + V("byteOffset", GetByteOffset, INVALID) /* get %TypedArray%.prototype.byteOffset */ \ + V("length", GetLength, INVALID) /* get %TypedArray%.prototype.length */ + +// List of functions in %TypedArray%.prototype, excluding the constructor and '@@' properties. +// V(name, func, length, stubIndex) +// where BuiltinsTypedArray::func refers to the native implementation of %TypedArray%.prototype[name]. +// The following functions are not included: +// - %TypedArray%.prototype.toString ( ), which is strictly equal to Array.prototype.toString +#define BUILTIN_TYPED_ARRAY_PROTOTYPE_FUNCTIONS(V) \ + /* %TypedArray%.prototype.at ( index ) */ \ + V("at", At, 1, INVALID) \ + /* %TypedArray%.prototype.copyWithin ( target, start [ , end ] ) */ \ + V("copyWithin", CopyWithin, 2, INVALID) \ + /* %TypedArray%.prototype.entries ( ) */ \ + V("entries", Entries, 0, INVALID) \ + /* %TypedArray%.prototype.every ( callbackfn [ , thisArg ] ) */ \ + V("every", Every, 1, INVALID) \ + /* %TypedArray%.prototype.fill ( value [ , start [ , end ] ] ) */ \ + V("fill", Fill, 1, INVALID) \ + /* %TypedArray%.prototype.filter ( callbackfn [ , thisArg ] ) */ \ + V("filter", Filter, 1, INVALID) \ + /* %TypedArray%.prototype.find ( predicate [ , thisArg ] ) */ \ + V("find", Find, 1, INVALID) \ + /* %TypedArray%.prototype.findIndex ( predicate [ , thisArg ] ) */ \ + V("findIndex", FindIndex, 1, INVALID) \ + /* %TypedArray%.prototype.findLast ( predicate [ , thisArg ] ) */ \ + V("findLast", FindLast, 1, INVALID) \ + /* %TypedArray%.prototype.findLastIndex ( predicate [ , thisArg ] ) */ \ + V("findLastIndex", FindLastIndex, 1, INVALID) \ + /* %TypedArray%.prototype.forEach ( callbackfn [ , thisArg ] ) */ \ + V("forEach", ForEach, 1, INVALID) \ + /* %TypedArray%.prototype.includes ( searchElement [ , fromIndex ] ) */ \ + V("includes", Includes, 1, INVALID) \ + /* %TypedArray%.prototype.indexOf ( searchElement [ , fromIndex ] ) */ \ + V("indexOf", IndexOf, 1, INVALID) \ + /* %TypedArray%.prototype.join ( separator ) */ \ + V("join", Join, 1, INVALID) \ + /* %TypedArray%.prototype.keys ( ) */ \ + V("keys", Keys, 0, INVALID) \ + /* %TypedArray%.prototype.lastIndexOf ( searchElement [ , fromIndex ] ) */ \ + V("lastIndexOf", LastIndexOf, 1, INVALID) \ + /* %TypedArray%.prototype.map ( callbackfn [ , thisArg ] ) */ \ + V("map", Map, 1, INVALID) \ + /* %TypedArray%.prototype.reduce ( callbackfn [ , initialValue ] ) */ \ + V("reduce", Reduce, 1, INVALID) \ + /* %TypedArray%.prototype.reduceRight ( callbackfn [ , initialValue ] ) */ \ + V("reduceRight", ReduceRight, 1, INVALID) \ + /* %TypedArray%.prototype.reverse ( ) */ \ + V("reverse", Reverse, 0, INVALID) \ + /* %TypedArray%.prototype.set ( source [ , offset ] ) */ \ + V("set", Set, 1, INVALID) \ + /* %TypedArray%.prototype.slice ( start, end ) */ \ + V("slice", Slice, 2, INVALID) \ + /* %TypedArray%.prototype.some ( callbackfn [ , thisArg ] ) */ \ + V("some", Some, 1, INVALID) \ + /* %TypedArray%.prototype.sort ( comparefn ) */ \ + V("sort", Sort, 1, INVALID) \ + /* %TypedArray%.prototype.subarray ( begin, end ) */ \ + V("subarray", Subarray, 2, INVALID) \ + /* %TypedArray%.prototype.toLocaleString ( [ reserved1 [ , reserved2 ] ] ) */ \ + V("toLocaleString", ToLocaleString, 0, INVALID) \ + /* %TypedArray%.prototype.toReversed ( ) */ \ + V("toReversed", ToReversed, 0, INVALID) \ + /* %TypedArray%.prototype.toSorted ( comparefn ) */ \ + V("toSorted", ToSorted, 1, INVALID) \ + /* %TypedArray%.prototype.values ( ) */ \ + V("values", Values, 0, INVALID) \ + /* %TypedArray%.prototype.with ( index, value ) */ \ + V("with", With, 2, INVALID) + namespace panda::ecmascript::builtins { class BuiltinsTypedArray : public base::BuiltinsBase { public: @@ -107,7 +208,56 @@ public: static JSTaggedValue ToStringTag(EcmaRuntimeCallInfo *argv); // es12 23.2.3.13 static JSTaggedValue Includes(EcmaRuntimeCallInfo *argv); + // 23.2.3.1 + static JSTaggedValue At(EcmaRuntimeCallInfo *argv); + // 23.2.3.32 %TypedArray%.prototype.toReversed ( ) + static JSTaggedValue ToReversed(EcmaRuntimeCallInfo *argv); + // 23.2.3.13 + static JSTaggedValue FindLast(EcmaRuntimeCallInfo *argv); + // 23.2.3.14 + static JSTaggedValue FindLastIndex(EcmaRuntimeCallInfo *argv); + // 23.2.3.33 + static JSTaggedValue ToSorted(EcmaRuntimeCallInfo *argv); + // 23.2.3.36 + static JSTaggedValue With(EcmaRuntimeCallInfo *argv); static const uint32_t MAX_ARRAY_INDEX = std::numeric_limits::max(); + + // Excluding the '@@' internal properties + static Span GetTypedArrayFunctions() + { + return Span(TYPED_ARRAY_FUNCTIONS); + } + + // Excluding the '@@' internal properties + static Span GetTypedArrayPrototypeAccessors() + { + return Span(TYPED_ARRAY_PROTOTYPE_ACCESSORS); + } + + // Excluding the constructor and '@@' internal properties. + static Span GetTypedArrayPrototypeFunctions() + { + return Span(TYPED_ARRAY_PROTOTYPE_FUNCTIONS); + } + +private: +#define BUILTIN_TYPED_ARRAY_FUNCTION_ENTRY(name, func, length, id) \ + base::BuiltinFunctionEntry::Create(name, BuiltinsTypedArray::func, length, kungfu::BuiltinsStubCSigns::id), +#define BUILTIN_TYPED_ARRAY_ACCESSOR_ENTRY(name, func, id) \ + base::BuiltinFunctionEntry::Create( \ + name, BuiltinsTypedArray::func, 0, kungfu::BuiltinsStubCSigns::id), + + static constexpr std::array TYPED_ARRAY_FUNCTIONS = { + BUILTIN_TYPED_ARRAY_FUNCTIONS(BUILTIN_TYPED_ARRAY_FUNCTION_ENTRY) + }; + static constexpr std::array TYPED_ARRAY_PROTOTYPE_ACCESSORS = { + BUILTIN_TYPED_ARRAY_PROTOTYPE_GETTERS(BUILTIN_TYPED_ARRAY_ACCESSOR_ENTRY) + }; + static constexpr std::array TYPED_ARRAY_PROTOTYPE_FUNCTIONS = { + BUILTIN_TYPED_ARRAY_PROTOTYPE_FUNCTIONS(BUILTIN_TYPED_ARRAY_FUNCTION_ENTRY) + }; +#undef BUILTIN_TYPED_ARRAY_FUNCTION_ENTRY +#undef BUILTIN_TYPED_ARRAY_ACCESSOR_ENTRY }; } // namespace panda::ecmascript::builtins diff --git a/ecmascript/builtins/builtins_weak_map.cpp b/ecmascript/builtins/builtins_weak_map.cpp index c0dcf62bc8ccaf3b0f5295b9a8826ca6fe007428..b2a0c216da4e0e5cfefa53fe6d26da962fbf4099 100644 --- a/ecmascript/builtins/builtins_weak_map.cpp +++ b/ecmascript/builtins/builtins_weak_map.cpp @@ -87,8 +87,8 @@ JSTaggedValue BuiltinsWeakMap::Delete(EcmaRuntimeCallInfo *argv) JSHandle weakMap(self); JSHandle key = GetCallArg(argv, 0); - // 5.if Type(key) is not Object, return false. - if (!key->IsHeapObject()) { + // 5.If CanBeHeldWeakly(key) is false, return false. + if (!JSTaggedValue::CanBeHeldWeakly(thread, key)) { return GetTaggedBoolean(false); } return GetTaggedBoolean(JSWeakMap::Delete(thread, weakMap, key)); @@ -106,10 +106,10 @@ JSTaggedValue BuiltinsWeakMap::Has(EcmaRuntimeCallInfo *argv) if (!self->IsJSWeakMap()) { THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSWeakMap.", JSTaggedValue::Exception()); } - JSWeakMap *jsWeakMap = JSWeakMap::Cast(*JSTaggedValue::ToObject(thread, self)); + JSWeakMap *jsWeakMap = JSWeakMap::Cast(self.GetTaggedValue().GetTaggedObject()); JSHandle key = GetCallArg(argv, 0); - // 5.if Type(key) is not Object, return false. - if (!key->IsHeapObject()) { + // 5.If CanBeHeldWeakly(key) is false, return false. + if (!JSTaggedValue::CanBeHeldWeakly(thread, key)) { return GetTaggedBoolean(false); } return GetTaggedBoolean(jsWeakMap->Has(key.GetTaggedValue())); @@ -127,9 +127,10 @@ JSTaggedValue BuiltinsWeakMap::Get(EcmaRuntimeCallInfo *argv) if (!self->IsJSWeakMap()) { THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSWeakMap.", JSTaggedValue::Exception()); } - JSWeakMap *jsWeakMap = JSWeakMap::Cast(*JSTaggedValue::ToObject(thread, self)); + JSWeakMap *jsWeakMap = JSWeakMap::Cast(self.GetTaggedValue().GetTaggedObject()); JSHandle key = GetCallArg(argv, 0); - if (!key->IsHeapObject()) { + // 4.If CanBeHeldWeakly(key) is false, return undefined. + if (!JSTaggedValue::CanBeHeldWeakly(thread, key)) { return JSTaggedValue::Undefined(); } return jsWeakMap->Get(key.GetTaggedValue()); @@ -150,11 +151,9 @@ JSTaggedValue BuiltinsWeakMap::Set(EcmaRuntimeCallInfo *argv) } JSHandle key = GetCallArg(argv, 0); - if (!key->IsHeapObject()) { - THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not an object.", JSTaggedValue::Exception()); - } - if (key->IsSymbol() || key->IsString()) { - THROW_TYPE_ERROR_AND_RETURN(thread, "key is Symblol or String", JSTaggedValue::Exception()); + // 4.If CanBeHeldWeakly(key) is false, throw a TypeError exception. + if (!JSTaggedValue::CanBeHeldWeakly(thread, key)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "invalid value used as weak map key.", JSTaggedValue::Exception()); } JSHandle value = GetCallArg(argv, 1); diff --git a/ecmascript/builtins/builtins_weak_ref.cpp b/ecmascript/builtins/builtins_weak_ref.cpp index 289c11c4cb8cee6e8d5a17e2830ac796935ce37e..ad4b85a330782bf6b7ed55fa18c6ce1d1f3917aa 100644 --- a/ecmascript/builtins/builtins_weak_ref.cpp +++ b/ecmascript/builtins/builtins_weak_ref.cpp @@ -17,7 +17,7 @@ #include "ecmascript/ecma_vm.h" #include "ecmascript/js_weak_ref.h" -#include "ecmascript/object_factory.h" +#include "ecmascript/object_factory-inl.h" namespace panda::ecmascript::builtins { JSTaggedValue BuiltinsWeakRef::WeakRefConstructor(EcmaRuntimeCallInfo *argv) @@ -32,10 +32,10 @@ JSTaggedValue BuiltinsWeakRef::WeakRefConstructor(EcmaRuntimeCallInfo *argv) if (newTarget->IsUndefined()) { THROW_TYPE_ERROR_AND_RETURN(thread, "new target can't be undefined", JSTaggedValue::Exception()); } - // 2. If Type(target) is not Object, throw a TypeError exception. + // 2. If CanBeHeldWeakly(target) is false, throw a TypeError exception. JSHandle target = GetCallArg(argv, 0); - if (!target->IsECMAObject()) { - THROW_TYPE_ERROR_AND_RETURN(thread, "target is not object", JSTaggedValue::Exception()); + if (!JSTaggedValue::CanBeHeldWeakly(thread, target)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "target invalid", JSTaggedValue::Exception()); } // 3. Let weakRef be ? OrdinaryCreateFromConstructor(NewTarget, "%WeakRef.prototype%", « [[WeakRefTarget]] »). JSHandle constructor = GetConstructor(argv); diff --git a/ecmascript/builtins/builtins_weak_set.cpp b/ecmascript/builtins/builtins_weak_set.cpp index 4205ef913ebf4321a394bab93c64ee5e0b22e6cc..12d71e83ffaba3718702366e1850d7a2362683d7 100644 --- a/ecmascript/builtins/builtins_weak_set.cpp +++ b/ecmascript/builtins/builtins_weak_set.cpp @@ -120,15 +120,12 @@ JSTaggedValue BuiltinsWeakSet::Add(EcmaRuntimeCallInfo *argv) } JSHandle value(GetCallArg(argv, 0)); - if (!value->IsHeapObject()) { - THROW_TYPE_ERROR_AND_RETURN(thread, "value is not an object", JSTaggedValue::Exception()); + // 4.If CanBeHeldWeakly(value) is false, throw a TypeError exception. + if (!JSTaggedValue::CanBeHeldWeakly(thread, value)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "invalid value used in weak set", JSTaggedValue::Exception()); } - if (value->IsSymbol() || value->IsString()) { - THROW_TYPE_ERROR_AND_RETURN(thread, "value is Symblol or String", JSTaggedValue::Exception()); - } - - JSHandle weakSet(thread, JSWeakSet::Cast(*JSTaggedValue::ToObject(thread, self))); + JSHandle weakSet(self); JSWeakSet::Add(thread, weakSet, value); return weakSet.GetTaggedValue(); } @@ -146,9 +143,10 @@ JSTaggedValue BuiltinsWeakSet::Delete(EcmaRuntimeCallInfo *argv) THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSWeakSet", JSTaggedValue::Exception()); } - JSHandle weakSet(thread, JSWeakSet::Cast(*JSTaggedValue::ToObject(thread, self))); + JSHandle weakSet(self); JSHandle value = GetCallArg(argv, 0); - if (!value->IsHeapObject()) { + // 4.If CanBeHeldWeakly(value) is false, return false. + if (!JSTaggedValue::CanBeHeldWeakly(thread, value)) { GetTaggedBoolean(false); } return GetTaggedBoolean(JSWeakSet::Delete(thread, weakSet, value)); @@ -166,9 +164,10 @@ JSTaggedValue BuiltinsWeakSet::Has(EcmaRuntimeCallInfo *argv) if (!self->IsJSWeakSet()) { THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSWeakSet", JSTaggedValue::Exception()); } - JSWeakSet *jsWeakSet = JSWeakSet::Cast(*JSTaggedValue::ToObject(thread, self)); + JSWeakSet *jsWeakSet = JSWeakSet::Cast(self.GetTaggedValue().GetTaggedObject()); JSHandle value = GetCallArg(argv, 0); - if (!value->IsHeapObject()) { + // 4.If CanBeHeldWeakly(value) is false, return false. + if (!JSTaggedValue::CanBeHeldWeakly(thread, value)) { GetTaggedBoolean(false); } return GetTaggedBoolean(jsWeakSet->Has(value.GetTaggedValue())); diff --git a/ecmascript/builtins/tests/builtins_array_test.cpp b/ecmascript/builtins/tests/builtins_array_test.cpp index 248ac346b3220d08a8f47c265be1680b150ddfde..be2ddd938cc4f6f6b36e286a13ded4cbbef9af61 100644 --- a/ecmascript/builtins/tests/builtins_array_test.cpp +++ b/ecmascript/builtins/tests/builtins_array_test.cpp @@ -35,6 +35,26 @@ using namespace panda::ecmascript; using namespace panda::ecmascript::builtins; using namespace panda::ecmascript::base; +constexpr int32_t INT_VALUE_0 = 0; +constexpr int32_t INT_VALUE_1 = 1; +constexpr int32_t INT_VALUE_2 = 2; +constexpr int32_t INT_VALUE_3 = 3; +constexpr int32_t INT_VALUE_4 = 4; +constexpr int32_t INT_VALUE_50 = 50; +constexpr int32_t INT_VALUE_200 = 200; +constexpr int32_t INT_VALUE_666 = 666; +constexpr uint32_t RUNTIME_CALL_INFO_PARA_0 = 0; +constexpr uint32_t RUNTIME_CALL_INFO_PARA_1 = 1; +constexpr uint32_t RUNTIME_CALL_INFO_PARA_NUM_4 = 4; +constexpr uint32_t RUNTIME_CALL_INFO_PARA_NUM_8 = 8; +constexpr uint32_t RUNTIME_CALL_INFO_PARA_NUM_10 = 10; + +enum class ArrayIndex { + ARRAY_INDEX_0, + ARRAY_INDEX_1, + ARRAY_INDEX_2, + ARRAY_INDEX_3 +}; namespace panda::test { using Array = ecmascript::builtins::BuiltinsArray; @@ -141,6 +161,30 @@ public: return GetTaggedBoolean(false); } + static JSTaggedValue TestFindLastFunc(EcmaRuntimeCallInfo *argv) + { + uint32_t argc = argv->GetArgsNumber(); + if (argc > 0) { + // 20 : test case + if (GetCallArg(argv, 0)->GetInt() > 20) { + return GetTaggedBoolean(true); + } + } + return GetTaggedBoolean(false); + } + + static JSTaggedValue TestFindLastIndexFunc(EcmaRuntimeCallInfo *argv) + { + uint32_t argc = argv->GetArgsNumber(); + if (argc > 0) { + // 20 : test case + if (GetCallArg(argv, 0)->GetInt() > 20) { + return GetTaggedBoolean(true); + } + } + return GetTaggedBoolean(false); + } + static JSTaggedValue TestReduceFunc(EcmaRuntimeCallInfo *argv) { int accumulator = GetCallArg(argv, 0)->GetInt(); @@ -928,7 +972,53 @@ HWTEST_F_L0(BuiltinsArrayTest, ForEach) EXPECT_EQ(jsArray->GetArrayLength(), 3U); } -// 22.1.3.11 new Array(1,2,3,4,3).IndexOf(searchElement [ , fromIndex ]) +#define ARRAY_DEFINE_OWN_PROPERTY(dest, index, value) \ + do { \ + JSHandle key(thread, JSTaggedValue(index)); \ + PropertyDescriptor desc(thread, JSHandle(thread, JSTaggedValue(value)), true, true, true); \ + JSArray::DefineOwnProperty(thread, dest, key, desc); \ + } while (false) + +#define ARRAY_BUILTIN_METHOD_TEST_CASE_ARG0(method, target, expected) \ + do { \ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); \ + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); \ + ecmaRuntimeCallInfo->SetThis((target).GetTaggedValue()); \ + \ + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo); \ + JSTaggedValue result = Array::method(ecmaRuntimeCallInfo); \ + TestHelper::TearDownFrame(thread, prev); \ + ASSERT_TRUE(JSTaggedValue::StrictEqual(result, JSTaggedValue(expected))); \ + } while (false) + +#define ARRAY_BUILTIN_METHOD_TEST_CASE_ARG1(method, target, expected, arg0) \ + do { \ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); \ + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); \ + ecmaRuntimeCallInfo->SetThis((target).GetTaggedValue()); \ + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(arg0)); \ + \ + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo); \ + JSTaggedValue result = Array::method(ecmaRuntimeCallInfo); \ + TestHelper::TearDownFrame(thread, prev); \ + ASSERT_TRUE(JSTaggedValue::StrictEqual(result, JSTaggedValue(expected))); \ + } while (false) + +#define ARRAY_BUILTIN_METHOD_TEST_CASE_ARG2(method, target, expected, arg0, arg1) \ + do { \ + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); \ + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); \ + ecmaRuntimeCallInfo->SetThis((target).GetTaggedValue()); \ + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(arg0)); \ + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(arg1)); \ + \ + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo); \ + JSTaggedValue result = Array::method(ecmaRuntimeCallInfo); \ + TestHelper::TearDownFrame(thread, prev); \ + ASSERT_TRUE(JSTaggedValue::StrictEqual(result, JSTaggedValue(expected))); \ + } while (false) + +// 22.1.3.11 Array.IndexOf(searchElement [ , fromIndex ]) HWTEST_F_L0(BuiltinsArrayTest, IndexOf) { JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); @@ -937,67 +1027,54 @@ HWTEST_F_L0(BuiltinsArrayTest, IndexOf) JSHandle obj(thread, arr); EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); - JSHandle key0(thread, JSTaggedValue(0)); - PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); - JSArray::DefineOwnProperty(thread, obj, key0, desc0); - JSHandle key1(thread, JSTaggedValue(1)); - PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); - JSArray::DefineOwnProperty(thread, obj, key1, desc1); - JSHandle key2(thread, JSTaggedValue(2)); - PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); - JSArray::DefineOwnProperty(thread, obj, key2, desc2); - JSHandle key3(thread, JSTaggedValue(3)); - PropertyDescriptor desc3(thread, JSHandle(thread, JSTaggedValue(4)), true, true, true); - JSArray::DefineOwnProperty(thread, obj, key3, desc3); - JSHandle key4(thread, JSTaggedValue(4)); - PropertyDescriptor desc4(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); - JSArray::DefineOwnProperty(thread, obj, key4, desc4); - - auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); - ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); - ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); - ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(3))); - ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(0))); - - [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1); - JSTaggedValue result = Array::IndexOf(ecmaRuntimeCallInfo1); - TestHelper::TearDownFrame(thread, prev); - ASSERT_EQ(result.GetRawData(), JSTaggedValue(2).GetRawData()); - - auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); - ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); - ecmaRuntimeCallInfo2->SetThis(obj.GetTaggedValue()); - ecmaRuntimeCallInfo2->SetCallArg(0, JSTaggedValue(static_cast(3))); - ecmaRuntimeCallInfo2->SetCallArg(1, JSTaggedValue(static_cast(3))); - - prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2); - result = Array::IndexOf(ecmaRuntimeCallInfo2); - TestHelper::TearDownFrame(thread, prev); - ASSERT_EQ(result.GetRawData(), JSTaggedValue(4).GetRawData()); - - auto ecmaRuntimeCallInfo3 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); - ecmaRuntimeCallInfo3->SetFunction(JSTaggedValue::Undefined()); - ecmaRuntimeCallInfo3->SetThis(obj.GetTaggedValue()); - ecmaRuntimeCallInfo3->SetCallArg(0, JSTaggedValue(static_cast(5))); - ecmaRuntimeCallInfo3->SetCallArg(1, JSTaggedValue(static_cast(0))); - - prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo3); - result = Array::IndexOf(ecmaRuntimeCallInfo3); - TestHelper::TearDownFrame(thread, prev); - ASSERT_EQ(result.GetRawData(), JSTaggedValue(-1).GetRawData()); - - auto ecmaRuntimeCallInfo4 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); - ecmaRuntimeCallInfo4->SetFunction(JSTaggedValue::Undefined()); - ecmaRuntimeCallInfo4->SetThis(obj.GetTaggedValue()); - ecmaRuntimeCallInfo4->SetCallArg(0, JSTaggedValue(static_cast(3))); - - prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo4); - result = Array::IndexOf(ecmaRuntimeCallInfo4); - TestHelper::TearDownFrame(thread, prev); - ASSERT_EQ(result.GetRawData(), JSTaggedValue(2).GetRawData()); + // arr = [1, 2, 3, 4, 3, 0, 2.0, +0.0, 3.0, -0.0, , , undefined] + ARRAY_DEFINE_OWN_PROPERTY(obj, 0, 1); + ARRAY_DEFINE_OWN_PROPERTY(obj, 1, 2); + ARRAY_DEFINE_OWN_PROPERTY(obj, 2, 3); + ARRAY_DEFINE_OWN_PROPERTY(obj, 3, 4); + ARRAY_DEFINE_OWN_PROPERTY(obj, 4, 3); + ARRAY_DEFINE_OWN_PROPERTY(obj, 5, 0); + ARRAY_DEFINE_OWN_PROPERTY(obj, 6, 2.0); + ARRAY_DEFINE_OWN_PROPERTY(obj, 7, +0.0); + ARRAY_DEFINE_OWN_PROPERTY(obj, 8, 3.0); + ARRAY_DEFINE_OWN_PROPERTY(obj, 9, -0.0); + ARRAY_DEFINE_OWN_PROPERTY(obj, 12, JSTaggedValue::Undefined()); + + // arr.indexOf(3, 0) == 2 + ARRAY_BUILTIN_METHOD_TEST_CASE_ARG2(IndexOf, obj, 2, 3, 0); + // arr.indexOf(3, 3) == 4 + ARRAY_BUILTIN_METHOD_TEST_CASE_ARG2(IndexOf, obj, 4, 3, 3); + // arr.indexOf(5, 0) == -1 + ARRAY_BUILTIN_METHOD_TEST_CASE_ARG2(IndexOf, obj, -1, 5, 0); + // arr.indexOf(3) == 2 + ARRAY_BUILTIN_METHOD_TEST_CASE_ARG1(IndexOf, obj, 2, 3); + + // Expects int32_t(x) and double(x) to be strictly equal + // arr.indexOf(3.0) == 2 + ARRAY_BUILTIN_METHOD_TEST_CASE_ARG1(IndexOf, obj, 2, 3.0); + // arr.indexOf(3, 5) == 8 + ARRAY_BUILTIN_METHOD_TEST_CASE_ARG2(IndexOf, obj, 8, 3, 5); + + // Expects 0, +0.0, -0.0 to be strictly equal + // arr.indexOf(+0.0) == 5 + ARRAY_BUILTIN_METHOD_TEST_CASE_ARG1(IndexOf, obj, 5, +0.0); + // arr.indexOf(-0.0) == 5 + ARRAY_BUILTIN_METHOD_TEST_CASE_ARG1(IndexOf, obj, 5, -0.0); + // arr.indexOf(0, 6) == 7 + ARRAY_BUILTIN_METHOD_TEST_CASE_ARG2(IndexOf, obj, 7, 0, 6); + // arr.indexOf(-0.0, 6) == 7 + ARRAY_BUILTIN_METHOD_TEST_CASE_ARG2(IndexOf, obj, 7, -0.0, 6); + // arr.indexOf(0, 8) == 9 + ARRAY_BUILTIN_METHOD_TEST_CASE_ARG2(IndexOf, obj, 9, 0, 8); + // arr.indexOf(+0.0, 8) == 9 + ARRAY_BUILTIN_METHOD_TEST_CASE_ARG2(IndexOf, obj, 9, +0.0, 8); + + // Expects undefined to be found + // arr.indexOf() == 12, where the first argument is undefined + ARRAY_BUILTIN_METHOD_TEST_CASE_ARG0(IndexOf, obj, 12); } -// 22.1.3.14 new Array(1,2,3,4,3).LastIndexOf(searchElement [ , fromIndex ]) +// 22.1.3.14 Array.LastIndexOf(searchElement [ , fromIndex ]) HWTEST_F_L0(BuiltinsArrayTest, LastIndexOf) { JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); @@ -1006,68 +1083,50 @@ HWTEST_F_L0(BuiltinsArrayTest, LastIndexOf) JSHandle obj(thread, arr); EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); - JSHandle key0(thread, JSTaggedValue(0)); - PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(1)), true, true, true); - JSArray::DefineOwnProperty(thread, obj, key0, desc0); - JSHandle key1(thread, JSTaggedValue(1)); - PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); - JSArray::DefineOwnProperty(thread, obj, key1, desc1); - JSHandle key2(thread, JSTaggedValue(2)); - PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); - JSArray::DefineOwnProperty(thread, obj, key2, desc2); - JSHandle key3(thread, JSTaggedValue(3)); - PropertyDescriptor desc3(thread, JSHandle(thread, JSTaggedValue(4)), true, true, true); - JSArray::DefineOwnProperty(thread, obj, key3, desc3); - JSHandle key4(thread, JSTaggedValue(4)); - PropertyDescriptor desc4(thread, JSHandle(thread, JSTaggedValue(3)), true, true, true); - JSArray::DefineOwnProperty(thread, obj, key4, desc4); - - // new Array(1,2,3,4,3).LastIndexOf(3,4) - auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); - ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); - ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); - ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(3))); - ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(4))); - - [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1); - JSTaggedValue result = Array::LastIndexOf(ecmaRuntimeCallInfo1); - TestHelper::TearDownFrame(thread, prev); - ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast(4)).GetRawData()); - - // new Array(1,2,3,4,3).LastIndexOf(3,3) - auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); - ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); - ecmaRuntimeCallInfo2->SetThis(obj.GetTaggedValue()); - ecmaRuntimeCallInfo2->SetCallArg(0, JSTaggedValue(static_cast(3))); - ecmaRuntimeCallInfo2->SetCallArg(1, JSTaggedValue(static_cast(3))); - - prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2); - result = Array::LastIndexOf(ecmaRuntimeCallInfo2); - TestHelper::TearDownFrame(thread, prev); - ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast(2)).GetRawData()); - - // new Array(1,2,3,4,3).LastIndexOf(5,4) - auto ecmaRuntimeCallInfo3 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); - ecmaRuntimeCallInfo3->SetFunction(JSTaggedValue::Undefined()); - ecmaRuntimeCallInfo3->SetThis(obj.GetTaggedValue()); - ecmaRuntimeCallInfo3->SetCallArg(0, JSTaggedValue(static_cast(5))); - ecmaRuntimeCallInfo3->SetCallArg(1, JSTaggedValue(static_cast(4))); - - prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo3); - result = Array::LastIndexOf(ecmaRuntimeCallInfo3); - TestHelper::TearDownFrame(thread, prev); - ASSERT_EQ(result.GetRawData(), JSTaggedValue(-1).GetRawData()); - - // new Array(1,2,3,4,3).LastIndexOf(3) - auto ecmaRuntimeCallInfo4 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); - ecmaRuntimeCallInfo4->SetFunction(JSTaggedValue::Undefined()); - ecmaRuntimeCallInfo4->SetThis(obj.GetTaggedValue()); - ecmaRuntimeCallInfo4->SetCallArg(0, JSTaggedValue(static_cast(3))); - - prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo4); - result = Array::LastIndexOf(ecmaRuntimeCallInfo4); - TestHelper::TearDownFrame(thread, prev); - ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast(4)).GetRawData()); + // arr = [1, 2, 3, 4, 3, 0, 2.0, +0.0, 3.0, -0.0, , , undefined, , , -1] + ARRAY_DEFINE_OWN_PROPERTY(obj, 0, 1); + ARRAY_DEFINE_OWN_PROPERTY(obj, 1, 2); + ARRAY_DEFINE_OWN_PROPERTY(obj, 2, 3); + ARRAY_DEFINE_OWN_PROPERTY(obj, 3, 4); + ARRAY_DEFINE_OWN_PROPERTY(obj, 4, 3); + ARRAY_DEFINE_OWN_PROPERTY(obj, 5, 0); + ARRAY_DEFINE_OWN_PROPERTY(obj, 6, 2.0); + ARRAY_DEFINE_OWN_PROPERTY(obj, 7, +0.0); + ARRAY_DEFINE_OWN_PROPERTY(obj, 8, 3.0); + ARRAY_DEFINE_OWN_PROPERTY(obj, 9, -0.0); + ARRAY_DEFINE_OWN_PROPERTY(obj, 12, JSTaggedValue::Undefined()); + ARRAY_DEFINE_OWN_PROPERTY(obj, 15, -1); + + // arr.lastIndexOf(3, 4) == 4 + ARRAY_BUILTIN_METHOD_TEST_CASE_ARG2(LastIndexOf, obj, 4, 3, 4); + // arr.lastIndexOf(3, 3) == 2 + ARRAY_BUILTIN_METHOD_TEST_CASE_ARG2(LastIndexOf, obj, 2, 3, 3); + // arr.lastIndexOf(5, 4) == -1 + ARRAY_BUILTIN_METHOD_TEST_CASE_ARG2(LastIndexOf, obj, -1, 5, 4); + + // Expects int32_t(x) and double(x) to be strictly equal + // arr.lastIndexOf(3) == 8 + ARRAY_BUILTIN_METHOD_TEST_CASE_ARG1(LastIndexOf, obj, 8, 3); + // arr.lastIndexOf(1.0) == 0 + ARRAY_BUILTIN_METHOD_TEST_CASE_ARG1(LastIndexOf, obj, 0, 1.0); + + // Expects 0, +0.0, -0.0 to be strictly equal + // arr.indexOf(+0.0) == 9 + ARRAY_BUILTIN_METHOD_TEST_CASE_ARG1(LastIndexOf, obj, 9, +0.0); + // arr.indexOf(0) == 9 + ARRAY_BUILTIN_METHOD_TEST_CASE_ARG1(LastIndexOf, obj, 9, 0); + // arr.indexOf(0, 8) == 7 + ARRAY_BUILTIN_METHOD_TEST_CASE_ARG2(LastIndexOf, obj, 7, 0, 8); + // arr.indexOf(-0.0, 8) == 7 + ARRAY_BUILTIN_METHOD_TEST_CASE_ARG2(LastIndexOf, obj, 7, -0.0, 8); + // arr.indexOf(-0.0, 6) == 5 + ARRAY_BUILTIN_METHOD_TEST_CASE_ARG2(LastIndexOf, obj, 5, -0.0, 6); + // arr.indexOf(+0.0, 6) == 5 + ARRAY_BUILTIN_METHOD_TEST_CASE_ARG2(LastIndexOf, obj, 5, +0.0, 6); + + // Expects undefined to be found + // arr.indexOf() == 12, where the first argument is undefined + ARRAY_BUILTIN_METHOD_TEST_CASE_ARG0(LastIndexOf, obj, 12); } // 22.1.3.11 new Array().Pop() @@ -1706,4 +1765,247 @@ HWTEST_F_L0(BuiltinsArrayTest, At) TestHelper::TearDownFrame(thread, prev6); ASSERT_EQ(result, JSTaggedValue::Undefined()); } + +HWTEST_F_L0(BuiltinsArrayTest, With) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = + JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(INT_VALUE_0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), + lengthKeyHandle).GetValue()->GetInt(), INT_VALUE_0); + + JSHandle key0(thread, JSTaggedValue(static_cast(ArrayIndex::ARRAY_INDEX_0))); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(INT_VALUE_0)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(static_cast(ArrayIndex::ARRAY_INDEX_1))); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(INT_VALUE_1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(static_cast(ArrayIndex::ARRAY_INDEX_2))); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(INT_VALUE_2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + auto ecmaRuntimeCallInfo1 = + TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), RUNTIME_CALL_INFO_PARA_NUM_8); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(RUNTIME_CALL_INFO_PARA_0, + JSTaggedValue(static_cast((ArrayIndex::ARRAY_INDEX_1)))); + ecmaRuntimeCallInfo1->SetCallArg(RUNTIME_CALL_INFO_PARA_1, JSTaggedValue(INT_VALUE_3)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1); + JSTaggedValue result = Array::With(ecmaRuntimeCallInfo1); + TestHelper::TearDownFrame(thread, prev); + + EXPECT_TRUE(result.IsECMAObject()); + JSHandle resultArr = + JSHandle(thread, JSTaggedValue(static_cast(result.GetRawData()))); + EXPECT_EQ(JSArray::GetProperty(thread, resultArr, key0).GetValue()->GetInt(), INT_VALUE_0); + EXPECT_EQ(JSArray::GetProperty(thread, resultArr, key1).GetValue()->GetInt(), INT_VALUE_3); + EXPECT_EQ(JSArray::GetProperty(thread, resultArr, key2).GetValue()->GetInt(), INT_VALUE_2); +} + +HWTEST_F_L0(BuiltinsArrayTest, ToSorted) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = + JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(INT_VALUE_0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), + lengthKeyHandle).GetValue()->GetInt(), INT_VALUE_0); + JSHandle key0(thread, JSTaggedValue(INT_VALUE_0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(INT_VALUE_3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(INT_VALUE_1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(INT_VALUE_2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(INT_VALUE_2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(INT_VALUE_1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + auto ecmaRuntimeCallInfo1 = + TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), RUNTIME_CALL_INFO_PARA_NUM_4); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1); + JSTaggedValue result2 = Array::ToSorted(ecmaRuntimeCallInfo1); + TestHelper::TearDownFrame(thread, prev); + + EXPECT_TRUE(result2.IsECMAObject()); + JSHandle resultArr = + JSHandle(thread, JSTaggedValue(static_cast(result2.GetRawData()))); + EXPECT_EQ(JSArray::GetProperty(thread, resultArr, key0).GetValue()->GetInt(), INT_VALUE_1); + EXPECT_EQ(JSArray::GetProperty(thread, resultArr, key1).GetValue()->GetInt(), INT_VALUE_2); + EXPECT_EQ(JSArray::GetProperty(thread, resultArr, key2).GetValue()->GetInt(), INT_VALUE_3); +} + +HWTEST_F_L0(BuiltinsArrayTest, ToSpliced) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = + JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(INT_VALUE_0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), + lengthKeyHandle).GetValue()->GetInt(), INT_VALUE_0); + JSHandle key0(thread, JSTaggedValue(INT_VALUE_0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(INT_VALUE_0)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(INT_VALUE_1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(INT_VALUE_1)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(INT_VALUE_2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(INT_VALUE_2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + auto ecmaRuntimeCallInfo1 = + TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), RUNTIME_CALL_INFO_PARA_NUM_10); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(INT_VALUE_0, JSTaggedValue(INT_VALUE_1)); + ecmaRuntimeCallInfo1->SetCallArg(INT_VALUE_1, JSTaggedValue(INT_VALUE_1)); + ecmaRuntimeCallInfo1->SetCallArg(INT_VALUE_2, JSTaggedValue(INT_VALUE_666)); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1); + JSTaggedValue result2 = Array::ToSpliced(ecmaRuntimeCallInfo1); + TestHelper::TearDownFrame(thread, prev); + + EXPECT_TRUE(result2.IsECMAObject()); + JSHandle resultArr = + JSHandle(thread, JSTaggedValue(static_cast(result2.GetRawData()))); + EXPECT_EQ(JSArray::GetProperty(thread, resultArr, key0).GetValue()->GetInt(), INT_VALUE_0); + EXPECT_EQ(JSArray::GetProperty(thread, resultArr, key1).GetValue()->GetInt(), INT_VALUE_666); + EXPECT_EQ(JSArray::GetProperty(thread, resultArr, key2).GetValue()->GetInt(), INT_VALUE_2); +} + +HWTEST_F_L0(BuiltinsArrayTest, FindLast) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + ObjectFactory *factory = ecmaVM->GetFactory(); + + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + // arr [50, 40, 2] + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(50)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(40)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(2)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + JSHandle jsArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestClass::TestFindLastFunc)); + + auto ecmaRuntimeCallInfo1 = + TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); // 8 means 2 call args + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(1, jsArray.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1); + JSTaggedValue result = Array::FindLast(ecmaRuntimeCallInfo1); + TestHelper::TearDownFrame(thread, prev); + + EXPECT_EQ(result.GetRawData(), JSTaggedValue(40).GetRawData()); +} + +HWTEST_F_L0(BuiltinsArrayTest, FindLastIndex) +{ + auto ecmaVM = thread->GetEcmaVM(); + JSHandle env = ecmaVM->GetGlobalEnv(); + ObjectFactory *factory = ecmaVM->GetFactory(); + + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), lengthKeyHandle).GetValue()->GetInt(), 0); + + // arr [50, 40, 30] + JSHandle key0(thread, JSTaggedValue(0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(50)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + + JSHandle key1(thread, JSTaggedValue(1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(40)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + + JSHandle key2(thread, JSTaggedValue(2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(30)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + JSHandle jsArray(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestClass::TestFindLastIndexFunc)); + + auto ecmaRuntimeCallInfo1 = + TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); // 8 means 2 call args + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(1, jsArray.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1); + JSTaggedValue result = Array::FindLastIndex(ecmaRuntimeCallInfo1); + TestHelper::TearDownFrame(thread, prev); + + EXPECT_EQ(result.GetRawData(), JSTaggedValue(static_cast(2)).GetRawData()); +} + +HWTEST_F_L0(BuiltinsArrayTest, ToReversed) +{ + JSHandle lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString(); + JSArray *arr = JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject()); + EXPECT_TRUE(arr != nullptr); + JSHandle obj(thread, arr); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), + lengthKeyHandle).GetValue()->GetInt(), INT_VALUE_0); + JSHandle key0(thread, JSTaggedValue(INT_VALUE_0)); + PropertyDescriptor desc0(thread, JSHandle(thread, JSTaggedValue(INT_VALUE_50)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key0, desc0); + JSHandle key1(thread, JSTaggedValue(INT_VALUE_1)); + PropertyDescriptor desc1(thread, JSHandle(thread, JSTaggedValue(INT_VALUE_200)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key1, desc1); + JSHandle key2(thread, JSTaggedValue(INT_VALUE_2)); + PropertyDescriptor desc2(thread, JSHandle(thread, JSTaggedValue(INT_VALUE_3)), true, true, true); + JSArray::DefineOwnProperty(thread, obj, key2, desc2); + + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), INT_VALUE_4); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1); + JSTaggedValue result = Array::ToReversed(ecmaRuntimeCallInfo1); + TestHelper::TearDownFrame(thread, prev); + JSTaggedValue value(static_cast(result.GetRawData())); + ASSERT_TRUE(value.IsECMAObject()); + + PropertyDescriptor descRes(thread); + JSHandle valueHandle(thread, value); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(valueHandle), + lengthKeyHandle).GetValue()->GetInt(), INT_VALUE_3); + JSObject::GetOwnProperty(thread, valueHandle, key0, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(INT_VALUE_3)); + JSObject::GetOwnProperty(thread, valueHandle, key1, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(INT_VALUE_200)); + JSObject::GetOwnProperty(thread, valueHandle, key2, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(INT_VALUE_50)); + EXPECT_EQ(JSArray::GetProperty(thread, JSHandle(obj), + lengthKeyHandle).GetValue()->GetInt(), INT_VALUE_3); + JSObject::GetOwnProperty(thread, obj, key0, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(INT_VALUE_50)); + JSObject::GetOwnProperty(thread, obj, key1, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(INT_VALUE_200)); + JSObject::GetOwnProperty(thread, obj, key2, descRes); + ASSERT_EQ(descRes.GetValue().GetTaggedValue(), JSTaggedValue(INT_VALUE_3)); +} } // namespace panda::test diff --git a/ecmascript/builtins/tests/builtins_errors_test.cpp b/ecmascript/builtins/tests/builtins_errors_test.cpp index 307341fd76589456b3c3e240c23bb562ad5e5fb5..025e6201294ec8349232589d203179332e323798 100644 --- a/ecmascript/builtins/tests/builtins_errors_test.cpp +++ b/ecmascript/builtins/tests/builtins_errors_test.cpp @@ -40,6 +40,7 @@ using TypeError = builtins::BuiltinsTypeError; using URIError = builtins::BuiltinsURIError; using EvalError = builtins::BuiltinsEvalError; using SyntaxError = builtins::BuiltinsSyntaxError; +using AggregateError = builtins::BuiltinsAggregateError; using JSType = ecmascript::JSType; class BuiltinsErrorsTest : public testing::Test { @@ -980,4 +981,55 @@ HWTEST_F_L0(BuiltinsErrorsTest, EvalErrorToString) EXPECT_EQ(EcmaStringAccessor::Compare(instance, factory->NewFromASCII("EvalError: This is EvalError!"), resultHandle), 0); } + +/* + * @tc.name: AggregateErrorParameterConstructor + * @tc.desc: new AggregateError([], "Hello AggregateError", {cause: "error cause"}) + * @tc.type: FUNC + */ +HWTEST_F_L0(BuiltinsErrorsTest, AggregateErrorParameterConstructor) +{ + ObjectFactory *factory = instance->GetFactory(); + JSHandle env = instance->GetGlobalEnv(); + + JSHandle error(env->GetAggregateErrorFunction()); + JSHandle paramMsg(factory->NewFromASCII("Hello AggregateError!")); + + JSHandle errayFunc = env->GetArrayFunction(); + JSHandle newArray = factory->NewJSObjectByConstructor(JSHandle(errayFunc), errayFunc); + + JSHandle causeKey = thread->GlobalConstants()->GetHandledCauseString(); + JSHandle objFun = env->GetObjectFunction(); + JSHandle optionsObj = factory->NewJSObjectByConstructor(JSHandle(objFun), objFun); + JSHandle causeValue(factory->NewFromASCII("error cause")); // test error cause + JSObject::SetProperty(thread, optionsObj, causeKey, causeValue); + + auto ecmaRuntimeCallInfo = + TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*error), 10); // 10 means 3 call args + ecmaRuntimeCallInfo->SetFunction(error.GetTaggedValue()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue(*error)); + ecmaRuntimeCallInfo->SetCallArg(0, newArray.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, paramMsg.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(2, optionsObj.GetTaggedValue()); // 2 means the options arg + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo); + JSTaggedValue result = AggregateError::AggregateErrorConstructor(ecmaRuntimeCallInfo); + EXPECT_TRUE(result.IsECMAObject()); + + JSHandle errorObject(thread, reinterpret_cast(result.GetRawData())); + JSHandle msgKey(factory->NewFromASCII("message")); + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + + JSHandle msgValue(JSObject::GetProperty(thread, errorObject, msgKey).GetValue()); + ASSERT_EQ(EcmaStringAccessor::Compare(instance, + factory->NewFromASCII("Hello AggregateError!"), JSHandle(msgValue)), 0); + + JSHandle nameValue(JSObject::GetProperty(thread, errorObject, nameKey).GetValue()); + ASSERT_EQ(EcmaStringAccessor::Compare(instance, + factory->NewFromASCII("AggregateError"), JSHandle(nameValue)), 0); + + JSHandle errCauseValue(JSObject::GetProperty(thread, errorObject, causeKey).GetValue()); + ASSERT_EQ(EcmaStringAccessor::Compare(instance, + factory->NewFromASCII("error cause"), JSHandle(errCauseValue)), 0); +} } // namespace panda::test diff --git a/ecmascript/builtins/tests/builtins_finalization_registry_test.cpp b/ecmascript/builtins/tests/builtins_finalization_registry_test.cpp index 2fda459022ad52f7f62dae89c3dd0862edfed871..9ff2f2ad6ba16637418ef00f16894b5c4e36957c 100644 --- a/ecmascript/builtins/tests/builtins_finalization_registry_test.cpp +++ b/ecmascript/builtins/tests/builtins_finalization_registry_test.cpp @@ -441,4 +441,96 @@ HWTEST_F_L0(BuiltinsFinalizationRegistryTest, Unregister2) vm->SetEnableForceGC(true); ASSERT_EQ(testValue, 0); } + +// finalizationRegistry.Register(target, heldValue [ , unregisterToken ]) target and unregisterToken Symbol +HWTEST_F_L0(BuiltinsFinalizationRegistryTest, RegisterTargetSymbol) +{ + testValue = 0; + EcmaVM *vm = thread->GetEcmaVM(); + + JSTaggedValue result = CreateFinalizationRegistryConstructor(thread); + JSHandle jsfinalizationRegistry(thread, result); + + vm->SetEnableForceGC(false); + JSTaggedValue target = JSTaggedValue::Undefined(); + JSTaggedValue target1 = JSTaggedValue::Undefined(); + { + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle symbol1 = thread->GetEcmaVM()->GetFactory()->NewJSSymbol(); + JSHandle symbol2 = thread->GetEcmaVM()->GetFactory()->NewJSSymbol(); + target = symbol1.GetTaggedValue(); + target1 = symbol2.GetTaggedValue(); + auto ecmaRuntimeCallInfo = + TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); // 10 means 3 call args + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsfinalizationRegistry.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, target); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(10)); + ecmaRuntimeCallInfo->SetCallArg(2, target); // 2 means the unregisterToken arg + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo); + BuiltinsFinalizationRegistry::Register(ecmaRuntimeCallInfo); + TestHelper::TearDownFrame(thread, prev); + + auto ecmaRuntimeCallInfo1 = + TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); // 10 means 3 call args + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(jsfinalizationRegistry.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, target1); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(10)); + ecmaRuntimeCallInfo1->SetCallArg(2, target1); // 2 means the unregisterToken arg + + [[maybe_unused]] auto prev1 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1); + BuiltinsFinalizationRegistry::Register(ecmaRuntimeCallInfo1); + TestHelper::TearDownFrame(thread, prev1); + } + vm->CollectGarbage(TriggerGCType::FULL_GC); + if (!thread->HasPendingException()) { + job::MicroJobQueue::ExecutePendingJob(thread, vm->GetJSThread()->GetCurrentEcmaContext()->GetMicroJobQueue()); + } + vm->SetEnableForceGC(true); + ASSERT_EQ(testValue, 2); +} + +// finalizationRegistry.Unregister(unregisterToken) unregisterToken Symbol +HWTEST_F_L0(BuiltinsFinalizationRegistryTest, UnregisterTokenSymbol) +{ + testValue = 0; + EcmaVM *vm = thread->GetEcmaVM(); + + JSTaggedValue result = CreateFinalizationRegistryConstructor(thread); + JSHandle jsfinalizationRegistry(thread, result); + vm->SetEnableForceGC(false); + JSTaggedValue target = JSTaggedValue::Undefined(); + { + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle symbol = thread->GetEcmaVM()->GetFactory()->NewJSSymbol(); + target = symbol.GetTaggedValue(); + auto ecmaRuntimeCallInfo = + TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 10); // 10 means 3 call args + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(jsfinalizationRegistry.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, target); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(10)); + ecmaRuntimeCallInfo->SetCallArg(2, target); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo); + BuiltinsFinalizationRegistry::Register(ecmaRuntimeCallInfo); + + auto ecmaRuntimeCallInfo1 = + TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); // 6 means 1 call args + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(jsfinalizationRegistry.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, target); + + BuiltinsFinalizationRegistry::Unregister(ecmaRuntimeCallInfo1); + TestHelper::TearDownFrame(thread, prev); + } + vm->CollectGarbage(TriggerGCType::FULL_GC); + if (!thread->HasPendingException()) { + job::MicroJobQueue::ExecutePendingJob(thread, vm->GetJSThread()->GetCurrentEcmaContext()->GetMicroJobQueue()); + } + vm->SetEnableForceGC(true); + ASSERT_EQ(testValue, 0); +} } // namespace panda::test diff --git a/ecmascript/builtins/tests/builtins_global_test.cpp b/ecmascript/builtins/tests/builtins_global_test.cpp index 9ed7dc1ed1157eb7324ed7a58f9da74904da54be..b6d4bd2c6d10b3fc471b1c3dce786fe39cd80a4c 100644 --- a/ecmascript/builtins/tests/builtins_global_test.cpp +++ b/ecmascript/builtins/tests/builtins_global_test.cpp @@ -120,4 +120,98 @@ HWTEST_F_L0(BuiltinsGlobalTest, CallJsProxy) EXPECT_EQ(result, JSTaggedValue::Undefined()); thread->ClearException(); } + +HWTEST_F_L0(BuiltinsGlobalTest, Escape) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle str1 = factory->NewFromASCII("?!=()#%&"); + auto ecmaRuntimeCallInfo1 = + TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); // NOLINT, 6 means 3 paras + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetCallArg(0, str1.GetTaggedValue()); + + [[maybe_unused]] auto prev1 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1); + JSTaggedValue result1 = BuiltinsGlobal::Escape(ecmaRuntimeCallInfo1); + TestHelper::TearDownFrame(thread, prev1); + EXPECT_TRUE(result1.IsString()); + JSHandle ecmaStrHandle1(thread, result1); + EXPECT_STREQ("%3F%21%3D%28%29%23%25%26", EcmaStringAccessor(ecmaStrHandle1).ToCString().c_str()); // NOLINT + + JSHandle str2 = factory->NewFromASCII("%u%u0%u9%ua%uF%u00%u09%u0f%u0F%u000%u00a%u00F"); + auto ecmaRuntimeCallInfo2 = + TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); // NOLINT + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetCallArg(0, str2.GetTaggedValue()); // NOLINT + + [[maybe_unused]] auto prev2 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2); + JSTaggedValue result2 = BuiltinsGlobal::Escape(ecmaRuntimeCallInfo2); + TestHelper::TearDownFrame(thread, prev2); + EXPECT_TRUE(result2.IsString()); + JSHandle ecmaStrHandle2(thread, result2); + EXPECT_STREQ("%25u%25u0%25u9%25ua%25uF%25u00%25u09%25u0f%25u0F%25u000%25u00a%25u00F", // NOLINT special value + EcmaStringAccessor(ecmaStrHandle2).ToCString().c_str()); + + JSHandle str3 = factory->NewFromASCII("Hello World!"); + auto ecmaRuntimeCallInfo3 = + TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); // NOLINT + ecmaRuntimeCallInfo3->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo3->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo3->SetCallArg(0, str3.GetTaggedValue()); + + [[maybe_unused]] auto prev3 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo3); + JSTaggedValue result3 = BuiltinsGlobal::Escape(ecmaRuntimeCallInfo3); + TestHelper::TearDownFrame(thread, prev3); + EXPECT_TRUE(result3.IsString()); + JSHandle ecmaStrHandle3(thread, result3); + EXPECT_STREQ("Hello%20World%21", EcmaStringAccessor(ecmaStrHandle3).ToCString().c_str()); +} + +HWTEST_F_L0(BuiltinsGlobalTest, Unescape) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle str1 = factory->NewFromASCII(""); + auto ecmaRuntimeCallInfo1 = + TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); // NOLINT + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetCallArg(0, str1.GetTaggedValue()); + + [[maybe_unused]] auto prev1 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1); + JSTaggedValue result1 = BuiltinsGlobal::Unescape(ecmaRuntimeCallInfo1); + TestHelper::TearDownFrame(thread, prev1); + EXPECT_TRUE(result1.IsString()); + JSHandle ecmaStrHandle1(thread, result1); + EXPECT_STREQ("", EcmaStringAccessor(ecmaStrHandle1).ToCString().c_str()); + + JSHandle str2 = factory->NewFromASCII("%u%u0%u9%ua%uF%u00%u09%u0f%u0F%u000%u00a%u00F"); + auto ecmaRuntimeCallInfo2 = + TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); // NOLINT + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetCallArg(0, str2.GetTaggedValue()); // NOLINT + + [[maybe_unused]] auto prev2 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2); + JSTaggedValue result2 = BuiltinsGlobal::Unescape(ecmaRuntimeCallInfo2); + TestHelper::TearDownFrame(thread, prev2); + EXPECT_TRUE(result2.IsString()); + JSHandle ecmaStrHandle2(thread, result2); + EXPECT_STREQ("%u%u0%u9%ua%uF%u00%u09%u0f%u0F%u000%u00a%u00F", + EcmaStringAccessor(ecmaStrHandle2).ToCString().c_str()); + + JSHandle str3 = factory->NewFromASCII("Hello%20World%21"); + auto ecmaRuntimeCallInfo3 = + TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); // NOLINT 6 means 3 paras + ecmaRuntimeCallInfo3->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo3->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo3->SetCallArg(0, str3.GetTaggedValue()); + + [[maybe_unused]] auto prev3 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo3); + JSTaggedValue result3 = BuiltinsGlobal::Escape(ecmaRuntimeCallInfo3); + TestHelper::TearDownFrame(thread, prev3); + EXPECT_TRUE(result3.IsString()); + JSHandle ecmaStrHandle3(thread, result3); + EXPECT_STREQ("Hello%2520World%2521", EcmaStringAccessor(ecmaStrHandle3).ToCString().c_str()); +} } // namespace panda::test diff --git a/ecmascript/builtins/tests/builtins_object_test.cpp b/ecmascript/builtins/tests/builtins_object_test.cpp index 2609da1a0aa188086b677dc1b6adc125c3439461..548c98c7d91d40dc15df09f70ee39ec38e292c1b 100644 --- a/ecmascript/builtins/tests/builtins_object_test.cpp +++ b/ecmascript/builtins/tests/builtins_object_test.cpp @@ -973,4 +973,27 @@ HWTEST_F_L0(BuiltinsObjectTest, ValueOf) ASSERT_TRUE(result.IsECMAObject()); } + +// Object.hasOwn (O, P) +HWTEST_F_L0(BuiltinsObjectTest, HasOwn) +{ + JSHandle function(thread, BuiltinsObjectTestCreate(thread)); + JSHandle object = + thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle(function), function); + JSHandle key(thread->GetEcmaVM()->GetFactory()->NewFromASCII("x")); + JSHandle value(thread, JSTaggedValue(1)); + JSObject::SetProperty(thread, JSHandle(object), key, value); + EXPECT_EQ(JSObject::GetProperty(thread, JSHandle(object), key).GetValue()->GetInt(), 1); + + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, object.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, key.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo); + JSTaggedValue result = BuiltinsObject::HasOwn(ecmaRuntimeCallInfo); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData()); +} } // namespace panda::test diff --git a/ecmascript/builtins/tests/builtins_regexp_test.cpp b/ecmascript/builtins/tests/builtins_regexp_test.cpp index 79df1902e4e954845a44c1d1cee80c040576c800..6727f3fc6a7bf4ca817e069fe02430cb8d1aec7c 100644 --- a/ecmascript/builtins/tests/builtins_regexp_test.cpp +++ b/ecmascript/builtins/tests/builtins_regexp_test.cpp @@ -657,4 +657,64 @@ HWTEST_F_L0(BuiltinsRegExpTest, RegExpParseCache) RegExpParserCache::CACHE_SIZE, vec).first.IsHole()); ASSERT_TRUE(regExpParserCache->GetCache(*string2, 0, vec).first.IsHole()); } + +HWTEST_F_L0(BuiltinsRegExpTest, FlagD) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // invoke RegExpConstructor method + JSHandle pattern1 = factory->NewFromASCII("(?a)"); + JSHandle flags1 = factory->NewFromASCII("gd"); + JSTaggedValue result1 = CreateBuiltinsRegExpObjByPatternAndFlags(thread, pattern1, flags1); + JSHandle result1Handle(thread, result1); + + // invoke GetFlags method + JSHandle flags(factory->NewFromASCII("flags")); + JSHandle flagsResult(JSObject::GetProperty(thread, result1Handle, flags).GetValue()); + JSHandle expectResult = factory->NewFromASCII("dg"); + ASSERT_EQ(EcmaStringAccessor::Compare(instance, JSHandle(flagsResult), expectResult), 0); + + // invoke GetHasIndices method + JSHandle hasIndices(factory->NewFromASCII("hasIndices")); + JSTaggedValue taggedHasIndicesResult = + JSObject::GetProperty(thread, result1Handle, hasIndices).GetValue().GetTaggedValue(); + ASSERT_EQ(taggedHasIndicesResult.GetRawData(), JSTaggedValue::True().GetRawData()); + + JSHandle inputString = factory->NewFromASCII("babcae"); + auto ecmaRuntimeCallInfo = + TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); // 6 means 1 call arg + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(result1Handle.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, inputString.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo); + // invoke Exec method + JSTaggedValue results = BuiltinsRegExp::Exec(ecmaRuntimeCallInfo); + TestHelper::TearDownFrame(thread, prev); + + JSHandle execResult(thread, results); + JSHandle indices(factory->NewFromASCII("indices")); + JSHandle indicesArr = JSObject::GetProperty(thread, execResult, indices).GetValue(); + EXPECT_TRUE(indicesArr->IsJSArray()); + + JSHandle indices0 = JSObject::GetProperty(thread, indicesArr, 0).GetValue(); + EXPECT_TRUE(indices0->IsJSArray()); + // indices[0] [1, 2] + EXPECT_EQ(JSObject::GetProperty(thread, indices0, 0).GetValue()->GetInt(), 1); + EXPECT_EQ(JSObject::GetProperty(thread, indices0, 1).GetValue()->GetInt(), 2); + JSHandle indices1 = JSObject::GetProperty(thread, indicesArr, 1).GetValue(); + EXPECT_TRUE(indices1->IsJSArray()); + // indices[1] [1, 2] + EXPECT_EQ(JSObject::GetProperty(thread, indices1, 0).GetValue()->GetInt(), 1); + EXPECT_EQ(JSObject::GetProperty(thread, indices1, 1).GetValue()->GetInt(), 2); + + JSHandle groups(factory->NewFromASCII("groups")); + JSHandle groupsObj = JSObject::GetProperty(thread, indicesArr, groups).GetValue(); + EXPECT_TRUE(groupsObj->IsJSObject()); + JSHandle groupName(factory->NewFromASCII("groupname")); + JSHandle groupNameArr = JSObject::GetProperty(thread, groupsObj, groupName).GetValue(); + EXPECT_TRUE(groupNameArr->IsJSArray()); + // {groupname: [1,2]]} + EXPECT_EQ(JSObject::GetProperty(thread, groupNameArr, 0).GetValue()->GetInt(), 1); + EXPECT_EQ(JSObject::GetProperty(thread, groupNameArr, 1).GetValue()->GetInt(), 2); +} } // namespace panda::test diff --git a/ecmascript/builtins/tests/builtins_typedarray_test.cpp b/ecmascript/builtins/tests/builtins_typedarray_test.cpp index c43145fdf1afe94dd870fe39e78ad453a54cc237..95c62679f4382280e8e2638c4593a0a5880b2608 100644 --- a/ecmascript/builtins/tests/builtins_typedarray_test.cpp +++ b/ecmascript/builtins/tests/builtins_typedarray_test.cpp @@ -44,6 +44,20 @@ namespace panda::test { using Array = ecmascript::builtins::BuiltinsArray; using TypedArray = ecmascript::builtins::BuiltinsTypedArray; using TypedArrayHelper = ecmascript::base::TypedArrayHelper; +constexpr uint32_t ECMA_RUNTIME_CALL_INFO_4 = 4; +constexpr uint32_t ECMA_RUNTIME_CALL_INFO_6 = 6; + +enum class TypeArrayIndex { + TYPED_ARRAY_INDEX_0, + TYPED_ARRAY_INDEX_1, + TYPED_ARRAY_INDEX_2, + TYPED_ARRAY_INDEX_3 +}; +constexpr uint32_t TYPED_ARRAY_LENGTH_3 = 3; +constexpr int32_t INT_VALUE_0 = 0; +constexpr int32_t INT_VALUE_2 = 2; +constexpr int32_t INT_VALUE_4 = 4; +constexpr int32_t INT_VALUE_9 = 9; class BuiltinsTypedArrayTest : public testing::Test { public: @@ -143,6 +157,42 @@ protected: return GetTaggedBoolean(false); } + static JSTaggedValue TestToSortedFunc(EcmaRuntimeCallInfo *argv) + { + uint32_t argc = argv->GetArgsNumber(); + if (argc > 1) { + // x < y + if (GetCallArg(argv, 0)->GetInt() < GetCallArg(argv, 1)->GetInt()) { + return GetTaggedBoolean(true); + } + } + return GetTaggedBoolean(false); + } + + static JSTaggedValue TestFindLastFunc(EcmaRuntimeCallInfo *argv) + { + uint32_t argc = argv->GetArgsNumber(); + if (argc > 0) { + // 20 : test case + if (GetCallArg(argv, 0)->GetInt() > 20) { + return GetTaggedBoolean(true); + } + } + return GetTaggedBoolean(false); + } + + static JSTaggedValue TestFindLastIndexFunc(EcmaRuntimeCallInfo *argv) + { + uint32_t argc = argv->GetArgsNumber(); + if (argc > 0) { + // 20 : test case + if (GetCallArg(argv, 0)->GetInt() > 20) { + return GetTaggedBoolean(true); + } + } + return GetTaggedBoolean(false); + } + static JSTaggedValue TestReduceFunc(EcmaRuntimeCallInfo *argv) { int accumulator = GetCallArg(argv, 0)->GetInt(); @@ -294,4 +344,275 @@ HWTEST_F_L0(BuiltinsTypedArrayTest, Includes) ASSERT_TRUE(!result.JSTaggedValue::ToBoolean()); // new Int8Array[2,3,4].includes(2, -2) } + +HWTEST_F_L0(BuiltinsTypedArrayTest, At) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + [[maybe_unused]] JSHandle array(factory->NewTaggedArray(3)); + array->Set(thread, 0, JSTaggedValue(2)); + array->Set(thread, 1, JSTaggedValue(3)); + array->Set(thread, 2, JSTaggedValue(4)); + + [[maybe_unused]] JSHandle obj = + JSHandle(thread, CreateTypedArrayFromList(thread, array)); + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(2))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1); + [[maybe_unused]] JSTaggedValue result = TypedArray::At(ecmaRuntimeCallInfo1); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(result, JSTaggedValue(4)); + + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(0, JSTaggedValue(static_cast(-2))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2); + result = TypedArray::At(ecmaRuntimeCallInfo2); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_EQ(result, JSTaggedValue(3)); + + auto ecmaRuntimeCallInfo3 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(0, JSTaggedValue(static_cast(-4))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo3); + result = TypedArray::At(ecmaRuntimeCallInfo3); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsUndefined()); + + auto ecmaRuntimeCallInfo4 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(0, JSTaggedValue(static_cast(3))); + + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo4); + result = TypedArray::At(ecmaRuntimeCallInfo4); + TestHelper::TearDownFrame(thread, prev); + + ASSERT_TRUE(result.IsUndefined()); +} + +HWTEST_F_L0(BuiltinsTypedArrayTest, ToReversed) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + [[maybe_unused]] JSHandle array(factory->NewTaggedArray(TYPED_ARRAY_LENGTH_3)); + array->Set(thread, static_cast(TypeArrayIndex::TYPED_ARRAY_INDEX_0), JSTaggedValue(INT_VALUE_0)); + array->Set(thread, static_cast(TypeArrayIndex::TYPED_ARRAY_INDEX_1), JSTaggedValue(INT_VALUE_4)); + array->Set(thread, static_cast(TypeArrayIndex::TYPED_ARRAY_INDEX_2), JSTaggedValue(INT_VALUE_9)); + + [[maybe_unused]] JSHandle obj = + JSHandle(thread, CreateTypedArrayFromList(thread, array)); + auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), + ECMA_RUNTIME_CALL_INFO_4); + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1); + [[maybe_unused]] JSTaggedValue result = TypedArray::ToReversed(ecmaRuntimeCallInfo1); + TestHelper::TearDownFrame(thread, prev); + + auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, + JSTaggedValue::Undefined(), + ECMA_RUNTIME_CALL_INFO_6); + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(static_cast(TypeArrayIndex::TYPED_ARRAY_INDEX_0), + JSTaggedValue(INT_VALUE_0)); + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2); + JSTaggedValue value = TypedArray::At(ecmaRuntimeCallInfo2); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(value, JSTaggedValue(INT_VALUE_0)); + + auto ecmaRuntimeCallInfo3 = TestHelper::CreateEcmaRuntimeCallInfo(thread, + JSTaggedValue::Undefined(), + ECMA_RUNTIME_CALL_INFO_6); + ecmaRuntimeCallInfo3->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo3->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo3->SetCallArg(static_cast(TypeArrayIndex::TYPED_ARRAY_INDEX_0), + JSTaggedValue(INT_VALUE_2)); + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo3); + value = TypedArray::At(ecmaRuntimeCallInfo3); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(value, JSTaggedValue(INT_VALUE_9)); + + auto ecmaRuntimeCallInfo4 = TestHelper::CreateEcmaRuntimeCallInfo(thread, + JSTaggedValue::Undefined(), + ECMA_RUNTIME_CALL_INFO_6); + ecmaRuntimeCallInfo4->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo4->SetThis(result); + ecmaRuntimeCallInfo4->SetCallArg(static_cast(TypeArrayIndex::TYPED_ARRAY_INDEX_0), + JSTaggedValue(INT_VALUE_0)); + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo4); + value = TypedArray::At(ecmaRuntimeCallInfo4); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(value, JSTaggedValue(INT_VALUE_9)); + auto ecmaRuntimeCallInfo5 = TestHelper::CreateEcmaRuntimeCallInfo(thread, + JSTaggedValue::Undefined(), + ECMA_RUNTIME_CALL_INFO_6); + ecmaRuntimeCallInfo5->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo5->SetThis(result); + ecmaRuntimeCallInfo5->SetCallArg(static_cast(TypeArrayIndex::TYPED_ARRAY_INDEX_0), + JSTaggedValue(INT_VALUE_2)); + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo5); + value = TypedArray::At(ecmaRuntimeCallInfo5); + TestHelper::TearDownFrame(thread, prev); + ASSERT_EQ(value, JSTaggedValue(INT_VALUE_0)); +} + +HWTEST_F_L0(BuiltinsTypedArrayTest, ToSorted) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle array(factory->NewTaggedArray(3)); + // array [10, 8, 30] + array->Set(thread, 0, JSTaggedValue(10)); + array->Set(thread, 1, JSTaggedValue(8)); + array->Set(thread, 2, JSTaggedValue(30)); + + JSHandle obj = JSHandle(thread, CreateTypedArrayFromList(thread, array)); + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestClass::TestToSortedFunc)); + auto ecmaRuntimeCallInfo1 = + TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); // 6 means 1 call arg + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue()); + + [[maybe_unused]] auto prev1 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1); + JSTaggedValue result1 = TypedArray::ToSorted(ecmaRuntimeCallInfo1); + TestHelper::TearDownFrame(thread, prev1); + + EXPECT_TRUE(result1.IsTypedArray()); + JSHandle resultArr1 = JSHandle(thread, result1); + // [30, 10, 8] + EXPECT_EQ(JSTypedArray::GetProperty(thread, resultArr1, 0).GetValue()->GetInt(), 30); + EXPECT_EQ(JSTypedArray::GetProperty(thread, resultArr1, 1).GetValue()->GetInt(), 10); + EXPECT_EQ(JSTypedArray::GetProperty(thread, resultArr1, 2).GetValue()->GetInt(), 8); + + auto ecmaRuntimeCallInfo2 = + TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); // 4 means 0 call arg + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(obj.GetTaggedValue()); + + [[maybe_unused]] auto prev2 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2); + JSTaggedValue result2 = TypedArray::ToSorted(ecmaRuntimeCallInfo2); + TestHelper::TearDownFrame(thread, prev2); + + EXPECT_TRUE(result2.IsTypedArray()); + JSHandle resultArr2 = JSHandle(thread, result2); + // [8, 10 ,30] + EXPECT_EQ(JSTypedArray::GetProperty(thread, resultArr2, 0).GetValue()->GetInt(), 8); + EXPECT_EQ(JSTypedArray::GetProperty(thread, resultArr2, 1).GetValue()->GetInt(), 10); + EXPECT_EQ(JSTypedArray::GetProperty(thread, resultArr2, 2).GetValue()->GetInt(), 30); +} + +HWTEST_F_L0(BuiltinsTypedArrayTest, With) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle array(factory->NewTaggedArray(3)); + // array [1, 2, 3] + array->Set(thread, 0, JSTaggedValue(1)); + array->Set(thread, 1, JSTaggedValue(2)); + array->Set(thread, 2, JSTaggedValue(3)); + + JSHandle obj = JSHandle(thread, CreateTypedArrayFromList(thread, array)); + auto ecmaRuntimeCallInfo1 = + TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); // 8 means 2 call args + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast(-1))); + ecmaRuntimeCallInfo1->SetCallArg(1, JSTaggedValue(static_cast(30))); // with(-1, 30) + + [[maybe_unused]] auto prev1 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1); + JSTaggedValue result1 = TypedArray::With(ecmaRuntimeCallInfo1); + TestHelper::TearDownFrame(thread, prev1); + + EXPECT_TRUE(result1.IsTypedArray()); + JSHandle resultArr1 = JSHandle(thread, result1); + // [1, 2, 30] + EXPECT_EQ(JSTypedArray::GetProperty(thread, resultArr1, 0).GetValue()->GetInt(), 1); + EXPECT_EQ(JSTypedArray::GetProperty(thread, resultArr1, 1).GetValue()->GetInt(), 2); + EXPECT_EQ(JSTypedArray::GetProperty(thread, resultArr1, 2).GetValue()->GetInt(), 30); + + auto ecmaRuntimeCallInfo2 = + TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); // 8 means 2 call args + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo2->SetCallArg(0, JSTaggedValue(static_cast(1))); + ecmaRuntimeCallInfo2->SetCallArg(1, JSTaggedValue(static_cast(-100))); // with(1, -100) + + [[maybe_unused]] auto prev2 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2); + JSTaggedValue result2 = TypedArray::With(ecmaRuntimeCallInfo2); + TestHelper::TearDownFrame(thread, prev2); + + EXPECT_TRUE(result2.IsTypedArray()); + JSHandle resultArr2 = JSHandle(thread, result2); + // [1, -100, 3] + EXPECT_EQ(JSTypedArray::GetProperty(thread, resultArr2, 0).GetValue()->GetInt(), 1); + EXPECT_EQ(JSTypedArray::GetProperty(thread, resultArr2, 1).GetValue()->GetInt(), -100); + EXPECT_EQ(JSTypedArray::GetProperty(thread, resultArr2, 2).GetValue()->GetInt(), 3); +} + +HWTEST_F_L0(BuiltinsTypedArrayTest, FindLast) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle array(factory->NewTaggedArray(3)); + // array [50, 40, 2] + array->Set(thread, 0, JSTaggedValue(50)); + array->Set(thread, 1, JSTaggedValue(40)); + array->Set(thread, 2, JSTaggedValue(2)); + + JSHandle obj = JSHandle(thread, CreateTypedArrayFromList(thread, array)); + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestClass::TestFindLastFunc)); + auto ecmaRuntimeCallInfo1 = + TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); // 6 means 1 call arg + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1); + JSTaggedValue result = TypedArray::FindLast(ecmaRuntimeCallInfo1); + TestHelper::TearDownFrame(thread, prev); + + EXPECT_EQ(result.GetRawData(), JSTaggedValue(40).GetRawData()); +} + +HWTEST_F_L0(BuiltinsTypedArrayTest, FindLastIndex) +{ + ASSERT_NE(thread, nullptr); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle array(factory->NewTaggedArray(3)); + // array [50, 40, 30] + array->Set(thread, 0, JSTaggedValue(50)); + array->Set(thread, 1, JSTaggedValue(40)); + array->Set(thread, 2, JSTaggedValue(30)); + + JSHandle obj = JSHandle(thread, CreateTypedArrayFromList(thread, array)); + JSHandle func = factory->NewJSFunction(env, reinterpret_cast(TestClass::TestFindLastFunc)); + auto ecmaRuntimeCallInfo1 = + TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); // 6 means 1 call arg + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(obj.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, func.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1); + JSTaggedValue result = TypedArray::FindLastIndex(ecmaRuntimeCallInfo1); + TestHelper::TearDownFrame(thread, prev); + + EXPECT_EQ(result.GetRawData(), JSTaggedValue(static_cast(2)).GetRawData()); +} } // namespace panda::test diff --git a/ecmascript/builtins/tests/builtins_weak_map_test.cpp b/ecmascript/builtins/tests/builtins_weak_map_test.cpp index f3a45779c1be8d1c574034563e2826a5263193ba..ac410b067dcba99b36942bd76ca734b7f9ab5e94 100644 --- a/ecmascript/builtins/tests/builtins_weak_map_test.cpp +++ b/ecmascript/builtins/tests/builtins_weak_map_test.cpp @@ -213,4 +213,55 @@ HWTEST_F_L0(BuiltinsWeakMapTest, DeleteAndRemove) EXPECT_EQ(result4.GetRawData(), JSTaggedValue::False().GetRawData()); } + +HWTEST_F_L0(BuiltinsWeakMapTest, SymbolKey) +{ + // create jsWeakMap + JSHandle weakMap(thread, CreateBuiltinsWeakMap(thread)); + + // add 2 symbol keys + JSTaggedValue lastKey(JSTaggedValue::Undefined()); + for (int i = 0; i < 2; i++) { + JSHandle symbolKey = thread->GetEcmaVM()->GetFactory()->NewJSSymbol(); + JSHandle key(symbolKey); + auto ecmaRuntimeCallInfo = + TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8); // 8 means 2 call args + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(weakMap.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(static_cast(i))); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo); + // set + JSTaggedValue result1 = BuiltinsWeakMap::Set(ecmaRuntimeCallInfo); + TestHelper::TearDownFrame(thread, prev); + + EXPECT_TRUE(result1.IsECMAObject()); + JSWeakMap *jsWeakMap = JSWeakMap::Cast(reinterpret_cast(result1.GetRawData())); + EXPECT_EQ(jsWeakMap->GetSize(), static_cast(i) + 1); + lastKey = key.GetTaggedValue(); + } + + // check whether jsWeakMap can get and delete lastKey + + auto ecmaRuntimeCallInfo1 = + TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); // 6 means 1 call arg + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(weakMap.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, lastKey); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1); + // get + JSTaggedValue result2 = BuiltinsWeakMap::Get(ecmaRuntimeCallInfo1); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result2, JSTaggedValue(1)); + + // delete + JSTaggedValue result3 = BuiltinsWeakMap::Delete(ecmaRuntimeCallInfo1); + EXPECT_EQ(result3.GetRawData(), JSTaggedValue::True().GetRawData()); + + // check deleteKey is deleted + JSTaggedValue result4 = BuiltinsWeakMap::Has(ecmaRuntimeCallInfo1); + EXPECT_EQ(result4.GetRawData(), JSTaggedValue::False().GetRawData()); +} } // namespace panda::test diff --git a/ecmascript/builtins/tests/builtins_weak_ref_test.cpp b/ecmascript/builtins/tests/builtins_weak_ref_test.cpp index 71d1bace2994dfcea9b85839d61db3416676ccf2..5c314ab2b2dd0cf3324d0486179252e6a7d04e7f 100644 --- a/ecmascript/builtins/tests/builtins_weak_ref_test.cpp +++ b/ecmascript/builtins/tests/builtins_weak_ref_test.cpp @@ -188,4 +188,38 @@ HWTEST_F_L0(BuiltinsWeakRefTest, Deref3) vm->SetEnableForceGC(true); ASSERT_TRUE(!result2.IsUndefined()); } + +// symbol target +HWTEST_F_L0(BuiltinsWeakRefTest, SymbolTarget) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + JSHandle symbolTarget = thread->GetEcmaVM()->GetFactory()->NewJSSymbol(); + JSHandle target(symbolTarget); + + JSHandle weakRef(env->GetBuiltinsWeakRefFunction()); + + auto ecmaRuntimeCallInfo1 = + TestHelper::CreateEcmaRuntimeCallInfo(thread, weakRef.GetTaggedValue(), 6); // 6 means 1 call arg + ecmaRuntimeCallInfo1->SetFunction(weakRef.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetCallArg(0, target.GetTaggedValue()); + + // constructor + [[maybe_unused]] auto prev1 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1); + JSTaggedValue result1 = BuiltinsWeakRef::WeakRefConstructor(ecmaRuntimeCallInfo1); + TestHelper::TearDownFrame(thread, prev1); + ASSERT_TRUE(result1.IsECMAObject()); + + JSHandle jsWeakRef(thread, JSWeakRef::Cast(reinterpret_cast(result1.GetRawData()))); + auto ecmaRuntimeCallInfo2 = + TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4); // 4 means 0 call arg + ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo2->SetThis(jsWeakRef.GetTaggedValue()); + + // weakRef.Deref() + [[maybe_unused]] auto prev2 = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2); + JSTaggedValue result2 = BuiltinsWeakRef::Deref(ecmaRuntimeCallInfo2); + TestHelper::TearDownFrame(thread, prev2); + ASSERT_EQ(result2, target.GetTaggedValue()); +} } // namespace panda::test diff --git a/ecmascript/builtins/tests/builtins_weak_set_test.cpp b/ecmascript/builtins/tests/builtins_weak_set_test.cpp index 38d5eee994b90e6a203a8ed22bc555b51c7a7d8b..61aedcdc35b5ed09b5fbdcbdd3fe43bf21d5fcff 100644 --- a/ecmascript/builtins/tests/builtins_weak_set_test.cpp +++ b/ecmascript/builtins/tests/builtins_weak_set_test.cpp @@ -207,4 +207,54 @@ HWTEST_F_L0(BuiltinsWeakSetTest, DeleteAndRemove) EXPECT_EQ(result4.GetRawData(), JSTaggedValue::False().GetRawData()); } + +HWTEST_F_L0(BuiltinsWeakSetTest, SymbolKey) +{ + // create jsSet + JSHandle weakSet(thread, CreateBuiltinsWeakSet(thread)); + + // add 2 keys + JSTaggedValue lastKey(JSTaggedValue::Undefined()); + for (int i = 0; i < 2; i++) { + JSHandle symbolKey = thread->GetEcmaVM()->GetFactory()->NewJSSymbol(); + JSHandle key(symbolKey); + + auto ecmaRuntimeCallInfo = + TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); // 6 means 1 call arg + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(weakSet.GetTaggedValue()); + ecmaRuntimeCallInfo->SetCallArg(0, key.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo); + // add + JSTaggedValue result1 = BuiltinsWeakSet::Add(ecmaRuntimeCallInfo); + TestHelper::TearDownFrame(thread, prev); + + EXPECT_TRUE(result1.IsECMAObject()); + JSWeakSet *jsWeakSet = JSWeakSet::Cast(reinterpret_cast(result1.GetRawData())); + EXPECT_EQ(jsWeakSet->GetSize(), static_cast(i) + 1); + lastKey = key.GetTaggedValue(); + } + // whether jsWeakSet has delete lastKey + + auto ecmaRuntimeCallInfo1 = + TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); // 6 means 1 call arg + ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo1->SetThis(weakSet.GetTaggedValue()); + ecmaRuntimeCallInfo1->SetCallArg(0, lastKey); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1); + // has + JSTaggedValue result2 = BuiltinsWeakSet::Has(ecmaRuntimeCallInfo1); + TestHelper::TearDownFrame(thread, prev); + EXPECT_EQ(result2.GetRawData(), JSTaggedValue::True().GetRawData()); + + // delete + JSTaggedValue result3 = BuiltinsWeakSet::Delete(ecmaRuntimeCallInfo1); + EXPECT_EQ(result3.GetRawData(), JSTaggedValue::True().GetRawData()); + + // check deleteKey is deleted + JSTaggedValue result4 = BuiltinsWeakSet::Has(ecmaRuntimeCallInfo1); + EXPECT_EQ(result4.GetRawData(), JSTaggedValue::False().GetRawData()); +} } // namespace panda::test diff --git a/ecmascript/byte_array.cpp b/ecmascript/byte_array.cpp index c99943ea525bfc19da164354448ea2996bcf38af..0cc15ecf46e2f6193ec44ee916bf851b957724d2 100644 --- a/ecmascript/byte_array.cpp +++ b/ecmascript/byte_array.cpp @@ -16,14 +16,15 @@ #include "ecmascript/byte_array.h" #include "ecmascript/builtins/builtins_arraybuffer.h" +#include "ecmascript/mem/tagged_object-inl.h" namespace panda::ecmascript { -void ByteArray::Set(uint32_t idx, DataViewType type, JSTaggedType val, uint32_t offset) +void ByteArray::Set(JSThread* thread, uint32_t idx, DataViewType type, JSTaggedType val, uint32_t offset) { void *pointer = GetData(); auto *block = reinterpret_cast(pointer) + offset; - builtins::BuiltinsArrayBuffer::SetValueInBuffer(idx * GetByteLength(), block, type, - JSTaggedValue(val).GetNumber(), true); + builtins::BuiltinsArrayBuffer::SetValueInBuffer(thread, idx * GetByteLength(), block, + type, JSTaggedValue(val).GetNumber(), true); } JSTaggedValue ByteArray::Get(JSThread *thread, uint32_t idx, DataViewType type, uint32_t offset) diff --git a/ecmascript/byte_array.h b/ecmascript/byte_array.h index 2f483e6b425bd2f7b3bd716c681816af7133f1b6..bb534b95c6e7c35e2408a3ccc07c32c2e5b7d8bf 100644 --- a/ecmascript/byte_array.h +++ b/ecmascript/byte_array.h @@ -40,7 +40,7 @@ public: return reinterpret_cast(ToUintPtr(this) + DATA_OFFSET + index * GetByteLength()); } - void Set(uint32_t idx, DataViewType type, JSTaggedType val, uint32_t offset = 0); + void Set(JSThread* thread, uint32_t idx, DataViewType type, JSTaggedType val, uint32_t offset = 0); JSTaggedValue Get(JSThread *thread, uint32_t idx, DataViewType type, uint32_t offset = 0); static constexpr size_t ARRAY_LENGTH_OFFSET = TaggedObjectSize(); diff --git a/ecmascript/common.h b/ecmascript/common.h index 7dfb6d04c5f1f8f570038d3b9339065fdf39ab2a..c22714e7c3c68376660a2f61620c1e00e7ee5cc5 100644 --- a/ecmascript/common.h +++ b/ecmascript/common.h @@ -50,6 +50,10 @@ enum class GCReason : uint8_t { OTHER, }; +enum class RequestAotMode : uint8_t { + RE_COMPILE_ON_IDLE = 0 +}; + #define SCOPE_LIST(V) \ V(TotalGC) \ V(Initialize) \ diff --git a/ecmascript/compiler/BUILD.gn b/ecmascript/compiler/BUILD.gn index 1c5002f78fba4c2c08037c809da1445e92024759..fcde27a76bd44db0b8650088d61c45334a9359b3 100644 --- a/ecmascript/compiler/BUILD.gn +++ b/ecmascript/compiler/BUILD.gn @@ -59,17 +59,24 @@ ohos_source_set("libark_jsoptimizer_set") { sources = [ "access_object_stub_builder.cpp", "argument_accessor.cpp", + "array_bounds_check_elimination.cpp", "assembler/aarch64/assembler_aarch64.cpp", "assembler/aarch64/extend_assembler.cpp", "assembler/x64/assembler_x64.cpp", "assembler/x64/extended_assembler_x64.cpp", "assembler_module.cpp", "async_function_lowering.cpp", + "base/depend_chain_helper.cpp", "bc_call_signature.cpp", + "builtins/builtins_array_stub_builder.cpp", "builtins/builtins_call_signature.cpp", + "builtins/builtins_collection_stub_builder.cpp", + "builtins/builtins_function_stub_builder.cpp", + "builtins/builtins_object_stub_builder.cpp", "builtins/builtins_string_stub_builder.cpp", "builtins/builtins_stubs.cpp", "builtins/containers_stub_builder.cpp", + "builtins/linked_hashtable_stub_builder.cpp", "builtins_lowering.cpp", "bytecode_circuit_builder.cpp", "bytecode_info_collector.cpp", @@ -77,44 +84,49 @@ ohos_source_set("libark_jsoptimizer_set") { "call_signature.cpp", "circuit.cpp", "circuit_builder.cpp", + "combined_pass_visitor.cpp", "common_stubs.cpp", "compilation_driver.cpp", "compiler_log.cpp", + "dead_code_elimination.cpp", "debug_info.cpp", "early_elimination.cpp", "file_generators.cpp", "frame_states.cpp", "gate.cpp", "gate_accessor.cpp", - "gate_meta_data.cpp", "graph_editor.cpp", "graph_linearizer.cpp", - "graph_visitor.cpp", + "hcr_gate_meta_data.cpp", "ic_stub_builder.cpp", "interpreter_stub.cpp", "later_elimination.cpp", + "lcr_gate_meta_data.cpp", "lcr_lowering.cpp", "llvm_codegen.cpp", "llvm_ir_builder.cpp", "loop_analysis.cpp", "loop_peeling.cpp", + "mcr_gate_meta_data.cpp", "new_object_stub_builder.cpp", + "ntype_hcr_lowering.cpp", "ntype_mcr_lowering.cpp", "number_speculative_lowering.cpp", "number_speculative_retype.cpp", "number_speculative_runner.cpp", + "object_access_helper.cpp", "operations_stub_builder.cpp", "pass_manager.cpp", "profiler_stub_builder.cpp", "range_analysis.cpp", + "range_guard.cpp", "rt_call_signature.cpp", "scheduler.cpp", + "share_gate_meta_data.cpp", "slowpath_lowering.cpp", "state_split_linearizer.cpp", "stub.cpp", "stub_builder.cpp", - "test_stubs.cpp", - "test_stubs_signature.cpp", "trampoline/aarch64/asm_interpreter_call.cpp", "trampoline/aarch64/common_call.cpp", "trampoline/aarch64/optimized_call.cpp", @@ -126,11 +138,14 @@ ohos_source_set("libark_jsoptimizer_set") { "ts_class_analysis.cpp", "ts_hclass_generator.cpp", "ts_hcr_lowering.cpp", + "ts_hcr_opt_pass.cpp", "ts_inline_lowering.cpp", "type.cpp", "type_inference/global_type_infer.cpp", "type_inference/initialization_analysis.cpp", "type_inference/method_type_infer.cpp", + "type_inference/pgo_type_infer.cpp", + "type_inference/pgo_type_infer_helper.cpp", "type_mcr_lowering.cpp", "type_recorder.cpp", "typed_array_stub_builder.cpp", @@ -228,16 +243,14 @@ ohos_source_set("libark_jsoptimizer_set") { "LLVMBitWriter", ] + if (!is_mac && !is_ios) { + libs += [ "LLVMParts" ] + } + # Only support compiling aarch64 target at device-side(arm64 platform). - # So these os-related libs of arm and x86 are not needed on arm64 platform. + # So these os-related libs of x86 are not needed on arm64 platform. if (is_mac || current_cpu != "arm64") { libs += [ - "LLVMARMUtils", - "LLVMARMCodeGen", - "LLVMARMDisassembler", - "LLVMARMDesc", - "LLVMARMInfo", - "LLVMARMAsmParser", "LLVMX86AsmParser", "LLVMX86CodeGen", "LLVMX86Desc", @@ -281,10 +294,7 @@ ohos_source_set("libark_jsoptimizer_set") { ohos_source_set("libark_stub_set") { stack_protector_ret = false - deps = [ - ":build_stub_to_cpp", - ":stub.an", - ] + deps = [ ":build_stub_to_cpp" ] sources = [ "$root_gen_dir/arkcompiler/ets_runtime/stub_an.cpp" ] @@ -327,29 +337,6 @@ ohos_shared_library("libark_jsoptimizer") { subsystem_name = "arkcompiler" } -ohos_shared_library("libark_jsoptimizer_test") { - stack_protector_ret = false - deps = [ - ":libark_jsoptimizer_set", - "$ark_root/libpandafile:libarkfile_static", - "$js_root:libark_jsruntime_test_set", - ] - - ldflags = [] - if (enable_coverage) { - ldflags += [ "--coverage" ] - cflags_cc = [ "--coverage" ] - } - - if (!ark_standalone_build) { - ldflags += [ "-Wl,--lto-O0" ] - } - install_enable = false - - output_extension = "so" - subsystem_name = "test" -} - ohos_executable("ark_stub_compiler") { sources = [ "stub_compiler.cpp" ] include_dirs = [ "$target_gen_dir" ] @@ -462,6 +449,10 @@ action("gen_stub_file") { if (current_toolchain == host_toolchain) { stub_option = " --stub-file=" + rebase_path(stub_file_gen_dir) + "/stub.an" + } else if (current_cpu == "x86_64") { + stub_option = + " --stub-file=" + rebase_path(stub_file_gen_dir) + "/stub.an" + + " --compiler-target-triple=x86_64-unknown-linux-gnu" } else { stub_option = " --stub-file=" + rebase_path(stub_file_gen_dir) + "/stub.an" + @@ -484,6 +475,11 @@ action("gen_stub_file") { rebase_path(root_out_dir_with_host_toolchain) + "/${icu_subsystem_name}/${icu_part_name}:" + rebase_path(root_out_dir_with_host_toolchain) + "/thirdparty/zlib:" + + rebase_path(root_out_dir_with_host_toolchain) + + "/resourceschedule/frame_aware_sched:" + + rebase_path(root_out_dir_with_host_toolchain) + "/hiviewdfx/hilog:" + + rebase_path(root_out_dir_with_host_toolchain) + + "/thirdparty/bounds_checking_function:" + rebase_path("//prebuilts/clang/ohos/linux-x86_64/llvm/lib/"), ] diff --git a/ecmascript/compiler/access_object_stub_builder.cpp b/ecmascript/compiler/access_object_stub_builder.cpp index 20f67bcec933b8c79b60edab95c47dca690639e2..1ff52d40e6e789f0f728588812be7466fb97ef44 100644 --- a/ecmascript/compiler/access_object_stub_builder.cpp +++ b/ecmascript/compiler/access_object_stub_builder.cpp @@ -15,6 +15,7 @@ #include "ecmascript/compiler/access_object_stub_builder.h" #include "ecmascript/compiler/ic_stub_builder.h" #include "ecmascript/compiler/interpreter_stub-inl.h" +#include "ecmascript/compiler/profiler_stub_builder.h" #include "ecmascript/compiler/rt_call_signature.h" #include "ecmascript/compiler/stub_builder-inl.h" #include "ecmascript/ic/profile_type_info.h" @@ -34,25 +35,19 @@ GateRef AccessObjectStubBuilder::LoadObjByName(GateRef glue, GateRef receiver, G GateRef value = 0; ICStubBuilder builder(this); builder.SetParameters(glue, receiver, profileTypeInfo, value, slotId); - builder.LoadICByName(&result, &tryFastPath, &slowPath, &exit); + builder.LoadICByName(&result, &tryFastPath, &slowPath, &exit, callback); Bind(&tryFastPath); { GateRef propKey = ResolvePropKey(glue, prop, info); - result = GetPropertyByName(glue, receiver, propKey); - Label notHole(env); - Branch(TaggedIsHole(*result), &slowPath, ¬Hole); - Bind(¬Hole); - { - callback.ProfileObjLayoutByLoad(receiver); - Jump(&exit); - } + result = GetPropertyByName(glue, receiver, propKey, callback); + Branch(TaggedIsHole(*result), &slowPath, &exit); } Bind(&slowPath); { GateRef propKey = ResolvePropKey(glue, prop, info); result = CallRuntime(glue, RTSTUB_ID(LoadICByName), { profileTypeInfo, receiver, propKey, IntToTaggedInt(slotId) }); - callback.ProfileObjLayoutByLoad(receiver); + callback.TryPreDump(); Jump(&exit); } Bind(&exit); @@ -75,7 +70,7 @@ GateRef AccessObjectStubBuilder::DeprecatedLoadObjByName(GateRef glue, GateRef r Branch(TaggedIsHeapObject(receiver), &fastPath, &slowPath); Bind(&fastPath); { - result = GetPropertyByName(glue, receiver, propKey); + result = GetPropertyByName(glue, receiver, propKey, ProfileOperation()); Branch(TaggedIsHole(*result), &slowPath, &exit); } Bind(&slowPath); @@ -116,7 +111,7 @@ GateRef AccessObjectStubBuilder::StoreObjByName(GateRef glue, GateRef receiver, GateRef propKey = ResolvePropKey(glue, prop, info); result = CallRuntime(glue, RTSTUB_ID(StoreICByName), { profileTypeInfo, receiver, propKey, value, IntToTaggedInt(slotId) }); - callback.ProfileObjLayoutByStore(receiver); + callback.TryPreDump(); Jump(&exit); } @@ -129,8 +124,8 @@ GateRef AccessObjectStubBuilder::StoreObjByName(GateRef glue, GateRef receiver, GateRef AccessObjectStubBuilder::ResolvePropKey(GateRef glue, GateRef prop, const StringIdInfo &info) { if (jsFunc_ != Circuit::NullGate()) { - GateRef key = LoadObjectFromConstPool(jsFunc_, prop); - return key; + GateRef constpool = GetConstPoolFromFunction(jsFunc_); + return GetStringFromConstPool(glue, constpool, ChangeIntPtrToInt32(prop)); } if (!info.IsValid()) { return prop; @@ -142,7 +137,7 @@ GateRef AccessObjectStubBuilder::ResolvePropKey(GateRef glue, GateRef prop, cons } GateRef AccessObjectStubBuilder::LoadObjByValue(GateRef glue, GateRef receiver, GateRef key, GateRef profileTypeInfo, - GateRef slotId) + GateRef slotId, ProfileOperation callback) { auto env = GetEnvironment(); Label entry(env); @@ -155,16 +150,17 @@ GateRef AccessObjectStubBuilder::LoadObjByValue(GateRef glue, GateRef receiver, GateRef value = 0; ICStubBuilder builder(this); builder.SetParameters(glue, receiver, profileTypeInfo, value, slotId, key); - builder.LoadICByValue(&result, &tryFastPath, &slowPath, &exit); + builder.LoadICByValue(&result, &tryFastPath, &slowPath, &exit, callback); Bind(&tryFastPath); { - result = GetPropertyByValue(glue, receiver, key); + result = GetPropertyByValue(glue, receiver, key, callback); Branch(TaggedIsHole(*result), &slowPath, &exit); } Bind(&slowPath); { result = CallRuntime(glue, RTSTUB_ID(LoadICByValue), { profileTypeInfo, receiver, key, IntToTaggedInt(slotId) }); + callback.TryPreDump(); Jump(&exit); } Bind(&exit); @@ -187,7 +183,7 @@ GateRef AccessObjectStubBuilder::DeprecatedLoadObjByValue(GateRef glue, GateRef Branch(TaggedIsHeapObject(receiver), &fastPath, &slowPath); Bind(&fastPath); { - result = GetPropertyByValue(glue, receiver, key); + result = GetPropertyByValue(glue, receiver, key, ProfileOperation()); Branch(TaggedIsHole(*result), &slowPath, &exit); } Bind(&slowPath); @@ -225,6 +221,7 @@ GateRef AccessObjectStubBuilder::StoreObjByValue(GateRef glue, GateRef receiver, { result = CallRuntime(glue, RTSTUB_ID(StoreICByValue), { profileTypeInfo, receiver, key, value, IntToTaggedInt(slotId) }); + callback.TryPreDump(); Jump(&exit); } Bind(&exit); @@ -234,7 +231,8 @@ GateRef AccessObjectStubBuilder::StoreObjByValue(GateRef glue, GateRef receiver, } GateRef AccessObjectStubBuilder::TryLoadGlobalByName(GateRef glue, GateRef prop, const StringIdInfo &info, - GateRef profileTypeInfo, GateRef slotId) + GateRef profileTypeInfo, GateRef slotId, + ProfileOperation callback) { auto env = GetEnvironment(); Label entry(env); @@ -264,7 +262,7 @@ GateRef AccessObjectStubBuilder::TryLoadGlobalByName(GateRef glue, GateRef prop, Bind(¬FoundInRecord); { GateRef globalObject = GetGlobalObject(glue); - result = GetGlobalOwnProperty(glue, globalObject, propKey); + result = GetGlobalOwnProperty(glue, globalObject, propKey, callback); Branch(TaggedIsHole(*result), &slowPath, &exit); } } @@ -283,7 +281,8 @@ GateRef AccessObjectStubBuilder::TryLoadGlobalByName(GateRef glue, GateRef prop, } GateRef AccessObjectStubBuilder::TryStoreGlobalByName(GateRef glue, GateRef prop, const StringIdInfo &info, - GateRef value, GateRef profileTypeInfo, GateRef slotId) + GateRef value, GateRef profileTypeInfo, GateRef slotId, + ProfileOperation callback) { auto env = GetEnvironment(); Label entry(env); @@ -312,7 +311,7 @@ GateRef AccessObjectStubBuilder::TryStoreGlobalByName(GateRef glue, GateRef prop Bind(¬FoundInRecord); { GateRef globalObject = GetGlobalObject(glue); - result = GetGlobalOwnProperty(glue, globalObject, propKey); + result = GetGlobalOwnProperty(glue, globalObject, propKey, callback); Label isFoundInGlobal(env); Label notFoundInGlobal(env); Branch(TaggedIsHole(*result), ¬FoundInGlobal, &isFoundInGlobal); @@ -345,7 +344,7 @@ GateRef AccessObjectStubBuilder::TryStoreGlobalByName(GateRef glue, GateRef prop } GateRef AccessObjectStubBuilder::LoadGlobalVar(GateRef glue, GateRef prop, const StringIdInfo &info, - GateRef profileTypeInfo, GateRef slotId) + GateRef profileTypeInfo, GateRef slotId, ProfileOperation callback) { auto env = GetEnvironment(); Label entry(env); @@ -364,7 +363,7 @@ GateRef AccessObjectStubBuilder::LoadGlobalVar(GateRef glue, GateRef prop, const { GateRef globalObject = GetGlobalObject(glue); GateRef propKey = ResolvePropKey(glue, prop, info); - result = GetGlobalOwnProperty(glue, globalObject, propKey); + result = GetGlobalOwnProperty(glue, globalObject, propKey, callback); Branch(TaggedIsHole(*result), &slowPath, &exit); } Bind(&slowPath); diff --git a/ecmascript/compiler/access_object_stub_builder.h b/ecmascript/compiler/access_object_stub_builder.h index e669596129e6a7a13045bff825d8c172f655a249..3e972b6a35c701fe095da7434a91a692c54d3cd4 100644 --- a/ecmascript/compiler/access_object_stub_builder.h +++ b/ecmascript/compiler/access_object_stub_builder.h @@ -38,16 +38,17 @@ public: GateRef DeprecatedLoadObjByName(GateRef glue, GateRef receiver, GateRef propKey); GateRef StoreObjByName(GateRef glue, GateRef receiver, GateRef prop, const StringIdInfo &info, GateRef value, GateRef profileTypeInfo, GateRef slotId, ProfileOperation callback = ProfileOperation()); - GateRef LoadObjByValue(GateRef glue, GateRef receiver, GateRef key, GateRef profileTypeInfo, GateRef slotId); + GateRef LoadObjByValue(GateRef glue, GateRef receiver, GateRef key, GateRef profileTypeInfo, GateRef slotId, + ProfileOperation callback = ProfileOperation()); GateRef StoreObjByValue(GateRef glue, GateRef receiver, GateRef key, GateRef value, GateRef profileTypeInfo, GateRef slotId, ProfileOperation callback = ProfileOperation()); GateRef DeprecatedLoadObjByValue(GateRef glue, GateRef receiver, GateRef key); GateRef TryLoadGlobalByName(GateRef glue, GateRef prop, const StringIdInfo &info, - GateRef profileTypeInfo, GateRef slotId); + GateRef profileTypeInfo, GateRef slotId, ProfileOperation callback); GateRef TryStoreGlobalByName(GateRef glue, GateRef prop, const StringIdInfo &info, - GateRef value, GateRef profileTypeInfo, GateRef slotId); + GateRef value, GateRef profileTypeInfo, GateRef slotId, ProfileOperation callback); GateRef LoadGlobalVar(GateRef glue, GateRef prop, const StringIdInfo &info, - GateRef profileTypeInfo, GateRef slotId); + GateRef profileTypeInfo, GateRef slotId, ProfileOperation callback); GateRef StoreGlobalVar(GateRef glue, GateRef prop, const StringIdInfo &info, GateRef value, GateRef profileTypeInfo, GateRef slotId); private: diff --git a/ecmascript/compiler/aot_compiler.cpp b/ecmascript/compiler/aot_compiler.cpp index c213f9e600c2832ad6db5ceb035941e4c05fc531..de87aeb587e58c805e30dff8360cff2dd2f3fb98 100644 --- a/ecmascript/compiler/aot_compiler.cpp +++ b/ecmascript/compiler/aot_compiler.cpp @@ -19,6 +19,7 @@ #include #include "ecmascript/compiler/aot_file/aot_file_manager.h" +#include "ecmascript/compiler/ohos_pkg_args.h" #include "ecmascript/base/string_helper.h" #include "ecmascript/compiler/pass_manager.h" #include "ecmascript/compiler/compiler_log.h" @@ -31,6 +32,7 @@ #include "ecmascript/platform/file.h" namespace panda::ecmascript::kungfu { +namespace { std::string GetHelper() { std::string str; @@ -47,14 +49,25 @@ void AOTInitialize(EcmaVM *vm) vm->GetJSThread()->GetCurrentEcmaContext()->GetTSManager()->Initialize(); } -JSPandaFile *CreateAndVerifyJSPandaFile(const JSRuntimeOptions &runtimeOptions, const std::string &fileName, EcmaVM *vm) +JSPandaFile *CreateAndVerifyJSPandaFile(const JSRuntimeOptions &runtimeOptions, const OhosPkgArgs &pkgArgs, + const std::string &fileName, EcmaVM *vm) { JSPandaFileManager *jsPandaFileManager = JSPandaFileManager::GetInstance(); std::shared_ptr jsPandaFile = nullptr; if (runtimeOptions.IsTargetCompilerMode()) { - std::string hapPath = runtimeOptions.GetHapPath(); - uint32_t offset = runtimeOptions.GetHapAbcOffset(); - uint32_t size = runtimeOptions.GetHapAbcSize(); + std::string hapPath; + uint32_t offset {}; + uint32_t size {}; + if (pkgArgs.Valid()) { + hapPath = pkgArgs.GetPath(); + offset = pkgArgs.GetOffset(); + size = pkgArgs.GetSize(); + } else { + // for legacy params + hapPath = runtimeOptions.GetHapPath(); + offset = runtimeOptions.GetHapAbcOffset(); + size = runtimeOptions.GetHapAbcSize(); + } if (size == 0) { LOG_ECMA(ERROR) << "buffer is empty in target compiler mode!"; return nullptr; @@ -91,6 +104,42 @@ JSPandaFile *CreateAndVerifyJSPandaFile(const JSRuntimeOptions &runtimeOptions, return jsPandaFile.get(); } +bool HandleOhosPkgArgs(EcmaVM *vm, JSRuntimeOptions &runtimeOptions, OhosPkgArgs &pkgArgs, arg_list_t &pandaFileNames) +{ + ASSERT(runtimeOptions.IsTargetCompilerMode()); + if (!runtimeOptions.GetCompilerPkgJsonInfo().empty()) { + if (pkgArgs.ParseFromJson(vm, runtimeOptions.GetCompilerPkgJsonInfo())) { + LOG_COMPILER(INFO) << "Parse main pkg info success."; + pkgArgs.Dump(); + pandaFileNames.emplace_back(pkgArgs.GetFullName()); + } else { + return false; + } + } + // for external pkg, dump it first. + if (!runtimeOptions.GetCompilerExternalPkgJsonInfo().empty()) { + std::list externalList; + OhosPkgArgs::ParseListFromJson(vm, runtimeOptions.GetCompilerExternalPkgJsonInfo(), externalList); + for (const auto &externalPkg : externalList) { + externalPkg.Dump(); + } + } + return true; +} + +void HandleTargetModeInfo(EcmaVM *vm, bool &isEnableOptOnHeapCheck, size_t &optLevel) +{ + JSRuntimeOptions &vmOpt = vm->GetJSOptions(); + ASSERT(vmOpt.IsTargetCompilerMode()); + // target need fast compiler mode + vmOpt.SetFastAOTCompileMode(true); + vmOpt.SetOptLevel(3); // 3: default opt level + optLevel = 3; + vmOpt.SetEnableOptOnHeapCheck(false); + isEnableOptOnHeapCheck = false; +} +} // namespace + int Main(const int argc, const char **argv) { auto startTime = @@ -102,22 +151,13 @@ int Main(const int argc, const char **argv) LOG_ECMA(DEBUG) << argv[i]; } - int newArgc = argc; if (argc < 2) { // 2: at least have two arguments LOG_COMPILER(ERROR) << GetHelper(); return -1; } - std::string files = argv[argc - 1]; - if (!base::StringHelper::EndsWith(files, ".abc")) { - LOG_COMPILER(ERROR) << "The last argument must be abc file" << std::endl; - LOG_COMPILER(ERROR) << GetHelper(); - return 1; - } - - newArgc--; JSRuntimeOptions runtimeOptions; - bool retOpt = runtimeOptions.ParseCommand(newArgc, argv); + bool retOpt = runtimeOptions.ParseCommand(argc, argv); if (!retOpt) { LOG_COMPILER(ERROR) << GetHelper(); return 1; @@ -141,7 +181,8 @@ int Main(const int argc, const char **argv) { LocalScope scope(vm); std::string delimiter = GetFileDelimiter(); - arg_list_t pandaFileNames = base::StringHelper::SplitString(files, delimiter); + arg_list_t pandaFileNames {}; + OhosPkgArgs pkgArgs; std::string triple = runtimeOptions.GetTargetTriple(); if (runtimeOptions.GetAOTOutputFile().empty()) { @@ -155,6 +196,7 @@ int Main(const int argc, const char **argv) bool compilerLogTime = runtimeOptions.IsEnableCompilerLogTime(); size_t maxAotMethodSize = runtimeOptions.GetMaxAotMethodSize(); size_t maxMethodsInModule = runtimeOptions.GetCompilerModuleMethods(); + bool isEnableArrayBoundsCheckElimination = runtimeOptions.IsEnableArrayBoundsCheckElimination(); bool isEnableTypeLowering = runtimeOptions.IsEnableTypeLowering(); bool isEnableEarlyElimination = runtimeOptions.IsEnableEarlyElimination(); bool isEnableLaterElimination = runtimeOptions.IsEnableLaterElimination(); @@ -163,9 +205,33 @@ int Main(const int argc, const char **argv) bool isEnableTypeInfer = isEnableTypeLowering || vm->GetJSThread()->GetCurrentEcmaContext()->GetTSManager()->AssertTypes(); bool isEnableOptPGOType = runtimeOptions.IsEnableOptPGOType(); + bool isEnableOptTrackField = runtimeOptions.IsEnableOptTrackField(); + bool isEnableOptLoopPeeling = runtimeOptions.IsEnableOptLoopPeeling(); + bool isEnableOptOnHeapCheck = runtimeOptions.IsEnableOptOnHeapCheck(); + bool isEnableOptLoopInvariantCodeMotion = runtimeOptions.IsEnableOptLoopInvariantCodeMotion(); + if (runtimeOptions.IsTargetCompilerMode()) { + if (!HandleOhosPkgArgs(vm, runtimeOptions, pkgArgs, pandaFileNames)) { + LOG_COMPILER(ERROR) << GetHelper(); + LOG_COMPILER(ERROR) << "Parse pkg info failed, exit."; + return 1; + } + HandleTargetModeInfo(vm, isEnableOptOnHeapCheck, optLevel); + } + if (runtimeOptions.GetCompilerPkgJsonInfo().empty() || !pkgArgs.Valid()) { + // if no pkgArgs, last param must be abc file + std::string files = argv[argc - 1]; + if (!base::StringHelper::EndsWith(files, ".abc")) { + LOG_COMPILER(ERROR) << "The last argument must be abc file" << std::endl; + LOG_COMPILER(ERROR) << GetHelper(); + return 1; + } + pandaFileNames = base::StringHelper::SplitString(files, delimiter); + } + PassOptions passOptions(isEnableArrayBoundsCheckElimination, isEnableTypeLowering, isEnableEarlyElimination, + isEnableLaterElimination, isEnableValueNumbering, isEnableTypeInfer, + isEnableOptInlining, isEnableOptPGOType, isEnableOptTrackField, isEnableOptLoopPeeling, + isEnableOptOnHeapCheck, isEnableOptLoopInvariantCodeMotion); - PassOptions passOptions(isEnableTypeLowering, isEnableEarlyElimination, isEnableLaterElimination, - isEnableValueNumbering, isEnableTypeInfer, isEnableOptInlining, isEnableOptPGOType); uint32_t hotnessThreshold = runtimeOptions.GetPGOHotnessThreshold(); AOTInitialize(vm); @@ -183,7 +249,7 @@ int Main(const int argc, const char **argv) for (const auto &fileName : pandaFileNames) { auto extendedFilePath = panda::os::file::File::GetExtendedFilePath(fileName); LOG_COMPILER(INFO) << "AOT compile: " << extendedFilePath; - JSPandaFile *jsPandaFile = CreateAndVerifyJSPandaFile(runtimeOptions, extendedFilePath, vm); + JSPandaFile *jsPandaFile = CreateAndVerifyJSPandaFile(runtimeOptions, pkgArgs, extendedFilePath, vm); if (passManager.Compile(jsPandaFile, extendedFilePath, generator) == false) { ret = false; continue; diff --git a/ecmascript/compiler/aot_file/an_file_data_manager.cpp b/ecmascript/compiler/aot_file/an_file_data_manager.cpp index 723dfacfe58247c9c83f8548b6135fee6e00acfc..f6ca3e36825d497f2e8d568d3284aa6297d5cda8 100644 --- a/ecmascript/compiler/aot_file/an_file_data_manager.cpp +++ b/ecmascript/compiler/aot_file/an_file_data_manager.cpp @@ -38,7 +38,7 @@ void AnFileDataManager::DestroyFileMapMem(MemMap &fileMapMem) void AnFileDataManager::SafeDestroyAllData() { - os::memory::WriteLockHolder lock(lock_); + WriteLockHolder lock(lock_); if (loadedStub_ != nullptr) { ExecutedMemoryAllocator::DestroyBuf(loadedStub_->GetStubsMem()); loadedStub_ = nullptr; @@ -53,7 +53,7 @@ void AnFileDataManager::SafeDestroyAllData() void AnFileDataManager::SafeDestroyAnData(const std::string &fileName) { - os::memory::WriteLockHolder lock(lock_); + WriteLockHolder lock(lock_); std::string anBasename = JSFilePath::GetBaseName(fileName); auto index = UnSafeGetFileInfoIndex(anBasename); if (index == INVALID_INDEX) { @@ -65,7 +65,7 @@ void AnFileDataManager::SafeDestroyAnData(const std::string &fileName) bool AnFileDataManager::SafeLoad(const std::string &fileName, Type type) { - os::memory::WriteLockHolder lock(lock_); + WriteLockHolder lock(lock_); if (type == Type::STUB) { if (loadedStub_ != nullptr) { return true; @@ -133,19 +133,19 @@ uint32_t AnFileDataManager::UnSafeGetFileInfoIndex(const std::string &fileName) uint32_t AnFileDataManager::SafeGetFileInfoIndex(const std::string &fileName) { - os::memory::ReadLockHolder lock(lock_); + ReadLockHolder lock(lock_); return UnSafeGetFileInfoIndex(fileName); } std::shared_ptr AnFileDataManager::SafeGetAnFileInfo(uint32_t index) { - os::memory::ReadLockHolder lock(lock_); + ReadLockHolder lock(lock_); return UnSafeGetAnFileInfo(index); } std::shared_ptr AnFileDataManager::SafeGetStubFileInfo() { - os::memory::ReadLockHolder lock(lock_); + ReadLockHolder lock(lock_); return loadedStub_; } @@ -165,7 +165,7 @@ bool AnFileDataManager::SafeTryReadLock() bool AnFileDataManager::SafeInsideStub(uintptr_t pc) { - os::memory::ReadLockHolder lock(lock_); + ReadLockHolder lock(lock_); if (loadedStub_ == nullptr) { LOG_COMPILER(ERROR) << "SafeInsideStub: The stub file is not loaded."; return false; @@ -189,7 +189,7 @@ bool AnFileDataManager::SafeInsideStub(uintptr_t pc) bool AnFileDataManager::SafeInsideAOT(uintptr_t pc) { - os::memory::ReadLockHolder lock(lock_); + ReadLockHolder lock(lock_); for (auto &info : loadedAn_) { const std::vector &des = info->GetCodeUnits(); for (const auto &curDes : des) { @@ -203,7 +203,7 @@ bool AnFileDataManager::SafeInsideAOT(uintptr_t pc) AOTFileInfo::CallSiteInfo AnFileDataManager::SafeCalCallSiteInfo(uintptr_t retAddr) { - os::memory::ReadLockHolder lock(lock_); + ReadLockHolder lock(lock_); AOTFileInfo::CallSiteInfo callsiteInfo; bool ans = false; diff --git a/ecmascript/compiler/aot_file/an_file_data_manager.h b/ecmascript/compiler/aot_file/an_file_data_manager.h index 555e7c9fe7f6bde0ba64f357f27a44d2b2894c3e..eccf45014a1c0e7d49a1b552efdf08e3c4f3fe36 100644 --- a/ecmascript/compiler/aot_file/an_file_data_manager.h +++ b/ecmascript/compiler/aot_file/an_file_data_manager.h @@ -76,7 +76,7 @@ private: return loadedAn_.at(index); } - os::memory::RWLock lock_ {}; + RWLock lock_ {}; std::unordered_map anFileNameToIndexMap_ {}; std::vector> loadedAn_ {}; std::shared_ptr loadedStub_ {nullptr}; diff --git a/ecmascript/compiler/aot_file/an_file_info.cpp b/ecmascript/compiler/aot_file/an_file_info.cpp index 5d677e11e0c7aa9bc50a23a3d397f57577634283..9d965f35bfa0b72265b1a882a4279779416a6950 100644 --- a/ecmascript/compiler/aot_file/an_file_info.cpp +++ b/ecmascript/compiler/aot_file/an_file_info.cpp @@ -40,7 +40,7 @@ void AnFileInfo::Save(const std::string &filename, Triple triple) ElfBuilder builder(des_, GetDumpSectionNames()); llvm::ELF::Elf64_Ehdr header; - builder.PackELFHeader(header, base::FileHeader::ToVersionNumber(AOTFileVersion::AN_VERSION), triple); + builder.PackELFHeader(header, base::FileHeaderBase::ToVersionNumber(AOTFileVersion::AN_VERSION), triple); file.write(reinterpret_cast(&header), sizeof(llvm::ELF::Elf64_Ehdr)); builder.PackELFSections(file); builder.PackELFSegment(file); @@ -69,7 +69,7 @@ bool AnFileInfo::Load(const std::string &filename) ElfReader reader(fileMapMem_); std::vector secs = GetDumpSectionNames(); - if (!reader.VerifyELFHeader(base::FileHeader::ToVersionNumber(AOTFileVersion::AN_VERSION), + if (!reader.VerifyELFHeader(base::FileHeaderBase::ToVersionNumber(AOTFileVersion::AN_VERSION), AOTFileVersion::AN_STRICT_MATCH)) { return false; } diff --git a/ecmascript/compiler/aot_file/an_file_info.h b/ecmascript/compiler/aot_file/an_file_info.h index 1ee48111479eb1e872e70795bd81457feebee40b..ebd62be068067c9a0382e9e43afdc4036dcc9a5e 100644 --- a/ecmascript/compiler/aot_file/an_file_info.h +++ b/ecmascript/compiler/aot_file/an_file_info.h @@ -47,9 +47,9 @@ public: void TryRemoveAnFile(const char *filename); - void AlignTextSec() + void AlignTextSec(uint32_t alignSize) { - curTextSecOffset_ = AlignUp(curTextSecOffset_, TEXT_SEC_ALIGN); + curTextSecOffset_ = AlignUp(curTextSecOffset_, alignSize); } void UpdateCurTextSecOffset(uint64_t size) diff --git a/ecmascript/compiler/aot_file/aot_file_info.h b/ecmascript/compiler/aot_file/aot_file_info.h index 82ffcc78e1b070d81e7f931cf7a6c9806f619c2b..4d7f772bd052987ab054b03d3750585057fa3573 100644 --- a/ecmascript/compiler/aot_file/aot_file_info.h +++ b/ecmascript/compiler/aot_file/aot_file_info.h @@ -32,8 +32,9 @@ public: AOTFileInfo() = default; virtual ~AOTFileInfo() = default; - static constexpr uint32_t DATA_SEC_ALIGN = 8; static constexpr uint32_t TEXT_SEC_ALIGN = 16; + static constexpr uint32_t DATA_SEC_ALIGN = 8; + static constexpr uint32_t PAGE_ALIGN = 4096; struct FuncEntryDes { uint64_t codeAddr_ {}; @@ -149,7 +150,7 @@ public: des.SetArkStackMapSize(size); } - size_t GetCodeUnitsNum() + size_t GetCodeUnitsNum() const { return des_.size(); } diff --git a/ecmascript/compiler/aot_file/aot_file_manager.cpp b/ecmascript/compiler/aot_file/aot_file_manager.cpp index 6428e23b96a3bd87697236b06726e551447b645f..f17b51bd371d254f253ec708afd844b8f2f29624 100644 --- a/ecmascript/compiler/aot_file/aot_file_manager.cpp +++ b/ecmascript/compiler/aot_file/aot_file_manager.cpp @@ -20,6 +20,7 @@ #include "ecmascript/compiler/aot_file/elf_builder.h" #include "ecmascript/compiler/aot_file/elf_reader.h" #include "ecmascript/compiler/bc_call_signature.h" +#include "ecmascript/compiler/call_signature.h" #include "ecmascript/compiler/common_stubs.h" #include "ecmascript/compiler/compiler_log.h" #include "ecmascript/deoptimizer/deoptimizer.h" @@ -82,24 +83,25 @@ bool AOTFileManager::LoadAiFile([[maybe_unused]] const std::string &filename) #endif } -void AOTFileManager::LoadAiFile(const JSPandaFile *jsPandaFile) +bool AOTFileManager::LoadAiFile(const JSPandaFile *jsPandaFile) { uint32_t anFileInfoIndex = GetAnFileIndex(jsPandaFile); // this abc file does not have corresponding an file if (anFileInfoIndex == INVALID_INDEX) { - return; + return false; } auto iter = desCPs_.find(anFileInfoIndex); // already loaded if (iter != desCPs_.end()) { - return; + return false; } AnFileDataManager *anFileDataManager = AnFileDataManager::GetInstance(); std::string aiFilename = anFileDataManager->GetDir(); aiFilename += JSFilePath::GetHapName(jsPandaFile) + AOTFileManager::FILE_EXTENSION_AI; LoadAiFile(aiFilename); + return true; } const std::shared_ptr AOTFileManager::GetAnFileInfo(const JSPandaFile *jsPandaFile) const @@ -217,6 +219,10 @@ void AOTFileManager::SetAOTMainFuncEntry(JSHandle mainFunc, const JS #ifndef NDEBUG PrintAOTEntry(jsPandaFile, method, mainEntry); #endif + + MethodLiteral *methodLiteral = method->GetMethodLiteral(); + methodLiteral->SetAotCodeBit(true); + methodLiteral->SetIsFastCall(isFastCall); } void AOTFileManager::SetAOTFuncEntry(const JSPandaFile *jsPandaFile, Method *method, @@ -239,6 +245,10 @@ void AOTFileManager::SetAOTFuncEntry(const JSPandaFile *jsPandaFile, Method *met if (canFastCall != nullptr) { *canFastCall = entry.isFastCall_; } + + MethodLiteral *methodLiteral = method->GetMethodLiteral(); + methodLiteral->SetAotCodeBit(true); + methodLiteral->SetIsFastCall(entry.isFastCall_); } kungfu::ArkStackMapParser *AOTFileManager::GetStackMapParser() const @@ -393,4 +403,12 @@ bool AOTFileManager::GetAbsolutePath(const CString &relativePathCstr, CString &a } return false; } + +const Heap *AOTFileManager::GetHeap() +{ + if (vm_ == nullptr) { + return nullptr; + } + return vm_->GetHeap(); +} } // namespace panda::ecmascript diff --git a/ecmascript/compiler/aot_file/aot_file_manager.h b/ecmascript/compiler/aot_file/aot_file_manager.h index a01d72bbb97ac4b0c99b5ba855dfb1bb4e66644f..77766633d47ff2f353e35391a94de7fcbc70b342 100644 --- a/ecmascript/compiler/aot_file/aot_file_manager.h +++ b/ecmascript/compiler/aot_file/aot_file_manager.h @@ -36,6 +36,18 @@ namespace panda::ecmascript { class JSpandafile; class JSThread; +/* AOTLiteralInfo + * +--------------------------------+---- + * | cache | ^ + * | ... | | + * | -1 (No AOT Function Entry) | | + * | AOT Function Entry Index | | + * | AOT Function Entry Index | | + * +--------------------------------+---- + * | AOT Instance Hclass (IHC) | + * | AOT Constructor Hclass (CHC) | + * +--------------------------------+ + */ class AOTLiteralInfo : public TaggedArray { public: static AOTLiteralInfo *Cast(TaggedObject *object) @@ -43,6 +55,67 @@ public: ASSERT(JSTaggedValue(object).IsTaggedArray()); return static_cast(object); } + + static size_t ComputeSize(uint32_t cacheSize) + { + return TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), cacheSize + RESERVED_LENGTH); + } + + inline void InitializeWithSpecialValue(JSTaggedValue initValue, uint32_t capacity, uint32_t extraLength = 0) + { + TaggedArray::InitializeWithSpecialValue(initValue, capacity + RESERVED_LENGTH, extraLength); + SetIhc(JSTaggedValue::Undefined()); + SetChc(JSTaggedValue::Undefined()); + } + + inline uint32_t GetCacheLength() const + { + return GetLength() - RESERVED_LENGTH; + } + + inline void SetIhc(JSTaggedValue value) + { + Barriers::SetPrimitive(GetData(), GetIhcOffset(), value.GetRawData()); + } + + inline JSTaggedValue GetIhc() const + { + return JSTaggedValue(Barriers::GetValue(GetData(), GetIhcOffset())); + } + + inline void SetChc(JSTaggedValue value) + { + Barriers::SetPrimitive(GetData(), GetChcOffset(), value.GetRawData()); + } + + inline JSTaggedValue GetChc() const + { + return JSTaggedValue(Barriers::GetValue(GetData(), GetChcOffset())); + } + + inline void SetObjectToCache(JSThread *thread, uint32_t index, JSTaggedValue value) + { + Set(thread, index, value); + } + + inline JSTaggedValue GetObjectFromCache(uint32_t index) const + { + return Get(index); + } +private: + static constexpr size_t AOT_CHC_INDEX = 1; + static constexpr size_t AOT_IHC_INDEX = 2; + static constexpr size_t RESERVED_LENGTH = AOT_IHC_INDEX; + + inline size_t GetIhcOffset() const + { + return JSTaggedValue::TaggedTypeSize() * (GetLength() - AOT_IHC_INDEX); + } + + inline size_t GetChcOffset() const + { + return JSTaggedValue::TaggedTypeSize() * (GetLength() - AOT_CHC_INDEX); + } }; class AOTFileManager { @@ -71,13 +144,14 @@ public: void SetAOTFuncEntry(const JSPandaFile *jsPandaFile, Method *method, uint32_t entryIndex, bool *canFastCall = nullptr); bool LoadAiFile([[maybe_unused]] const std::string &filename); - void LoadAiFile(const JSPandaFile *jsPandaFile); + bool LoadAiFile(const JSPandaFile *jsPandaFile); kungfu::ArkStackMapParser* GetStackMapParser() const; static JSTaggedValue GetAbsolutePath(JSThread *thread, JSTaggedValue relativePathVal); static bool GetAbsolutePath(const CString &relativePathCstr, CString &absPathCstr); static bool RewriteDataSection(uintptr_t dataSec, size_t size, uintptr_t newData, size_t newSize); void AddConstantPool(const CString &snapshotFileName, JSTaggedValue deserializedCPList); JSHandle GetDeserializedConstantPool(const JSPandaFile *jsPandaFile, int32_t cpID); + const Heap *GetHeap(); static void DumpAOTInfo() DUMP_API_ATTR; diff --git a/ecmascript/compiler/aot_file/aot_version.h b/ecmascript/compiler/aot_file/aot_version.h index 0e50be1746469f82461e0d71cc3cf5b780f4dc42..28d7f771694e05430638739f016835e4ff06a4d2 100644 --- a/ecmascript/compiler/aot_file/aot_version.h +++ b/ecmascript/compiler/aot_file/aot_version.h @@ -25,10 +25,10 @@ public: // Release Version Snapshot Version // 3.2 0.0.0.x // 4.0 4.0.0.x - static constexpr base::FileHeader::VersionType AN_VERSION = {4, 0, 0, 3}; + static constexpr base::FileHeaderBase::VersionType AN_VERSION = {4, 0, 0, 5}; static constexpr bool AN_STRICT_MATCH = true; - static constexpr base::FileHeader::VersionType AI_VERSION = {4, 0, 0, 1}; + static constexpr base::FileHeaderBase::VersionType AI_VERSION = {4, 0, 0, 2}; static constexpr bool AI_STRICT_MATCH = true; }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_AOT_VERSION_H \ No newline at end of file +#endif // ECMASCRIPT_AOT_VERSION_H diff --git a/ecmascript/compiler/aot_file/elf_builder.cpp b/ecmascript/compiler/aot_file/elf_builder.cpp index 044b2ab20b36aba52ccf4e62e15f76ee2929834f..8edd523abe874ba90d024d8bf12273b68d58c3a8 100644 --- a/ecmascript/compiler/aot_file/elf_builder.cpp +++ b/ecmascript/compiler/aot_file/elf_builder.cpp @@ -23,7 +23,7 @@ void ElfBuilder::AddShStrTabSection() { std::map> §ions = des_[ShStrTableModuleDesIndex].GetSectionsInfo(); - + uint32_t size = 1; for (auto &s : sections_) { std::string str = ModuleSectionDes::GetSecName(s); @@ -83,12 +83,7 @@ void ElfBuilder::Initialize() des_[i].AddArkStackMapSection(); } sectionToAlign_ = { - {ElfSecName::RODATA, AOTFileInfo::TEXT_SEC_ALIGN}, - {ElfSecName::RODATA_CST4, AOTFileInfo::TEXT_SEC_ALIGN}, - {ElfSecName::RODATA_CST8, AOTFileInfo::TEXT_SEC_ALIGN}, - {ElfSecName::RODATA_CST16, AOTFileInfo::TEXT_SEC_ALIGN}, - {ElfSecName::RODATA_CST32, AOTFileInfo::TEXT_SEC_ALIGN}, - {ElfSecName::TEXT, AOTFileInfo::TEXT_SEC_ALIGN}, + {ElfSecName::TEXT, AOTFileInfo::PAGE_ALIGN}, {ElfSecName::STRTAB, 1}, {ElfSecName::SYMTAB, AOTFileInfo::DATA_SEC_ALIGN}, {ElfSecName::SHSTRTAB, AOTFileInfo::DATA_SEC_ALIGN}, @@ -155,7 +150,7 @@ uint32_t ElfBuilder::GetShIndex(ElfSecName section) const int ElfBuilder::GetSecNum() const { - return sections_.size(); + return sections_.size() + 1; // add first empty section. } /* @@ -218,7 +213,7 @@ void ElfBuilder::PackELFHeader(llvm::ELF::Elf64_Ehdr &header, uint32_t version, // size of section headers header.e_shentsize = sizeof(llvm::ELF::Elf64_Shdr); // number of section headers - header.e_shnum = GetSecNum() + 1; // 1: skip null section and ark stackmap + header.e_shnum = GetSecNum(); // section header string table index header.e_shstrndx = static_cast(GetShIndex(ElfSecName::SHSTRTAB)); // section header stub sec info index @@ -301,8 +296,10 @@ std::pair ElfBuilder::FindShStrTab() const void ElfBuilder::AllocateShdr(std::unique_ptr &shdr, const uint32_t &secNum) { shdr = std::make_unique(secNum); - if (memset_s(reinterpret_cast(&shdr[0]), sizeof(llvm::ELF::Elf64_Shdr), - 0, sizeof(llvm::ELF::Elf64_Shdr)) != EOK) { + if (memset_s(reinterpret_cast(&shdr[0]), + sizeof(llvm::ELF::Elf64_Shdr), + 0, + sizeof(llvm::ELF::Elf64_Shdr)) != EOK) { UNREACHABLE(); } } @@ -331,19 +328,32 @@ void ElfBuilder::MergeTextSections(std::ofstream &file, ModuleSectionDes::ModuleRegionInfo &curInfo = moduleInfo[i]; uint32_t curSecSize = des.GetSecSize(ElfSecName::TEXT); uint64_t curSecAddr = des.GetSecAddr(ElfSecName::TEXT); - curSecOffset = AlignUp(curSecOffset, AOTFileInfo::TEXT_SEC_ALIGN); + curSecOffset = AlignUp(curSecOffset, AOTFileInfo::PAGE_ALIGN); file.seekp(curSecOffset); auto curModuleSec = des.GetSectionsInfo(); - if (curModuleSec.find(ElfSecName::RODATA_CST8) != curModuleSec.end()) { - uint32_t rodataSize = des.GetSecSize(ElfSecName::RODATA_CST8); - uint64_t rodataAddr = des.GetSecAddr(ElfSecName::RODATA_CST8); - file.write(reinterpret_cast(rodataAddr), rodataSize); - curInfo.rodataSize = rodataSize; - curSecOffset += rodataSize; + uint64_t rodataAddrBeforeText = 0; + uint32_t rodataSizeBeforeText = 0; + uint64_t rodataAddrAfterText = 0; + uint32_t rodataSizeAfterText = 0; + std::tie(rodataAddrBeforeText, rodataSizeBeforeText, rodataAddrAfterText, rodataSizeAfterText) = + des.GetMergedRODataAddrAndSize(curSecAddr); + if (rodataSizeBeforeText != 0) { + file.write(reinterpret_cast(rodataAddrBeforeText), rodataSizeBeforeText); + curInfo.rodataSizeBeforeText = rodataSizeBeforeText; + curSecOffset += rodataSizeBeforeText; + curSecOffset = AlignUp(curSecOffset, AOTFileInfo::TEXT_SEC_ALIGN); + file.seekp(curSecOffset); } - curInfo.textSize = curSecSize; file.write(reinterpret_cast(curSecAddr), curSecSize); + curInfo.textSize = curSecSize; curSecOffset += curSecSize; + if (rodataSizeAfterText != 0) { + curSecOffset = AlignUp(curSecOffset, AOTFileInfo::DATA_SEC_ALIGN); + file.seekp(curSecOffset); + file.write(reinterpret_cast(rodataAddrAfterText), rodataSizeAfterText); + curInfo.rodataSizeAfterText = rodataSizeAfterText; + curSecOffset += rodataSizeAfterText; + } } } @@ -366,6 +376,38 @@ void ElfBuilder::MergeArkStackMapSections(std::ofstream &file, } } +void ElfBuilder::FixSymtab(llvm::ELF::Elf64_Shdr* shdr) +{ + using Elf64_Sym = llvm::ELF::Elf64_Sym; + + uint32_t secSize = des_[FullSecIndex].GetSecSize(ElfSecName::SYMTAB); + uint64_t secAddr = des_[FullSecIndex].GetSecAddr(ElfSecName::SYMTAB); + uint32_t secNum = static_cast(GetSecNum()); + uint64_t textSecOffset = sectionToShdr_[ElfSecName::TEXT].sh_offset; + uint32_t shStrTabIndex = GetShIndex(ElfSecName::SHSTRTAB); + uint32_t textSecIndex = GetShIndex(ElfSecName::TEXT); + + Elf64_Sym *syms = reinterpret_cast(secAddr); + size_t n = secSize / sizeof(Elf64_Sym); + int localCount = -1; + for (size_t i = 0; i < n; ++i) { + Elf64_Sym* sy = &syms[i]; + if (sy->getBinding() != llvm::ELF::STB_LOCAL && localCount == -1) { + localCount = static_cast(i); + } + if (sy->getType() == llvm::ELF::STT_SECTION) { + sy->st_shndx = static_cast(shStrTabIndex); + } else if (sy->getType() == llvm::ELF::STT_FUNC) { + sy->st_shndx = static_cast(textSecIndex); + sy->st_value += textSecOffset; + } + if (sy->st_shndx > secNum) { + sy->st_shndx = 0; + } + } + shdr->sh_info = static_cast(localCount); +} + /* section of aot.an layout as follows: @@ -410,7 +452,7 @@ void ElfBuilder::PackELFSections(std::ofstream &file) llvm::ELF::Elf64_Off curSecOffset = ComputeEndAddrOfShdr(secNum); file.seekp(curSecOffset); - int i = 1; // 1: skip null section + int i = static_cast(GetShIndex(ElfSecName::TEXT)); auto shStrTab = FindShStrTab(); for (auto const &[secName, secInfo] : sections) { @@ -427,7 +469,7 @@ void ElfBuilder::PackELFSections(std::ofstream &file) std::string secNameStr = ModuleSectionDes::GetSecName(secName); // text section address needs 16 bytes alignment if (secName == ElfSecName::TEXT) { - curSecOffset = AlignUp(curSecOffset, AOTFileInfo::TEXT_SEC_ALIGN); + curSecOffset = AlignUp(curSecOffset, AOTFileInfo::PAGE_ALIGN); file.seekp(curSecOffset); } llvm::ELF::Elf64_Word shName = FindShName(secNameStr, shStrTab.first, shStrTab.second); @@ -437,6 +479,7 @@ void ElfBuilder::PackELFSections(std::ofstream &file) curShdr.sh_flags = section.Flag(); curShdr.sh_addr = curSecOffset; curShdr.sh_offset = static_cast(curSecOffset); + curShdr.sh_info = 0; sectionToFileOffset_[secName] = static_cast(file.tellp()); switch (secName) { case ElfSecName::ARK_MODULEINFO: { @@ -463,6 +506,9 @@ void ElfBuilder::PackELFSections(std::ofstream &file) case ElfSecName::SHSTRTAB: case ElfSecName::ARK_FUNCENTRY: case ElfSecName::ARK_ASMSTUB: { + if (secName == ElfSecName::SYMTAB) { + FixSymtab(&curShdr); + } uint32_t curSecSize = des_[FullSecIndex].GetSecSize(secName); uint64_t curSecAddr = des_[FullSecIndex].GetSecAddr(secName); file.write(reinterpret_cast(curSecAddr), curSecSize); @@ -480,7 +526,6 @@ void ElfBuilder::PackELFSections(std::ofstream &file) file.seekp(curSecOffset); } curShdr.sh_link = static_cast(section.Link()); - curShdr.sh_info = 0; curShdr.sh_entsize = static_cast(section.Entsize()); sectionToShdr_[secName] = curShdr; LOG_COMPILER(DEBUG) << " shdr[i].sh_entsize " << std::hex << curShdr.sh_entsize << std::endl; diff --git a/ecmascript/compiler/aot_file/elf_builder.h b/ecmascript/compiler/aot_file/elf_builder.h index a1722003fa0c84e5098ce93c381a6d4cd1357123..710ae95fc0c377525de2953075b03ac5be618b05 100644 --- a/ecmascript/compiler/aot_file/elf_builder.h +++ b/ecmascript/compiler/aot_file/elf_builder.h @@ -65,10 +65,12 @@ private: void Initialize(); void SetLastSection(); void RemoveNotNeedSection(); + void FixSymtab(llvm::ELF::Elf64_Shdr* shdr); static constexpr uint32_t ASMSTUB_MODULE_NUM = 3; static constexpr uint32_t ShStrTableModuleDesIndex = 0; static constexpr uint32_t FullSecIndex = 0; + std::vector des_ {}; std::unique_ptr shStrTabPtr_ {nullptr}; std::map sectionToShdr_; diff --git a/ecmascript/compiler/aot_file/elf_reader.cpp b/ecmascript/compiler/aot_file/elf_reader.cpp index bc1f56c9688396d32e794e8edb85cd6944a06921..8223e230850d26eb19f95ef8460cb95a15f368c9 100644 --- a/ecmascript/compiler/aot_file/elf_reader.cpp +++ b/ecmascript/compiler/aot_file/elf_reader.cpp @@ -32,7 +32,7 @@ bool ElfReader::VerifyELFHeader(uint32_t version, bool strictMatch) << header.e_ident[llvm::ELF::EI_MAG2] << header.e_ident[llvm::ELF::EI_MAG3]; return false; } - if (!base::FileHeader::VerifyVersion("Elf ", header.e_version, version, strictMatch)) { + if (!base::FileHeaderBase::VerifyVersion("Elf ", header.e_version, version, strictMatch)) { return false; } return true; @@ -256,15 +256,20 @@ void ElfReader::SeparateTextSections(std::vector &des, { for (size_t i = 0; i < des.size(); ++i) { auto moduleInfo = GetCurModuleInfo(i, moduleInfoOffset); - secOffset = AlignUp(secOffset, TEXT_SEC_ALIGN); - uint32_t rodataSize = moduleInfo->rodataSize; - if (rodataSize > 0) { - des[i].SetSecAddrAndSize(ElfSecName::RODATA_CST8, secAddr + secOffset, rodataSize); - secOffset += rodataSize; + secOffset = AlignUp(secOffset, AOTFileInfo::PAGE_ALIGN); + uint32_t rodataSizeBeforeText = moduleInfo->rodataSizeBeforeText; + uint32_t rodataSizeAfterText = moduleInfo->rodataSizeAfterText; + if (rodataSizeBeforeText != 0) { + secOffset += rodataSizeBeforeText; + secOffset = AlignUp(secOffset, AOTFileInfo::TEXT_SEC_ALIGN); } uint32_t textSize = moduleInfo->textSize; des[i].SetSecAddrAndSize(ElfSecName::TEXT, secAddr + secOffset, textSize); secOffset += textSize; + if (rodataSizeAfterText != 0) { + secOffset = AlignUp(secOffset, AOTFileInfo::DATA_SEC_ALIGN); + secOffset += rodataSizeAfterText; + } } } @@ -294,17 +299,25 @@ void ElfReader::SeparateTextSections(BinaryBufferParser &parser, { for (size_t i = 0; i < des.size(); ++i) { auto moduleInfo = moduleInfo_[i]; - secOffset = AlignUp(secOffset, TEXT_SEC_ALIGN); - uint32_t rodataSize = moduleInfo.rodataSize; - if (rodataSize > 0) { - parser.ParseBuffer(reinterpret_cast(secAddr + secOffset), rodataSize, curShOffset + secOffset); - des[i].SetSecAddrAndSize(ElfSecName::RODATA_CST8, secAddr + secOffset, rodataSize); - secOffset += rodataSize; + secOffset = AlignUp(secOffset, AOTFileInfo::PAGE_ALIGN); + uint32_t rodataSizeBeforeText = moduleInfo.rodataSizeBeforeText; + uint32_t rodataSizeAfterText = moduleInfo.rodataSizeAfterText; + if (rodataSizeBeforeText != 0) { + parser.ParseBuffer(reinterpret_cast(secAddr + secOffset), rodataSizeBeforeText, + curShOffset + secOffset); + secOffset += rodataSizeBeforeText; + secOffset = AlignUp(secOffset, AOTFileInfo::TEXT_SEC_ALIGN); } uint32_t textSize = moduleInfo.textSize; parser.ParseBuffer(reinterpret_cast(secAddr + secOffset), textSize, curShOffset + secOffset); des[i].SetSecAddrAndSize(ElfSecName::TEXT, secAddr + secOffset, textSize); secOffset += textSize; + if (rodataSizeAfterText != 0) { + secOffset = AlignUp(secOffset, AOTFileInfo::DATA_SEC_ALIGN); + parser.ParseBuffer(reinterpret_cast(secAddr + secOffset), rodataSizeAfterText, + curShOffset + secOffset); + secOffset += rodataSizeAfterText; + } } } diff --git a/ecmascript/compiler/aot_file/elf_reader.h b/ecmascript/compiler/aot_file/elf_reader.h index 6771e6d302c2269df292f9faaf4f1b978f3bc28c..4a6ef0a7f44f3bf292ec095503a83c6bd6f3e997 100644 --- a/ecmascript/compiler/aot_file/elf_reader.h +++ b/ecmascript/compiler/aot_file/elf_reader.h @@ -53,11 +53,10 @@ private: return moduleInfoSize / sizeof(ModuleSectionDes::ModuleRegionInfo); } - static constexpr uint32_t TEXT_SEC_ALIGN = 16; static constexpr uint32_t ASMSTUB_MODULE_NUM = 3; ExecutedMemoryAllocator::ExeMem stubsMem_ {}; MemMap fileMapMem_ {}; std::vector moduleInfo_; }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_COMPILER_AOT_FILE_ELF_READER_H \ No newline at end of file +#endif // ECMASCRIPT_COMPILER_AOT_FILE_ELF_READER_H diff --git a/ecmascript/compiler/aot_file/module_section_des.h b/ecmascript/compiler/aot_file/module_section_des.h index 5e292e21edb0960b88aefa3adc15d3bd455f44f5..b92e67b8c5a62cff788dadccd4a0f501511e6ade 100644 --- a/ecmascript/compiler/aot_file/module_section_des.h +++ b/ecmascript/compiler/aot_file/module_section_des.h @@ -18,6 +18,7 @@ #include #include +#include "ecmascript/base/number_helper.h" #include "ecmascript/compiler/aot_file/binary_buffer_parser.h" #include "ecmascript/compiler/binary_section.h" @@ -27,12 +28,45 @@ public: struct ModuleRegionInfo { uint32_t startIndex {0}; uint32_t funcCount {0}; - uint32_t rodataSize {0}; + uint32_t rodataSizeBeforeText {0}; + uint32_t rodataSizeAfterText {0}; uint32_t textSize {0}; uint32_t stackMapSize {0}; }; static std::string GetSecName(ElfSecName idx); + void UpdateRODataInfo(uint64_t textAddr, uint64_t &addrBeforeText, uint32_t &sizeBeforeText, + uint64_t &addrAfterText, uint32_t &sizeAfterText, ElfSecName sec) const + { + if (sectionsInfo_.find(sec) == sectionsInfo_.end()) { + return; + } + uint64_t curSectionAddr = GetSecAddr(sec); + ASSERT(curSectionAddr != 0); + ASSERT(curSectionAddr != textAddr); + if (curSectionAddr < textAddr) { + addrBeforeText = (curSectionAddr < addrBeforeText) ? curSectionAddr : addrBeforeText; + sizeBeforeText += GetSecSize(sec); + } else { + addrAfterText = (curSectionAddr < addrAfterText) ? curSectionAddr : addrAfterText; + sizeAfterText += GetSecSize(sec); + } + } + + std::tuple GetMergedRODataAddrAndSize(uint64_t textAddr) const + { + uint64_t addrBeforeText = base::MAX_UINT64_VALUE; + uint32_t sizeBeforeText = 0; + uint64_t addrAfterText = base::MAX_UINT64_VALUE; + uint32_t sizeAfterText = 0; + for (uint8_t i = static_cast(ElfSecName::RODATA); i <= static_cast(ElfSecName::RODATA_CST32); + i++) { + UpdateRODataInfo(textAddr, addrBeforeText, sizeBeforeText, addrAfterText, sizeAfterText, + static_cast(i)); + } + return std::make_tuple(addrBeforeText, sizeBeforeText, addrAfterText, sizeAfterText); + } + void SetArkStackMapPtr(std::shared_ptr ptr) { arkStackMapPtr_ = std::move(ptr); diff --git a/ecmascript/compiler/aot_file/stub_file_info.cpp b/ecmascript/compiler/aot_file/stub_file_info.cpp index 5fabb372fccc8584664f47c02095518c0aa52c8a..dae836b24af5a52a8ea782ce4db60893d64e9cb0 100644 --- a/ecmascript/compiler/aot_file/stub_file_info.cpp +++ b/ecmascript/compiler/aot_file/stub_file_info.cpp @@ -49,7 +49,7 @@ void StubFileInfo::Save(const std::string &filename, Triple triple) ElfBuilder builder(des_, GetDumpSectionNames()); llvm::ELF::Elf64_Ehdr header; - builder.PackELFHeader(header, base::FileHeader::ToVersionNumber(AOTFileVersion::AN_VERSION), triple); + builder.PackELFHeader(header, base::FileHeaderBase::ToVersionNumber(AOTFileVersion::AN_VERSION), triple); file.write(reinterpret_cast(&header), sizeof(llvm::ELF::Elf64_Ehdr)); builder.PackELFSections(file); builder.PackELFSegment(file); diff --git a/ecmascript/compiler/argument_accessor.cpp b/ecmascript/compiler/argument_accessor.cpp index 9835e50b47c0bb43f01decef4f8e26b78ee38eb4..d1544279a42930e126b5faa71c3d00d605afe35e 100644 --- a/ecmascript/compiler/argument_accessor.cpp +++ b/ecmascript/compiler/argument_accessor.cpp @@ -156,8 +156,14 @@ GateRef ArgumentAccessor::GetFrameArgsIn(GateRef gate, FrameArgIdx idx) ASSERT(gateAcc.GetOpCode(gate) == OpCode::JS_BYTECODE || gateAcc.GetOpCode(gate) == OpCode::FRAME_STATE); GateRef frameArgs = Circuit::NullGate(); if (gateAcc.GetOpCode(gate) == OpCode::JS_BYTECODE) { - frameArgs = gateAcc.GetFrameState(gate); - ASSERT(gateAcc.GetOpCode(frameArgs) == OpCode::FRAME_ARGS); + GateRef frameState = gateAcc.GetFrameState(gate); + OpCode op = gateAcc.GetOpCode(frameState); + if (op == OpCode::FRAME_STATE) { + frameArgs = gateAcc.GetValueIn(frameState, 0); // 0: frame args + } else { + ASSERT(op == OpCode::FRAME_ARGS); + frameArgs = frameState; + } } else { frameArgs = gateAcc.GetValueIn(gate, 0); // 0: frame args } diff --git a/ecmascript/compiler/array_bounds_check_elimination.cpp b/ecmascript/compiler/array_bounds_check_elimination.cpp new file mode 100644 index 0000000000000000000000000000000000000000..34a422a27d43b8fbcb4544ba25d29fd7be41d672 --- /dev/null +++ b/ecmascript/compiler/array_bounds_check_elimination.cpp @@ -0,0 +1,943 @@ +/* + * 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/compiler/array_bounds_check_elimination.h" + +namespace panda::ecmascript::kungfu { +void ArrayBoundsCheckElimination::Run() +{ + bounds_.resize(circuit_->GetMaxGateId() + 1, nullptr); // 1: +1 for size + indexCheckInfo_.resize(circuit_->GetMaxGateId() + 1, nullptr); + graphLinearizer_.SetScheduleJSOpcode(); + graphLinearizer_.LinearizeGraph(); + + CalcBounds(graphLinearizer_.GetEntryRegion(), nullptr); + + if (IsLogEnabled()) { + LOG_COMPILER(INFO) << ""; + LOG_COMPILER(INFO) << "\033[34m" + << "====================" + << " After array bounds check elimination " + << "[" << GetMethodName() << "]" + << "====================" + << "\033[0m"; + circuit_->PrintAllGatesWithBytecode(); + LOG_COMPILER(INFO) << "\033[34m" << "========================= End ==========================" << "\033[0m"; + } +} + +/* + i_lower + c_lower <= x <= i_upper + c_upper + Initially, when nothing about the bounds is known yet, every instrution has the bounds: + MIN <= x <= MAX +*/ +ArrayBoundsCheckElimination::Bound::Bound() +{ + lower_ = INT_MIN; + upper_ = INT_MAX; + lowerGate_ = Circuit::NullGate(); + upperGate_ = Circuit::NullGate(); +} + +ArrayBoundsCheckElimination::Bound::Bound(int lower, GateRef lowerGate, int upper, GateRef upperGate) +{ + lower_ = lower; + upper_ = upper; + lowerGate_ = lowerGate; + upperGate_ = upperGate; +} + +ArrayBoundsCheckElimination::Bound::Bound(TypedBinOp op, GateRef gate, int constant) +{ + switch (op) { + case TypedBinOp::TYPED_EQ: + lower_ = constant; + lowerGate_ = gate; + upper_ = constant; + upperGate_ = gate; + break; + case TypedBinOp::TYPED_NOTEQ: + lower_ = INT_MIN; + lowerGate_ = Circuit::NullGate(); + upper_ = INT_MAX; + upperGate_ = Circuit::NullGate(); + if (gate == Circuit::NullGate()) { + if (constant == INT_MIN) { + lower_++; + } + if (constant == INT_MAX) { + upper_--; + } + } + break; + case TypedBinOp::TYPED_GREATEREQ: + lower_ = constant; + lowerGate_ = gate; + upper_ = INT_MAX; + upperGate_ = Circuit::NullGate(); + break; + case TypedBinOp::TYPED_LESSEQ: + lower_ = INT_MIN; + lowerGate_ = Circuit::NullGate(); + upper_ = constant; + upperGate_ = gate; + break; + default: + UNREACHABLE(); + } +} + +ArrayBoundsCheckElimination::Bound *ArrayBoundsCheckElimination::AndOp(Bound *bound, Bound *b) +{ + // Update lower bound + if (bound->lowerGate_ == b->lowerGate_) { + bound->lower_ = std::max(bound->lower_, b->lower_); + } + if (b->HasLower()) { + bool set = true; + if (bound->lowerGate_ != Circuit::NullGate() && b->lowerGate_ != Circuit::NullGate()) { + auto boundLowerGateRegion = graphLinearizer_.GateToRegion(bound->lowerGate_); + auto bLowerGateRegion = graphLinearizer_.GateToRegion(b->lowerGate_); + int32_t boundLowerDominatorDepth = -1; + if (boundLowerGateRegion) { + boundLowerDominatorDepth = boundLowerGateRegion->GetDepth(); + } + int32_t bLowerDominatorDepth = -1; + if (bLowerGateRegion) { + bLowerDominatorDepth = bLowerGateRegion->GetDepth(); + } + set = (boundLowerDominatorDepth > bLowerDominatorDepth); + } + if (set) { + bound->lower_ = b->lower_; + bound->lowerGate_ = b->lowerGate_; + } + } + + // Update upper bound + if (bound->upperGate_ == b->upperGate_) { + bound->upper_ = std::min(bound->upper_, b->upper_); + } + if (b->HasUpper()) { + bool set = true; + if (bound->upperGate_ != Circuit::NullGate() && b->upperGate_ != Circuit::NullGate()) { + auto boundUpperGateRegion = graphLinearizer_.GateToRegion(bound->upperGate_); + auto bUpperGateRegion = graphLinearizer_.GateToRegion(b->upperGate_); + int32_t boundUpperDominatorDepth = -1; + if (boundUpperGateRegion) { + boundUpperDominatorDepth = boundUpperGateRegion->GetDepth(); + } + int32_t bUpperDominatorDepth = -1; + if (bUpperGateRegion) { + bUpperDominatorDepth = bUpperGateRegion->GetDepth(); + } + set = (boundUpperDominatorDepth > bUpperDominatorDepth); + } + if (set) { + bound->upper_ = b->upper_; + bound->upperGate_ = b->upperGate_; + } + } + + return bound; +} + +ArrayBoundsCheckElimination::Bound *ArrayBoundsCheckElimination::OrOp(Bound *bound, Bound *b) +{ + // Update lower bound + if (bound->lowerGate_ != b->lowerGate_) { + bound->lowerGate_ = Circuit::NullGate(); + bound->lower_ = INT_MIN; + } else { + bound->lower_ = std::min(bound->lower_, b->lower_); + } + // Update upper bound + if (bound->upperGate_ != b->upperGate_) { + bound->upperGate_ = Circuit::NullGate(); + bound->upper_ = INT_MAX; + } else { + bound->upper_ = std::max(bound->upper_, b->upper_); + } + + return bound; +} + +ArrayBoundsCheckElimination::Bound *ArrayBoundsCheckElimination::DoConstant(GateRef gate) +{ + int constValue = static_cast(acc_.GetConstantValue(gate)); + return new Bound(constValue, Circuit::NullGate(), constValue, Circuit::NullGate()); +} + +ArrayBoundsCheckElimination::Bound *ArrayBoundsCheckElimination::DoArithmeticOp(GateRef gate) +{ + auto op = acc_.GetTypedBinaryOp(gate); + auto x = acc_.GetValueIn(gate, 0); + auto y = acc_.GetValueIn(gate, 1); + if (!acc_.IsConstant(x) || !acc_.IsConstant(y)) { // One of the operands must be non-constant! + if (op == TypedBinOp::TYPED_AND && (acc_.IsConstant(x) || acc_.IsConstant(y))) { + int constValue = 0; + if (acc_.IsConstant(x)) { + constValue = static_cast(acc_.GetConstantValue(x)); + } else { + constValue = static_cast(acc_.GetConstantValue(y)); + } + if (constValue >= 0) { + return new Bound(0, Circuit::NullGate(), constValue, Circuit::NullGate()); + } + } else if (op == TypedBinOp::TYPED_MOD) { + Bound *xBound = GetBound(x); + if (xBound->Lower() >= 0 && xBound->LowerGate() == Circuit::NullGate() && IsArrayLength(y)) { + return new Bound(0, Circuit::NullGate(), -1, y); + } else if (xBound->HasLower() && xBound->Lower() >= 0 && acc_.IsConstant(y) + && acc_.GetConstantValue(y) != 0) { + int constValue = static_cast(acc_.GetConstantValue(y)); + if (constValue != INT_MIN) { + return new Bound(0, Circuit::NullGate(), abs(constValue) - 1, Circuit::NullGate()); + } else { + return new Bound(); + } + } else { + return new Bound(); + } + } else if (((acc_.IsConstant(x) || acc_.IsConstant(y)) && op == TypedBinOp::TYPED_ADD) || + (acc_.IsConstant(y) && op == TypedBinOp::TYPED_SUB)) { + // x is constant, y is variable. + if (acc_.IsConstant(y)) { + std::swap(x, y); + } + + // Add, Constant now in x + int constValue = static_cast(acc_.GetConstantValue(x)); + if (op == TypedBinOp::TYPED_SUB) { + constValue = -constValue; + } + + Bound *bound = GetBound(y); + if (!bound->HasUpper() || !bound->HasLower()) { + return new Bound(); + } + + int lower = bound->Lower(); + int upper = bound->Upper(); + int newLower = lower + constValue; + int newUpper = upper + constValue; + bool overflow = ((constValue < 0 && (newLower > lower)) || + (constValue > 0 && (newUpper < upper))); + if (overflow) { + return new Bound(); + } else { + return new Bound(newLower, bound->LowerGate(), newUpper, bound->UpperGate()); + } + } else if (op == TypedBinOp::TYPED_SUB) { + Bound *bound = GetBound(x); + if (bound->LowerGate() == y) { + return new Bound(TypedBinOp::TYPED_GREATEREQ, Circuit::NullGate(), bound->Lower()); + } else { + return new Bound(); + } + } else { + return new Bound(); + } + } + return nullptr; +} + +bool ArrayBoundsCheckElimination::InLoop(GateRef loopHeader, GateRef gate) +{ + while (gate != acc_.GetStateRoot()) { + if (gate == loopHeader) { + return true; + } else { + gate = acc_.GetState(gate, 0); + } + } + return false; +} + +/* +Do phi +*/ +ArrayBoundsCheckElimination::Bound *ArrayBoundsCheckElimination::DoPhi(GateRef gate) +{ + Bound *bound = nullptr; + size_t valueSize = acc_.GetInValueCount(gate); + GateRef stateIn = acc_.GetState(gate); + bool isLoopHead = acc_.IsLoopHead(stateIn); + bool hasUpper = true; + bool hasLower = true; + for (size_t i = 0; i < valueSize; i++) { + GateRef value = acc_.GetValueIn(gate, i); + // Check if instruction is connected with phi itself + if (isLoopHead && acc_.GetOpCode(value) == OpCode::TYPED_UNARY_OP + && InLoop(stateIn, value)) { + auto unOp = acc_.GetTypedUnAccessor(value).GetTypedUnOp(); + switch (unOp) { + case TypedUnOp::TYPED_INC: + hasUpper = false; + break; + case TypedUnOp::TYPED_DEC: + hasLower = false; + break; + default: + break; + } + continue; + } + + Bound *vBound = GetBound(value); + Bound *curBound; + GateRef curGate; + int curConstant; + GetInstrAndConstValueFromOp(value, curGate, curConstant); + if (!vBound->HasUpper() || !vBound->HasLower()) { + curBound = new Bound(curConstant, curGate, curConstant, curGate); + } else { + curBound = vBound; + } + + if (curBound) { + if (!bound) { + bound = curBound->Copy(); + } else { + bound = OrOp(bound, curBound); + } + } else { + bound = new Bound(); + break; + } + } + + if (!hasUpper) { + bound->RemoveUpper(); + } + if (!hasLower) { + bound->RemoveLower(); + } + return bound; +} + +ArrayBoundsCheckElimination::Bound *ArrayBoundsCheckElimination::VisitGate(GateRef gate) +{ + OpCode op = acc_.GetOpCode(gate); + switch (op) { + case OpCode::CONSTANT: + return DoConstant(gate); + case OpCode::TYPED_BINARY_OP: + return DoArithmeticOp(gate); + case OpCode::VALUE_SELECTOR: + return DoPhi(gate); + default: + return nullptr; + } + return nullptr; +} + +// y = a + b - c ..... +void ArrayBoundsCheckElimination::GetInstrAndConstValueFromOp(GateRef gate, GateRef& instrValue, int& constValue) +{ + int base = 0; + constValue = 0; + instrValue = gate; + if (acc_.IsConstant(gate)) { + constValue = static_cast(acc_.GetConstantValue(gate)); + instrValue = Circuit::NullGate(); + } else { + while (acc_.GetOpCode(gate) == OpCode::TYPED_BINARY_OP) { + auto op = acc_.GetTypedBinaryOp(gate); + auto x = acc_.GetValueIn(gate, 0); + auto y = acc_.GetValueIn(gate, 1); + GateRef other = x; + if ((op == TypedBinOp::TYPED_ADD && (acc_.IsConstant(x) || acc_.IsConstant(y))) + || (op == TypedBinOp::TYPED_SUB && acc_.IsConstant(y))) { + int value = 0; + if (acc_.IsConstant(x)) { + value = static_cast(acc_.GetConstantValue(x)); + other = y; + } else { + value = static_cast(acc_.GetConstantValue(y)); + other = x; + } + + while (acc_.GetOpCode(other) == OpCode::INDEX_CHECK) { // Get IndexCheck Index + other = acc_.GetValueIn(other, 1); + } + + if (op == TypedBinOp::TYPED_SUB) { + value = -value; + } + + if (acc_.IsConstant(other)) { + base += value + static_cast(acc_.GetConstantValue(other)); + constValue = base; + instrValue = Circuit::NullGate(); + break ; + } else { + base += value; + constValue = base; + instrValue = other; + gate = other; + } + } else { + break; + } + } + } +} + +ArrayBoundsCheckElimination::Bound *ArrayBoundsCheckElimination::GetBound(GateRef gate) +{ + if (gate == Circuit::NullGate()) { + return nullptr; + } + if (!bounds_[acc_.GetId(gate)]) { + bounds_[acc_.GetId(gate)] = new BoundStack(chunk_); + Bound *bound = VisitGate(gate); + if (bound) { + bounds_[acc_.GetId(gate)]->push_back(bound); + } + if (bounds_[acc_.GetId(gate)]->size() == 0) { + bounds_[acc_.GetId(gate)]->push_back(new Bound()); + } + } else if (bounds_[acc_.GetId(gate)]->size() == 0) { + return new Bound(); + } + return bounds_[acc_.GetId(gate)]->back(); +} + +void ArrayBoundsCheckElimination::UpdateBound(IntegerStack &pushed, GateRef gate, Bound *bound) +{ + if (acc_.IsConstant(gate)) { + // No bound update for constants + return; + } + if (!bounds_[acc_.GetId(gate)]) { + GetBound(gate); + } + Bound* top = nullptr; + if (bounds_[acc_.GetId(gate)]->size() > 0) { + top = bounds_[acc_.GetId(gate)]->back(); + } + if (top) { + bound = AndOp(bound, top); + } + bounds_[acc_.GetId(gate)]->push_back(bound); + pushed.push_back(acc_.GetId(gate)); +} + +/* +x op y + constValue +for example: + x >= Circuit::NullGate() + 0 + x < Length + 0 +*/ +void ArrayBoundsCheckElimination::UpdateBound(IntegerStack &pushed, GateRef x, TypedBinOp op, + GateRef instrValue, int constValue) +{ + if (op == TypedBinOp::TYPED_GREATER) { // x < 3 -> x <= 4 + op = TypedBinOp::TYPED_GREATEREQ; + // Cannot Represent c > INT_MAX, do not update bounds + if (constValue == INT_MAX && instrValue == Circuit::NullGate()) { + return; + } else { + constValue++; + } + } else if (op == TypedBinOp::TYPED_LESS) { // x > 3 -> x >= 2 + op = TypedBinOp::TYPED_LESSEQ; + // Cannot Represent c < INT_MIN, do not update bounds + if (constValue == INT_MIN && instrValue == Circuit::NullGate()) { + return; + } else { + constValue--; + } + } + Bound *bound = new Bound(op, instrValue, constValue); + UpdateBound(pushed, x, bound); +} + +// Add if condition when x is a variable, x op y +void ArrayBoundsCheckElimination::AddIfCondition(IntegerStack &pushed, GateRef x, GateRef y, TypedBinOp op) +{ + if (acc_.IsConstant(x)) { // x must be non-constant! + return; + } + int constValue; + GateRef instrValue; + GetInstrAndConstValueFromOp(y, instrValue, constValue); + UpdateBound(pushed, x, op, instrValue, constValue); +} + +bool ArrayBoundsCheckElimination::IsArrayLength(GateRef gate) +{ + if (gate == Circuit::NullGate()) { + return false; + } + OpCode op = acc_.GetOpCode(gate); + switch (op) { + case OpCode::LOAD_ARRAY_LENGTH: + case OpCode::LOAD_TYPED_ARRAY_LENGTH: + return true; + default: + return false; + } + UNREACHABLE(); + return false; +} + +bool ArrayBoundsCheckElimination::InArrayBound(Bound *bound, GateRef length, GateRef array) +{ + if (!bound || array == Circuit::NullGate()) { + return false; + } + + if (bound->Lower() >= 0 && bound->LowerGate() == Circuit::NullGate() && + bound->Upper() < 0 && bound->UpperGate() != Circuit::NullGate()) { + if (length != Circuit::NullGate() && bound->UpperGate() == length) { + return true; + } + } + + return false; +} + +void ArrayBoundsCheckElimination::RemoveIndexCheck(GateRef gate) +{ + ASSERT(acc_.GetDependCount(gate) == 1); + ASSERT(acc_.GetStateCount(gate) == 1); + ASSERT(acc_.GetInValueCount(gate) == 2); // 2: ValueCount + + GateRef depend = acc_.GetDep(gate); + GateRef state = acc_.GetState(gate); + GateRef value = acc_.GetValueIn(gate, 1); // Index + + acc_.ReplaceGate(gate, state, depend, value); +} + +bool ArrayBoundsCheckElimination::CheckLoop(GateRef array, GateRef lowerGate, int lower, GateRef upperGate, int upper) +{ + if (IsArrayLength(upperGate) && acc_.GetValueIn(upperGate, 0) == array) { + if (upper >= 0) { + return false; + } + } + if (IsArrayLength(lowerGate) && acc_.GetValueIn(lowerGate, 0) == array) { + if (lower >= 0) { + return false; + } + } + return true; +} + +bool ArrayBoundsCheckElimination::LoopInvariant(GateRegion *loopHeader, GateRef gate) +{ + if (gate == Circuit::NullGate()) { + return true; + } + auto gateRegion = graphLinearizer_.GateToRegion(gate); + if (!gateRegion) { + return true; + } + GateRegion* g = loopHeader->GetDominator(); + while (g != nullptr) { + if (g == gateRegion) { + return true; + } + if (g == g->GetDominator()) { // entry + break ; + } + g = g->GetDominator(); + } + return false; +} + +GateRef ArrayBoundsCheckElimination::Predicate(GateRef left, TypedBinOp cond, GateRef right) +{ + return builder_.InsertRangeCheckPredicate(left, cond, right); +} + +GateRef ArrayBoundsCheckElimination::PredicateCmpWithConst(GateRef left, TypedBinOp cond, int32_t right) +{ + GateRef constGate = builder_.Int32(right); + return Predicate(left, cond, constGate); +} + +GateRef ArrayBoundsCheckElimination::PredicateAdd(GateRef left, int32_t leftConst, TypedBinOp cond, GateRef right) +{ + GateRef constGate = builder_.Int32(leftConst); + GateRef binaryOpGate = builder_.InsertTypedBinaryop(left, constGate, GateType::NumberType(), + GateType::NumberType(), GateType::AnyType(), + PGOSampleType::NoneType(), TypedBinOp::TYPED_ADD); + return Predicate(binaryOpGate, cond, right); +} + +GateRef ArrayBoundsCheckElimination::PredicateAddCmpWithConst(GateRef left, int32_t leftConst, + TypedBinOp cond, int32_t right) +{ + GateRef constGate = builder_.Int32(right); + return PredicateAdd(left, leftConst, cond, constGate); +} + +void ArrayBoundsCheckElimination::LoopInvariantMotionForIndexCheck(GateRef array, GateRef length, + GateRef lowerGate, int lower, + GateRef upperGate, int upper, + bool isTypedArray) +{ + // lower > 0 + if (lowerGate != Circuit::NullGate()) { + if (lower == 0) { + // lowerGate >= 0 + PredicateCmpWithConst(lowerGate, TypedBinOp::TYPED_GREATEREQ, 0); + } else if (lower > 0) { + // lowerGate + lower >= 0 + PredicateAddCmpWithConst(lowerGate, lower, TypedBinOp::TYPED_GREATEREQ, 0); + } else { + // lowerGate + lower < 0 + // lower < 0 + // lowerGate < -lower + lower++; + lower = -lower; + PredicateCmpWithConst(lowerGate, TypedBinOp::TYPED_GREATER, lower); + } + } + + // LOAD LENGTH if necessary + if (length == Circuit::NullGate()) { + length = builder_.InsertLoadArrayLength(array, isTypedArray); + } + + if (upperGate == Circuit::NullGate()) { + ASSERT(upper >= 0); + PredicateCmpWithConst(length, TypedBinOp::TYPED_GREATER, upper); + } else { + if (upper == 0) { + Predicate(upperGate, TypedBinOp::TYPED_LESS, length); + } else if (upper > 0) { + // upperGate + upper < length + PredicateAdd(upperGate, upper, TypedBinOp::TYPED_LESS, length); + } else { + // upperGate + upper < length + // upper < 0 + // upperGate < length + (-upper) + PredicateAdd(length, -upper, TypedBinOp::TYPED_GREATER, upperGate); + } + } +} + +void ArrayBoundsCheckElimination::ProcessIndexCheck(GateRegion *loopHeader, GateRef gate) +{ + auto length = acc_.GetValueIn(gate, 0); + auto array = acc_.GetValueIn(length, 0); + auto index = acc_.GetValueIn(gate, 1); + Bound *indexBound = GetBound(index); + if (!indexBound->HasLower() || !indexBound->HasUpper()) { + return; + } + + if (InArrayBound(indexBound, length, array)) { + RemoveIndexCheck(gate); + } else if (loopHeader) { + if (!LoopInvariant(loopHeader, array) + || !LoopInvariant(loopHeader, indexBound->LowerGate()) + || !LoopInvariant(loopHeader, indexBound->UpperGate()) + || (indexBound->LowerGate() == Circuit::NullGate() && indexBound->Lower() < 0) + || (indexBound->UpperGate() == Circuit::NullGate() && indexBound->Upper() < 0)) { + return; + } + + ASSERT(length != Circuit::NullGate()); + bool isTypedArray = false; + if (acc_.GetOpCode(length) == OpCode::LOAD_TYPED_ARRAY_LENGTH) { + isTypedArray = true; + } + + // Length instrution + if (!LoopInvariant(loopHeader, length)) { + // Generate length instruction yourself + length = Circuit::NullGate(); + } + + // Insert Before loopHeader State, and if find IF_TRUE and IF_FALSE, insert after the DEPEND_RELAY + // if find MERGE, insert after DEPEND_SELECTOR + GateRef insertAfter = acc_.GetState(loopHeader->GetState(), 0); // after end + GateRef stateIn = insertAfter; + GateRef dependIn = insertAfter; + acc_.GetStateInAndDependIn(insertAfter, stateIn, dependIn); + + if (!CheckLoop(array, indexBound->LowerGate(), indexBound->Lower(), + indexBound->UpperGate(), indexBound->Upper())) { + return; + } + + Environment env(stateIn, dependIn, {}, circuit_, &builder_); + LoopInvariantMotionForIndexCheck(array, length, indexBound->LowerGate(), indexBound->Lower(), + indexBound->UpperGate(), indexBound->Upper(), isTypedArray); + RemoveIndexCheck(gate); + } +} + +void ArrayBoundsCheckElimination::ProcessIf(IntegerStack &pushed, GateRegion *parent, OpCode cond) +{ + auto& gateLists = parent->GetGates(); + for (int i = static_cast(gateLists.size()) - 1; i >= 0; i--) { // Found the last BinaryOp + GateRef gate = gateLists[i]; + if (gate == Circuit::NullGate()) continue; + OpCode opGate = acc_.GetOpCode(gate); + if (opGate != OpCode::TYPED_BINARY_OP) { + continue ; + } + + TypedBinOp op = acc_.GetTypedBinaryOp(gate); + GateRef x = acc_.GetValueIn(gate, 0); + GateRef y = acc_.GetValueIn(gate, 1); + + switch (op) { + case TypedBinOp::TYPED_LESS: + case TypedBinOp::TYPED_LESSEQ: + case TypedBinOp::TYPED_GREATER: + case TypedBinOp::TYPED_GREATEREQ: + case TypedBinOp::TYPED_EQ: + case TypedBinOp::TYPED_NOTEQ: + if (cond == OpCode::IF_TRUE) { + op = TypedBinaryMetaData::GetRevCompareOp(op); + } + AddIfCondition(pushed, x, y, op); + AddIfCondition(pushed, y, x, TypedBinaryMetaData::GetSwapCompareOp(op)); + break; + default: + break; + } + break; + } +} + +bool ArrayBoundsCheckElimination::Contain(GateLists &gateLists, GateRef gate) +{ + for (size_t i = 0; i < gateLists.size(); i++) { + if (gateLists[i] == gate) { + return true; + } + } + return false; +} + +void ArrayBoundsCheckElimination::AddAccessIndexedInfo(GateLists &indices, GateRef gate, int idx, GateRef indexCheck) +{ + IndexCheckInfo *indexCheckInfo = indexCheckInfo_[acc_.GetId(gate)]; + if (indexCheckInfo == nullptr) { + indexCheckInfo = new IndexCheckInfo(chunk_); + indexCheckInfo_[acc_.GetId(gate)] = indexCheckInfo; + indices.push_back(gate); + indexCheckInfo->min_ = idx; + indexCheckInfo->max_ = idx; + } else if (idx >= indexCheckInfo->min_ && idx <= indexCheckInfo->max_) { + RemoveIndexCheck(indexCheck); + return; + } + indexCheckInfo->min_ = std::min(indexCheckInfo->min_, idx); + indexCheckInfo->max_ = std::max(indexCheckInfo->max_, idx); + indexCheckInfo->list_.push_back(indexCheck); +} + +void ArrayBoundsCheckElimination::InBlockMotion(GateLists &indexChecked, GateLists &arrays) +{ + GateLists indices(chunk_); + for (size_t i = 0; i < arrays.size(); i++) { + int maxConstant = -1; + GateLists listConstant(chunk_); + GateRef arrayGate = arrays[i]; + for (size_t j = 0; j < indexChecked.size(); j++) { + GateRef indexCheck = indexChecked[j]; + // INDEX_CHECK may be dead + if (acc_.GetOpCode(indexCheck) != OpCode::INDEX_CHECK) { + continue; + } + GateRef length = acc_.GetValueIn(indexCheck, 0); + GateRef index = acc_.GetValueIn(indexCheck, 1); + GateRef array = acc_.GetValueIn(length, 0); + if (array != arrayGate) { + continue; + } + if (acc_.IsConstant(index)) { + int constValue = static_cast(acc_.GetConstantValue(index)); + if (constValue >= 0 && constValue <= maxConstant) { + RemoveIndexCheck(indexCheck); + } else if (constValue >= 0 && constValue > maxConstant) { + maxConstant = constValue; + listConstant.push_back(indexCheck); + } + } else { + int lastInteger; + GateRef lastGate; + GetInstrAndConstValueFromOp(index, lastGate, lastInteger); + if (lastInteger >= 0 && lastGate == Circuit::NullGate()) { // IsConstant + if (lastInteger <= maxConstant) { + RemoveIndexCheck(indexCheck); + } else { + maxConstant = lastInteger; + listConstant.push_back(indexCheck); + } + } else if (lastGate != Circuit::NullGate()) { + AddAccessIndexedInfo(indices, lastGate, lastInteger, indexCheck); + } // when lastInteger < 0, dont remove IndexCheck + } + } + + // Iterate over all different indices + for (size_t j = 0; j < indices.size(); j++) { + GateRef index = indices[j]; + + IndexCheckInfo *info = indexCheckInfo_[acc_.GetId(index)]; + ASSERT(info != nullptr); + + // maybe index < 0, max > 0 + // max + index in [0, a.length) + // min + index overflow !!!, min + index > 0 + // so, min + index >= INT_MIN, min >= INT_MIN - index + // max in [-index, a.length - index) + // min >= INT_MIN + max + bool rangeCond = (info->max_ < 0 || info->max_ + INT_MIN <= info->min_); + if (info->list_.size() > 2 && rangeCond) { // 2: size + GateRef insertAfter = info->list_.front(); + GateRef length = acc_.GetValueIn(insertAfter, 0); + ASSERT(length != Circuit::NullGate()); + + Environment env(insertAfter, circuit_, &builder_); + + // Calculate lower bound + GateRef lowerCompare = index; + if (info->min_ > 0) { + GateRef minGate = builder_.Int32(info->min_); + lowerCompare = builder_.InsertTypedBinaryop(lowerCompare, minGate, + GateType::NumberType(), GateType::NumberType(), + GateType::AnyType(), PGOSampleType::NoneType(), + TypedBinOp::TYPED_ADD); + } else if (info->min_ < 0) { + GateRef minGate = builder_.Int32(-info->min_); + lowerCompare = builder_.InsertTypedBinaryop(lowerCompare, minGate, + GateType::NumberType(), GateType::NumberType(), + GateType::AnyType(), PGOSampleType::NoneType(), + TypedBinOp::TYPED_SUB); + } + + PredicateCmpWithConst(lowerCompare, TypedBinOp::TYPED_GREATEREQ, 0); + + // Calculate upper bound + GateRef upperCompare = index; + if (info->max_ != 0) { + if (info->max_ > 0) { + GateRef maxGate = builder_.Int32(info->max_); + upperCompare = builder_.InsertTypedBinaryop(upperCompare, maxGate, + GateType::NumberType(), GateType::NumberType(), + GateType::AnyType(), PGOSampleType::NoneType(), + TypedBinOp::TYPED_ADD); + } else if (info->max_ < 0) { + GateRef maxGate = builder_.Int32(-info->max_); + upperCompare = builder_.InsertTypedBinaryop(upperCompare, maxGate, + GateType::NumberType(), GateType::NumberType(), + GateType::AnyType(), PGOSampleType::NoneType(), + TypedBinOp::TYPED_SUB); + } + } + + Predicate(upperCompare, TypedBinOp::TYPED_LESS, length); + for (auto& indexCheck: (info->list_)) { + RemoveIndexCheck(indexCheck); + } + } + } + + // index only constant + if (listConstant.size() > 1) { + GateRef firIndexCheckGate = listConstant.front(); + Environment env(firIndexCheckGate, circuit_, &builder_); + GateRef length = acc_.GetValueIn(firIndexCheckGate, 0); + ASSERT(length != Circuit::NullGate()); + ASSERT(maxConstant >= 0); + PredicateCmpWithConst(length, TypedBinOp::TYPED_GREATER, maxConstant); // length > index + for (size_t j = 0; j < listConstant.size(); j++) { + GateRef indexCheck = listConstant[j]; + RemoveIndexCheck(indexCheck); + } + } + + for (size_t j = 0; j < indices.size(); j++) { + indexCheckInfo_[acc_.GetId(indices[j])] = nullptr; + } + indices.clear(); + } +} + +void ArrayBoundsCheckElimination::CalcBounds(GateRegion *block, GateRegion *loopHeader) +{ + // Pushed stack for condition + IntegerStack pushed(chunk_); + + // Process If + GateRegion *parent = block->GetDominator(); + if (parent != nullptr) { + auto gate = block->GetGates().front(); + auto op = acc_.GetOpCode(gate); + if (op == OpCode::IF_TRUE || op == OpCode::IF_FALSE) { // Recognize If (including the condition in forloop) + ProcessIf(pushed, parent, op); + } + } + + GateLists indexChecked(chunk_); + GateLists arrays(chunk_); + + auto& gateList_ = block->GetGates(); + for (size_t i = 0; i < gateList_.size(); i++) { // Visit GateUnion + GateRef gate = gateList_[i]; + auto op = acc_.GetOpCode(gate); + if (op == OpCode::INDEX_CHECK) { + auto length = acc_.GetValueIn(gate, 0); + auto index = acc_.GetValueIn(gate, 1); + auto array = acc_.GetValueIn(length, 0); + + ProcessIndexCheck(loopHeader, gate); + indexChecked.push_back(gate); + + if (!Contain(arrays, array)) { + arrays.push_back(array); + } + + // Give IndexCheck a bound [0, Length - 1] + Bound *b = GetBound(index); + if (b->LowerGate() == Circuit::NullGate()) { // LowerBound is the Constant !!! + UpdateBound(pushed, index, TypedBinOp::TYPED_GREATEREQ, Circuit::NullGate(), 0); + } + if (!b->HasUpper() && length != Circuit::NullGate()) { // default dont know the Length + UpdateBound(pushed, index, TypedBinOp::TYPED_LESS, length, 0); + } + } + } + + InBlockMotion(indexChecked, arrays); + + auto& dominatedRegions_ = block->GetDominatedRegions(); + for (size_t i = 0; i < dominatedRegions_.size(); i++) { + GateRegion *nex = dominatedRegions_[i]; + if (block->IsLoopHead() && (block->GetInnerLoopIndex() == nex->GetInnerLoopIndex() + || nex->GetLoopDepth() > block->GetLoopDepth())) { + CalcBounds(nex, block); + } else { + CalcBounds(nex, loopHeader); + } + } + + for (size_t i = 0; i < pushed.size(); i++) { + bounds_[pushed[i]]->pop_back(); + } +} +} \ No newline at end of file diff --git a/ecmascript/compiler/array_bounds_check_elimination.h b/ecmascript/compiler/array_bounds_check_elimination.h new file mode 100644 index 0000000000000000000000000000000000000000..54b0c5a50d8f7bd7c85c8c8c8c8c22ffaff0f848 --- /dev/null +++ b/ecmascript/compiler/array_bounds_check_elimination.h @@ -0,0 +1,169 @@ +/* + * 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_COMPILER_ARRAY_BOUNDS_CHECK_ELIMINATION_H +#define ECMASCRIPT_COMPILER_ARRAY_BOUNDS_CHECK_ELIMINATION_H + +#include "ecmascript/compiler/circuit_builder.h" +#include "ecmascript/compiler/mcr_gate_meta_data.h" +#include "ecmascript/compiler/gate_accessor.h" +#include "ecmascript/compiler/graph_linearizer.h" +#include "ecmascript/compiler/pass_manager.h" +#include "ecmascript/mem/chunk_containers.h" + +namespace panda::ecmascript::kungfu { +class ArrayBoundsCheckElimination { +public: + ArrayBoundsCheckElimination(Circuit *circuit, bool enableLog, const std::string& name, Chunk* chunk) + : acc_(circuit), bounds_(chunk), circuit_(circuit), builder_(circuit), chunk_(chunk), enableLog_(enableLog), + graphLinearizer_(circuit, enableLog, name, chunk, true, true), methodName_(name), indexCheckInfo_(chunk) {} + + ~ArrayBoundsCheckElimination() = default; + void Run(); + +private: + class Bound { + public: + Bound(); + Bound(GateRef v); + Bound(int lower, GateRef lowerGate, int upper, GateRef upperGate); + Bound(TypedBinOp op, GateRef gate, int constant); + ~Bound(){}; + int Upper() + { + return upper_; + } + GateRef UpperGate() + { + return upperGate_; + } + int Lower() + { + return lower_; + } + GateRef LowerGate() + { + return lowerGate_; + } + bool HasUpper() + { + return upperGate_ != Circuit::NullGate() || upper_ < INT_MAX; + } + bool HasLower() + { + return lowerGate_ != Circuit::NullGate() || lower_ > INT_MIN; + } + void RemoveUpper() + { + upperGate_ = Circuit::NullGate(); + upper_ = INT_MAX; + } + void RemoveLower() + { + lowerGate_ = Circuit::NullGate(); + lower_ = INT_MIN; + } + bool IsSmaller(Bound *b) + { + if (b->LowerGate() != upperGate_) { + return false; + } + return upper_ < b->Lower(); + } + Bound* Copy() + { + return new Bound(lower_, lowerGate_, upper_, upperGate_); + } + + private: + int upper_; + GateRef upperGate_; + int lower_; + GateRef lowerGate_; + + friend ArrayBoundsCheckElimination; + }; + + bool IsLogEnabled() const + { + return enableLog_; + } + + const std::string& GetMethodName() const + { + return methodName_; + } + + typedef ChunkVector BoundStack; + typedef ChunkVector BoundMap; + typedef ChunkVector IntegerStack; + typedef ChunkVector GateLists; + + void AddAccessIndexedInfo(GateLists &indices, GateRef gate, int idx, GateRef indexCheck); + void AddIfCondition(IntegerStack &pushed, GateRef x, GateRef y, TypedBinOp op); + Bound *AndOp(Bound *bound, Bound *b); + Bound *OrOp(Bound *bound, Bound *b); + bool Contain(GateLists& gateLists, GateRef gate); + void CalcBounds(GateRegion *block, GateRegion *loopHeader); + bool CheckLoop(GateRef array, GateRef lowerGate, int lower, GateRef upperGate, int upper); + void InBlockMotion(GateLists &indexChecked, GateLists &arrays); + bool InLoop(GateRef loopHeader, GateRef gate); + bool IsArrayLength(GateRef gate); + bool LoopInvariant(GateRegion *loopHeader, GateRef gate); + void UpdateBound(IntegerStack &pushed, GateRef gate, Bound *bound); + void UpdateBound(IntegerStack &pushed, GateRef x, TypedBinOp op, GateRef y, int constValue); + void ProcessIndexCheck(GateRegion *loopHeader, GateRef gate); + void RemoveIndexCheck(GateRef gate); + void CopyStateInAndDependIn(GateRef &stateIn, GateRef &dependIn, GateRef insertAfter); + void LoopInvariantMotionForIndexCheck(GateRef array, GateRef length, GateRef lowerGate, int lower, + GateRef upperGate, int upper, bool isTypedArray); + void GetInstrAndConstValueFromOp(GateRef gate, GateRef &instrValue, int& constValue); + Bound *GetBound(GateRef gate); + Bound *DoConstant(GateRef gate); + Bound *DoArithmeticOp(GateRef gate); + Bound *DoPhi(GateRef gate); + void SetBound(GateRef gate, Bound *bound); + void ProcessIf(IntegerStack &pushed, GateRegion *parent, OpCode cond); + bool InArrayBound(Bound *bound, GateRef length, GateRef array); + Bound *VisitGate(GateRef gate); + + void ReplaceIn(GateRef stateIn, GateRef dependIn, GateRef newGate); + + GateRef Predicate(GateRef left, TypedBinOp cond, GateRef right); + GateRef PredicateCmpWithConst(GateRef left, TypedBinOp cond, int right); + GateRef PredicateAdd(GateRef left, int leftConst, TypedBinOp cond, GateRef right); + GateRef PredicateAddCmpWithConst(GateRef left, int leftConst, TypedBinOp cond, int right); + + GateAccessor acc_; + BoundMap bounds_; + Circuit *circuit_ {nullptr}; + CircuitBuilder builder_; + Chunk *chunk_ {nullptr}; + bool enableLog_ {false}; + GraphLinearizer graphLinearizer_; + std::string methodName_; + + class IndexCheckInfo { + public: + IndexCheckInfo(Chunk* chunk): list_(chunk) {} + GateLists list_; + int min_; + int max_; + }; + typedef ChunkVector IndexCheckInfoList; + IndexCheckInfoList indexCheckInfo_; +}; +} +#endif \ No newline at end of file diff --git a/ecmascript/compiler/assembler/tests/assembler_aarch64_test.cpp b/ecmascript/compiler/assembler/tests/assembler_aarch64_test.cpp index 05bb40ffe5de18d2726e4479d544b34513ba5434..1a08bbbc98f4b3a09897ecc392f7da21d9cee3ee 100644 --- a/ecmascript/compiler/assembler/tests/assembler_aarch64_test.cpp +++ b/ecmascript/compiler/assembler/tests/assembler_aarch64_test.cpp @@ -80,13 +80,6 @@ public: LLVMInitializeAArch64AsmPrinter(); LLVMInitializeAArch64AsmParser(); LLVMInitializeAArch64Target(); - } else if (triple.compare(TARGET_ARM32) == 0) { - LLVMInitializeARMTargetInfo(); - LLVMInitializeARMTargetMC(); - LLVMInitializeARMDisassembler(); - LLVMInitializeARMAsmPrinter(); - LLVMInitializeARMAsmParser(); - LLVMInitializeARMTarget(); } else { LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); diff --git a/ecmascript/compiler/assembler/tests/assembler_x64_test.cpp b/ecmascript/compiler/assembler/tests/assembler_x64_test.cpp index 5afa7050f2dde919138b84e5287b99ac4f7b1174..56820bc6a739dbbaeb8b840adae24a12c4dca15e 100644 --- a/ecmascript/compiler/assembler/tests/assembler_x64_test.cpp +++ b/ecmascript/compiler/assembler/tests/assembler_x64_test.cpp @@ -85,13 +85,6 @@ public: LLVMInitializeAArch64AsmPrinter(); LLVMInitializeAArch64AsmParser(); LLVMInitializeAArch64Target(); - } else if (triple.compare(TARGET_ARM32) == 0) { - LLVMInitializeARMTargetInfo(); - LLVMInitializeARMTargetMC(); - LLVMInitializeARMDisassembler(); - LLVMInitializeARMAsmPrinter(); - LLVMInitializeARMAsmParser(); - LLVMInitializeARMTarget(); } else { LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); diff --git a/ecmascript/compiler/async_function_lowering.cpp b/ecmascript/compiler/async_function_lowering.cpp index 647a0b47a87e46f771ea5a46f2883198896ee943..6b3a5db6ae0fe8fb35ce93cb6e8090c7f7e4ad9e 100644 --- a/ecmascript/compiler/async_function_lowering.cpp +++ b/ecmascript/compiler/async_function_lowering.cpp @@ -41,7 +41,7 @@ void AsyncFunctionLowering::ProcessJumpTable() GateRef ifBranchCondition = builder_.Branch(stateEntry_, isEqual); GateRef ifTrueCondition = builder_.IfTrue(ifBranchCondition); GateRef ifFalseCondition = builder_.IfFalse(ifBranchCondition); - if (accessor_.GetOpCode(*firstUse) == OpCode::STATE_SPLIT) { + while (accessor_.GetOpCode(*firstUse) == OpCode::STATE_SPLIT) { firstUse++; } accessor_.ReplaceStateIn(*firstUse, ifTrueCondition); @@ -81,10 +81,9 @@ void AsyncFunctionLowering::RebuildGeneratorCfg(GateRef resumeGate, GateRef rest GateRef loopBeginStateIn = Circuit::NullGate(); GateRef prevBcOffsetPhiGate = Circuit::NullGate(); while (true) { - auto opcode = accessor_.GetOpCode(stateInGate); - if (opcode == OpCode::STATE_ENTRY) { + if (stateInGate == GetEntryBBStateOut()) { // from state entry GateRef condition = builder_.Equal(offsetConstantGate, restoreOffsetGate); - GateRef ifBranch = circuit_->NewGate(circuit_->IfBranch(), { ifFalseCondition, condition }); + GateRef ifBranch = circuit_->NewGate(circuit_->IfBranch(0), { ifFalseCondition, condition }); GateRef ifTrue = circuit_->NewGate(circuit_->IfTrue(), {ifBranch}); GateRef ifFalse = circuit_->NewGate(circuit_->IfFalse(), {ifBranch}); GateRef ifTrueDepend = builder_.DependRelay(ifTrue, restoreOffsetGate); @@ -110,7 +109,7 @@ void AsyncFunctionLowering::RebuildGeneratorCfg(GateRef resumeGate, GateRef rest } firstState = ifBranch; } - + auto opcode = accessor_.GetOpCode(stateInGate); if (opcode == OpCode::LOOP_BEGIN) { bool resumeInLoopBody = false; CheckResumeInLoopBody(stateInGate, resumeInLoopBody); @@ -125,7 +124,7 @@ void AsyncFunctionLowering::RebuildGeneratorCfg(GateRef resumeGate, GateRef rest GateType::NJSValue()); GateRef condition = builder_.Equal(offsetConstantGate, bcOffsetPhiGate); - GateRef ifBranch = circuit_->NewGate(circuit_->IfBranch(), {stateInGate, condition}); + GateRef ifBranch = circuit_->NewGate(circuit_->IfBranch(0), {stateInGate, condition}); GateRef ifTrue = circuit_->NewGate(circuit_->IfTrue(), {ifBranch}); GateRef ifFalse = circuit_->NewGate(circuit_->IfFalse(), {ifBranch}); @@ -158,7 +157,7 @@ void AsyncFunctionLowering::RebuildGeneratorCfg(GateRef resumeGate, GateRef rest UpdateValueSelector(prevLoopBeginGate, loopBeginStateIn, prevBcOffsetPhiGate); break; } - if (accessor_.GetOpCode(stateInGate) == OpCode::STATE_ENTRY) { + if (stateInGate == GetEntryBBStateOut()) { break; } stateInGate = accessor_.GetState(stateInGate); @@ -192,9 +191,16 @@ void AsyncFunctionLowering::UpdateValueSelector(GateRef prevLoopBeginGate, if (accessor_.GetOpCode(use) == OpCode::VALUE_SELECTOR && use != prevBcOffsetPhiGate) { auto machineType = accessor_.GetMachineType(use); auto gateType = accessor_.GetGateType(use); - GateRef undefinedGate = + GateRef undefinedGate = Circuit::NullGate(); + if (gateType.IsNumberType()) { + undefinedGate = + circuit_->NewGate(circuit_->GetMetaBuilder()->Constant(JSTaggedValue::VALUE_ZERO), + machineType, GateType::IntType()); + } else { + undefinedGate = circuit_->NewGate(circuit_->GetMetaBuilder()->Constant(JSTaggedValue::VALUE_UNDEFINED), machineType, gateType); + } auto firstValueGate = accessor_.GetValueIn(use, 0); auto newValueSelector = circuit_->NewGate(circuit_->ValueSelector(2), machineType, // 2: valuesIn {newGate, undefinedGate, firstValueGate}, @@ -279,5 +285,27 @@ GateRef AsyncFunctionLowering::GetDependPhiFromLoopBegin(GateRef gate) const LOG_COMPILER(FATAL) << "Can not find depend-selector from loopbegin"; return Circuit::NullGate(); } + +GateRef AsyncFunctionLowering::GetEntryBBStateOut() const +{ + auto bb = bcBuilder_->GetBasicBlockById(1); // 1 : First Block Id + auto state = bb.stateCurrent; + if (accessor_.IsCFGMerge(state)) { + return accessor_.GetState(state); + } else { + return state; + } +} + +GateRef AsyncFunctionLowering::GetEntryBBDependOut() const +{ + auto bb = bcBuilder_->GetBasicBlockById(1); // 1 : First Block Id + auto depend = bb.dependCurrent; + if (accessor_.IsDependSelector(depend)) { + return accessor_.GetDep(depend); + } else { + return depend; + } +} } // panda::ecmascript::kungfu diff --git a/ecmascript/compiler/async_function_lowering.h b/ecmascript/compiler/async_function_lowering.h index 35c19cac298282399df7506ea26bf54c193457c8..adca55226c7621a35c709e74656297123d0ef8a2 100644 --- a/ecmascript/compiler/async_function_lowering.h +++ b/ecmascript/compiler/async_function_lowering.h @@ -20,7 +20,6 @@ #include "ecmascript/compiler/circuit.h" #include "ecmascript/compiler/circuit_builder-inl.h" #include "ecmascript/compiler/circuit_builder.h" -#include "ecmascript/compiler/graph_visitor.h" #include "ecmascript/mem/chunk_containers.h" namespace panda::ecmascript::kungfu { @@ -29,9 +28,8 @@ public: AsyncFunctionLowering(BytecodeCircuitBuilder *bcBuilder, Circuit *circuit, CompilationConfig *cmpCfg, bool enableLog, const std::string& name) : bcBuilder_(bcBuilder), circuit_(circuit), builder_(circuit, cmpCfg), enableLog_(enableLog), - stateEntry_(circuit->GetStateRoot()), - dependEntry_(circuit->GetDependRoot()), - accessor_(circuit), argAccessor_(circuit), methodName_(name) + accessor_(circuit), argAccessor_(circuit), stateEntry_(GetEntryBBStateOut()), + dependEntry_(GetEntryBBDependOut()), methodName_(name) { } @@ -66,14 +64,18 @@ private: GateRef GetDependPhiFromLoopBegin(GateRef loopbegin) const; + GateRef GetEntryBBStateOut() const; + + GateRef GetEntryBBDependOut() const; + BytecodeCircuitBuilder *bcBuilder_; Circuit *circuit_; CircuitBuilder builder_; bool enableLog_ {false}; - GateRef stateEntry_ {Circuit::NullGate()}; - GateRef dependEntry_ {Circuit::NullGate()}; GateAccessor accessor_; ArgumentAccessor argAccessor_; + GateRef stateEntry_ {Circuit::NullGate()}; + GateRef dependEntry_ {Circuit::NullGate()}; std::string methodName_; }; } // panda::ecmascript::kungfu diff --git a/ecmascript/compiler/base/bit_set.h b/ecmascript/compiler/base/bit_set.h index eaa1321fdca48e8d4d7f3c4b0252e2c18c9ce2ed..5eb0c40878ee4b9c578de5df48dbad4dd1edd682 100644 --- a/ecmascript/compiler/base/bit_set.h +++ b/ecmascript/compiler/base/bit_set.h @@ -18,6 +18,7 @@ #include #include +#include "ecmascript/mem/chunk.h" namespace panda::ecmascript::kungfu { class BitSet { @@ -29,22 +30,34 @@ public: static constexpr uint32_t BIT_PER_WORD_LOG2 = 6; static constexpr uint32_t BIT_PER_WORD_MASK = BIT_PER_WORD - 1; - explicit BitSet(size_t bitSize) + explicit BitSet(Chunk* chunk, size_t bitSize) { wordCount_ = SizeOf(bitSize); if (UseWords()) { - data_.words_ = new uint64_t[wordCount_]; + data_.words_ = chunk->NewArray(wordCount_); + Reset(); } } ~BitSet() { if (UseWords()) { - delete[] data_.words_; + // no need delete chunk memory data_.words_ = nullptr; } } + void Reset() + { + if (!UseWords()) { + data_.inlineWord_ = 0; + } else { + for (size_t i = 0; i < wordCount_; i++) { + data_.words_[i] = 0; + } + } + } + bool TestBit(size_t offset) const { if (!UseWords()) { @@ -127,7 +140,7 @@ private: uint64_t Mask(size_t index) const { - return 1 << index; + return uint64_t{1} << index; } size_t IndexInWord(size_t offset) const diff --git a/ecmascript/compiler/base/depend_chain_helper.cpp b/ecmascript/compiler/base/depend_chain_helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5d87bc51ce349ebf9bc632f00adab9a8a3ed6fee --- /dev/null +++ b/ecmascript/compiler/base/depend_chain_helper.cpp @@ -0,0 +1,106 @@ +/* + * 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/compiler/base/depend_chain_helper.h" + +namespace panda::ecmascript::kungfu { + +void DependChains::Merge(DependChains* that) +{ + // find common sub list + while (size_ > that->size_) { + head_ = head_->next; + size_--; + } + + auto lhs = this->head_; + auto rhs = that->head_; + size_t rhsSize = that->size_; + while (rhsSize > size_) { + rhs = rhs->next; + rhsSize--; + } + while (lhs != rhs) { + ASSERT(lhs != nullptr); + lhs = lhs->next; + rhs = rhs->next; + size_--; + } + head_ = lhs; +} + +bool DependChains::Equals(DependChains* that) +{ + if (that == nullptr) { + return false; + } + if (size_ != that->size_) { + return false; + } + auto lhs = this->head_; + auto rhs = that->head_; + while (lhs != rhs) { + if (lhs->gate != rhs->gate) { + return false; + } + lhs = lhs->next; + rhs = rhs->next; + } + return true; +} + +uint32_t DependChains::FoundIndexCheckedForLength(RangeGuard* rangeGuard, GateRef input) +{ + for (Node* node = head_; node != nullptr; node = node->next) { + uint32_t length = rangeGuard->CheckIndexCheckLengthInput(node->gate, input); + if (length > 0) { // found !!! + return length; + } + } + return 0; +} + +uint32_t DependChains::FoundIndexCheckedForIndex(RangeGuard* rangeGuard, GateRef input) +{ + for (Node* node = head_; node != nullptr; node = node->next) { + uint32_t length = rangeGuard->CheckIndexCheckIndexInput(node->gate, input); + if (length > 0) { // found !!! + return length; + } + } + return 0; +} + +GateRef DependChains::LookupNode(LaterElimination* elimination, GateRef gate) +{ + for (Node* node = head_; node != nullptr; node = node->next) { + if (elimination->CheckReplacement(node->gate, gate)) { + return node->gate; + } + } + return Circuit::NullGate(); +} + +DependChains* DependChains::UpdateNode(GateRef gate) +{ + // assign node->next to head + Node* node = chunk_->New(gate, head_); + DependChains* that = new (chunk_) DependChains(chunk_); + // assign head to node + that->head_ = node; + that->size_ = size_ + 1; + return that; +} +} // namespace panda::ecmascript::kungfu \ No newline at end of file diff --git a/ecmascript/compiler/base/depend_chain_helper.h b/ecmascript/compiler/base/depend_chain_helper.h new file mode 100644 index 0000000000000000000000000000000000000000..e6da61809518dd626300cae1f6cfc0cfb829021c --- /dev/null +++ b/ecmascript/compiler/base/depend_chain_helper.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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_COMPILER_DEPEND_CHAIN_HELPER_H +#define ECMASCRIPT_COMPILER_DEPEND_CHAIN_HELPER_H + +#include "ecmascript/compiler/circuit_builder.h" +#include "ecmascript/compiler/gate_accessor.h" +#include "ecmascript/compiler/later_elimination.h" +#include "ecmascript/compiler/range_guard.h" +#include "ecmascript/mem/chunk_containers.h" + +namespace panda::ecmascript::kungfu { +class LaterElimination; +class RangeGuard; +class DependChains : public ChunkObject { +public: + DependChains(Chunk* chunk) : chunk_(chunk) {} + ~DependChains() = default; + + DependChains* UpdateNode(GateRef gate); + bool Equals(DependChains* that); + void Merge(DependChains* that); + void CopyFrom(DependChains *other) + { + head_ = other->head_; + size_ = other->size_; + } + uint32_t FoundIndexCheckedForLength(RangeGuard* rangeGuard, GateRef input); + uint32_t FoundIndexCheckedForIndex(RangeGuard* rangeGuard, GateRef input); + GateRef LookupNode(LaterElimination* elimination, GateRef gate); +private: + struct Node { + Node(GateRef gate, Node* next) : gate(gate), next(next) {} + GateRef gate; + Node *next; + }; + + Node *head_{nullptr}; + size_t size_ {0}; + Chunk* chunk_; +}; +} // panda::ecmascript::kungfu +#endif // ECMASCRIPT_COMPILER_DEPEND_CHAIN_HELPER_H \ No newline at end of file diff --git a/ecmascript/compiler/bc_call_signature.h b/ecmascript/compiler/bc_call_signature.h index 0b10e70b36b25ca4a20319b1795968fb72fb0a69..05c2567779ef6c9d44424b7a23dcd6cb9bc4d48c 100644 --- a/ecmascript/compiler/bc_call_signature.h +++ b/ecmascript/compiler/bc_call_signature.h @@ -382,7 +382,10 @@ namespace panda::ecmascript::kungfu { V(ThrowStackOverflowException) #define APPEND_SUFFIX(name, V) \ - V(name##WithProf, name) + V(name##WithProf, name, SlotIDFormat::IMM8) + +#define APPEND_SUFFIX_IMM16(name, V) \ + V(name##WithProf, name, SlotIDFormat::IMM16) #define ASM_INTERPRETER_BC_PROFILER_STUB_LIST(V) \ ASM_INTERPRETER_BC_TYPE_PROFILER_STUB_LIST(V) \ @@ -416,68 +419,87 @@ namespace panda::ecmascript::kungfu { APPEND_SUFFIX(HandleStrictnoteqImm8V8, V) \ APPEND_SUFFIX(HandleStricteqImm8V8, V) -#define ASM_INTERPRETER_BC_FUNC_HOT_PROFILER_STUB_LIST(V) \ - APPEND_SUFFIX(HandleJmpImm8, V) \ - APPEND_SUFFIX(HandleJmpImm16, V) \ - APPEND_SUFFIX(HandleJmpImm32, V) \ - APPEND_SUFFIX(HandleJeqzImm8, V) \ - APPEND_SUFFIX(HandleJeqzImm16, V) \ - APPEND_SUFFIX(HandleJeqzImm32, V) \ - APPEND_SUFFIX(HandleJnezImm8, V) \ - APPEND_SUFFIX(HandleJnezImm16, V) \ - APPEND_SUFFIX(HandleJnezImm32, V) \ - APPEND_SUFFIX(HandleReturn, V) \ - APPEND_SUFFIX(HandleReturnundefined, V) \ - APPEND_SUFFIX(HandleSuspendgeneratorV8, V) \ - APPEND_SUFFIX(HandleDeprecatedSuspendgeneratorPrefV8V8, V) \ +#define ASM_INTERPRETER_BC_FUNC_HOT_PROFILER_STUB_LIST(V) \ + APPEND_SUFFIX(HandleJmpImm8, V) \ + APPEND_SUFFIX_IMM16(HandleJmpImm16, V) \ + APPEND_SUFFIX(HandleJmpImm32, V) \ + APPEND_SUFFIX(HandleJeqzImm8, V) \ + APPEND_SUFFIX_IMM16(HandleJeqzImm16, V) \ + APPEND_SUFFIX(HandleJeqzImm32, V) \ + APPEND_SUFFIX(HandleJnezImm8, V) \ + APPEND_SUFFIX_IMM16(HandleJnezImm16, V) \ + APPEND_SUFFIX(HandleJnezImm32, V) \ + APPEND_SUFFIX(HandleReturn, V) \ + APPEND_SUFFIX(HandleReturnundefined, V) \ + APPEND_SUFFIX(HandleSuspendgeneratorV8, V) \ APPEND_SUFFIX(HandleAsyncgeneratorresolveV8V8V8, V) #define ASM_INTERPRETER_BC_FUNC_COUNT_PROFILER_STUB_LIST(V) \ APPEND_SUFFIX(HandleCallarg0Imm8, V) \ - APPEND_SUFFIX(HandleDeprecatedCallarg0PrefV8, V) \ APPEND_SUFFIX(HandleCallarg1Imm8V8, V) \ - APPEND_SUFFIX(HandleDeprecatedCallarg1PrefV8V8, V) \ APPEND_SUFFIX(HandleCallargs2Imm8V8V8, V) \ - APPEND_SUFFIX(HandleDeprecatedCallargs2PrefV8V8V8, V) \ APPEND_SUFFIX(HandleCallargs3Imm8V8V8V8, V) \ - APPEND_SUFFIX(HandleDeprecatedCallargs3PrefV8V8V8V8, V) \ APPEND_SUFFIX(HandleCallrangeImm8Imm8V8, V) \ - APPEND_SUFFIX(HandleWideCallrangePrefImm16V8, V) \ - APPEND_SUFFIX(HandleDeprecatedCallrangePrefImm16V8, V) \ + APPEND_SUFFIX_IMM16(HandleWideCallrangePrefImm16V8, V) \ APPEND_SUFFIX(HandleCallthisrangeImm8Imm8V8, V) \ - APPEND_SUFFIX(HandleWideCallthisrangePrefImm16V8, V) \ - APPEND_SUFFIX(HandleDeprecatedCallthisrangePrefImm16V8, V) \ + APPEND_SUFFIX_IMM16(HandleWideCallthisrangePrefImm16V8, V) \ APPEND_SUFFIX(HandleCallthis0Imm8V8, V) \ APPEND_SUFFIX(HandleCallthis1Imm8V8V8, V) \ APPEND_SUFFIX(HandleCallthis2Imm8V8V8V8, V) \ APPEND_SUFFIX(HandleCallthis3Imm8V8V8V8V8, V) \ APPEND_SUFFIX(HandleNewobjrangeImm8Imm8V8, V) \ - APPEND_SUFFIX(HandleNewobjrangeImm16Imm8V8, V) \ - APPEND_SUFFIX(HandleWideNewobjrangePrefImm16V8, V) - -#define ASM_INTERPRETER_BC_LAYOUT_PROFILER_STUB_LIST(V) \ - APPEND_SUFFIX(HandleDefineclasswithbufferImm8Id16Id16Imm16V8, V) \ - APPEND_SUFFIX(HandleDefineclasswithbufferImm16Id16Id16Imm16V8, V) \ - APPEND_SUFFIX(HandleDefinegettersetterbyvalueV8V8V8V8, V) \ - APPEND_SUFFIX(HandleLdobjbynameImm8Id16, V) \ - APPEND_SUFFIX(HandleLdobjbynameImm16Id16, V) \ - APPEND_SUFFIX(HandleLdthisbynameImm16Id16, V) \ - APPEND_SUFFIX(HandleLdthisbynameImm8Id16, V) \ - APPEND_SUFFIX(HandleStthisbynameImm8Id16, V) \ - APPEND_SUFFIX(HandleStthisbynameImm16Id16, V) \ - APPEND_SUFFIX(HandleStthisbyvalueImm8V8, V) \ - APPEND_SUFFIX(HandleStthisbyvalueImm16V8, V) \ - APPEND_SUFFIX(HandleStobjbyvalueImm16V8V8, V) \ - APPEND_SUFFIX(HandleStobjbynameImm8Id16V8, V) \ - APPEND_SUFFIX(HandleStobjbynameImm16Id16V8, V) \ - APPEND_SUFFIX(HandleStobjbyvalueImm8V8V8, V) \ - APPEND_SUFFIX(HandleStownbyvaluewithnamesetImm16V8V8, V) \ - APPEND_SUFFIX(HandleStownbyvaluewithnamesetImm8V8V8, V) \ - APPEND_SUFFIX(HandleStownbyvalueImm8V8V8, V) \ - APPEND_SUFFIX(HandleStownbyvalueImm16V8V8, V) \ - APPEND_SUFFIX(HandleStownbynamewithnamesetImm16Id16V8, V) \ - APPEND_SUFFIX(HandleStownbynamewithnamesetImm8Id16V8, V) \ - APPEND_SUFFIX(HandleStownbynameImm16Id16V8, V) \ + APPEND_SUFFIX_IMM16(HandleNewobjrangeImm16Imm8V8, V) \ + APPEND_SUFFIX_IMM16(HandleWideNewobjrangePrefImm16V8, V) \ + APPEND_SUFFIX(HandleInstanceofImm8V8, V) \ + APPEND_SUFFIX(HandleTryldglobalbynameImm8Id16, V) \ + APPEND_SUFFIX_IMM16(HandleTryldglobalbynameImm16Id16, V) \ + APPEND_SUFFIX(HandleTrystglobalbynameImm8Id16, V) \ + APPEND_SUFFIX_IMM16(HandleTrystglobalbynameImm16Id16, V) \ + APPEND_SUFFIX_IMM16(HandleLdglobalvarImm16Id16, V) + +#define ASM_INTERPRETER_BC_LAYOUT_PROFILER_STUB_LIST(V) \ + APPEND_SUFFIX(HandleDefineclasswithbufferImm8Id16Id16Imm16V8, V) \ + APPEND_SUFFIX_IMM16(HandleDefineclasswithbufferImm16Id16Id16Imm16V8, V) \ + APPEND_SUFFIX(HandleDefinegettersetterbyvalueV8V8V8V8, V) \ + APPEND_SUFFIX(HandleCreateobjectwithbufferImm8Id16, V) \ + APPEND_SUFFIX_IMM16(HandleCreateobjectwithbufferImm16Id16, V) \ + APPEND_SUFFIX(HandleCreatearraywithbufferImm8Id16, V) \ + APPEND_SUFFIX_IMM16(HandleCreatearraywithbufferImm16Id16, V) \ + APPEND_SUFFIX(HandleCreateemptyobject, V) \ + APPEND_SUFFIX(HandleCreateemptyarrayImm8, V) \ + APPEND_SUFFIX_IMM16(HandleCreateemptyarrayImm16, V) \ + APPEND_SUFFIX(HandleLdobjbynameImm8Id16, V) \ + APPEND_SUFFIX_IMM16(HandleLdobjbynameImm16Id16, V) \ + APPEND_SUFFIX_IMM16(HandleLdthisbynameImm16Id16, V) \ + APPEND_SUFFIX(HandleLdthisbynameImm8Id16, V) \ + APPEND_SUFFIX(HandleStthisbynameImm8Id16, V) \ + APPEND_SUFFIX_IMM16(HandleStthisbynameImm16Id16, V) \ + APPEND_SUFFIX(HandleStthisbyvalueImm8V8, V) \ + APPEND_SUFFIX_IMM16(HandleStthisbyvalueImm16V8, V) \ + APPEND_SUFFIX_IMM16(HandleStobjbyvalueImm16V8V8, V) \ + APPEND_SUFFIX(HandleStobjbynameImm8Id16V8, V) \ + APPEND_SUFFIX_IMM16(HandleStobjbynameImm16Id16V8, V) \ + APPEND_SUFFIX(HandleStobjbyvalueImm8V8V8, V) \ + APPEND_SUFFIX(HandleStobjbyindexImm8V8Imm16, V) \ + APPEND_SUFFIX_IMM16(HandleStobjbyindexImm16V8Imm16, V) \ + APPEND_SUFFIX(HandleLdobjbyvalueImm8V8, V) \ + APPEND_SUFFIX_IMM16(HandleLdobjbyvalueImm16V8, V) \ + APPEND_SUFFIX_IMM16(HandleLdthisbyvalueImm16, V) \ + APPEND_SUFFIX(HandleLdthisbyvalueImm8, V) \ + APPEND_SUFFIX(HandleLdobjbyindexImm8Imm16, V) \ + APPEND_SUFFIX_IMM16(HandleLdobjbyindexImm16Imm16, V) \ + APPEND_SUFFIX(HandleWideLdobjbyindexPrefImm32, V) \ + APPEND_SUFFIX(HandleWideStobjbyindexPrefV8Imm32, V) \ + APPEND_SUFFIX_IMM16(HandleStownbyindexImm16V8Imm16, V) \ + APPEND_SUFFIX(HandleStownbyindexImm8V8Imm16, V) \ + APPEND_SUFFIX(HandleWideStownbyindexPrefV8Imm32, V) \ + APPEND_SUFFIX_IMM16(HandleStownbyvaluewithnamesetImm16V8V8, V) \ + APPEND_SUFFIX(HandleStownbyvaluewithnamesetImm8V8V8, V) \ + APPEND_SUFFIX(HandleStownbyvalueImm8V8V8, V) \ + APPEND_SUFFIX_IMM16(HandleStownbyvalueImm16V8V8, V) \ + APPEND_SUFFIX_IMM16(HandleStownbynamewithnamesetImm16Id16V8, V) \ + APPEND_SUFFIX(HandleStownbynamewithnamesetImm8Id16V8, V) \ + APPEND_SUFFIX_IMM16(HandleStownbynameImm16Id16V8, V) \ APPEND_SUFFIX(HandleStownbynameImm8Id16V8, V) #define INTERPRETER_DISABLE_SINGLE_STEP_DEBUGGING_BC_STUB_LIST(V) \ diff --git a/ecmascript/compiler/binary_section.h b/ecmascript/compiler/binary_section.h index 7a221e269b4afa771432c60416936253db0435c9..54c26ee2b791f6dc1db6a39622fe499edae23239 100644 --- a/ecmascript/compiler/binary_section.h +++ b/ecmascript/compiler/binary_section.h @@ -144,7 +144,8 @@ public: int Link() const { - return value_ == ElfSecName::SYMTAB ? 1 : 0; + // The strtab index is 2 inside An file. + return value_ == ElfSecName::SYMTAB ? 2 : 0; } void InitShTypeAndFlag() @@ -212,7 +213,7 @@ public: // RO data section needs 16 bytes alignment bool InRodataSection() const { - return ElfSecName::RODATA <= value_ && value_ <= ElfSecName::RODATA_CST8; + return ElfSecName::RODATA <= value_ && value_ <= ElfSecName::RODATA_CST32; } private: static int const FIX_SIZE = 24; // 24:Elf_Rel diff --git a/ecmascript/compiler/builtins/builtins_array_stub_builder.cpp b/ecmascript/compiler/builtins/builtins_array_stub_builder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6e07f5d7296705b960438d6082bcd9ea65059856 --- /dev/null +++ b/ecmascript/compiler/builtins/builtins_array_stub_builder.cpp @@ -0,0 +1,289 @@ +/* + * 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/compiler/builtins/builtins_array_stub_builder.h" + +#include "ecmascript/compiler/builtins/builtins_stubs.h" +#include "ecmascript/compiler/new_object_stub_builder.h" +#include "ecmascript/compiler/profiler_operation.h" +#include "ecmascript/compiler/rt_call_signature.h" +#include "ecmascript/runtime_call_id.h" + +namespace panda::ecmascript::kungfu { +void BuiltinsArrayStubBuilder::Concat(GateRef glue, GateRef thisValue, GateRef numArgs, + Variable *result, Label *exit, Label *slowPath) +{ + auto env = GetEnvironment(); + Label thisIsEmpty(env); + // Fast path if all the conditions below are satisfied: + // (1) this is an empty array with constructor not reset (see ArraySpeciesCreate for details); + // (2) At most one argument; + // (3) all the arguments (if exists) are empty arrays. + JsArrayRequirements reqThisValue; + reqThisValue.defaultConstructor = true; + Branch(IsJsArrayWithLengthLimit(glue, thisValue, MAX_LENGTH_ZERO, reqThisValue), &thisIsEmpty, slowPath); + Bind(&thisIsEmpty); + { + Label atMostOneArg(env); + Label argValIsEmpty(env); + GateRef numArgsAsInt32 = TruncPtrToInt32(numArgs); + Branch(Int32LessThanOrEqual(numArgsAsInt32, Int32(1)), &atMostOneArg, slowPath); + Bind(&atMostOneArg); + { + Label exactlyOneArg(env); + Branch(Int32Equal(numArgsAsInt32, Int32(0)), &argValIsEmpty, &exactlyOneArg); + Bind(&exactlyOneArg); + GateRef argVal = GetCallArg0(numArgs); + JsArrayRequirements reqArgVal; + Branch(IsJsArrayWithLengthLimit(glue, argVal, MAX_LENGTH_ZERO, reqArgVal), &argValIsEmpty, slowPath); + // Creates an empty array on fast path + Bind(&argValIsEmpty); + NewObjectStubBuilder newBuilder(this); + result->WriteVariable(newBuilder.CreateEmptyArray(glue)); + Jump(exit); + } + } +} + +void BuiltinsArrayStubBuilder::Filter(GateRef glue, GateRef thisValue, GateRef numArgs, + Variable *result, Label *exit, Label *slowPath) +{ + auto env = GetEnvironment(); + Label thisIsEmpty(env); + // Fast path if all the conditions below are satisfied: + // (1) this is an empty array with constructor not reset (see ArraySpeciesCreate for details); + // (2) callbackFn is callable (otherwise a TypeError shall be thrown in the slow path) + JsArrayRequirements req; + req.defaultConstructor = true; + Branch(IsJsArrayWithLengthLimit(glue, thisValue, MAX_LENGTH_ZERO, req), &thisIsEmpty, slowPath); + Bind(&thisIsEmpty); + { + Label isCallable(env); + Branch(IsCallable(GetCallArg0(numArgs)), &isCallable, slowPath); + // Creates an empty array on fast path + Bind(&isCallable); + NewObjectStubBuilder newBuilder(this); + result->WriteVariable(newBuilder.CreateEmptyArray(glue)); + Jump(exit); + } +} + +// Note: unused arguments are reserved for further development +void BuiltinsArrayStubBuilder::ForEach([[maybe_unused]] GateRef glue, GateRef thisValue, GateRef numArgs, + [[maybe_unused]] Variable *result, Label *exit, Label *slowPath) +{ + auto env = GetEnvironment(); + Label thisIsEmpty(env); + // Fast path if all the conditions below are satisfied: + // (1) this is an empty array with constructor not reset (see ArraySpeciesCreate for details); + // (2) callbackFn is callable (otherwise a TypeError shall be thrown in the slow path) + JsArrayRequirements req; + req.defaultConstructor = true; + Branch(IsJsArrayWithLengthLimit(glue, thisValue, MAX_LENGTH_ZERO, req), &thisIsEmpty, slowPath); + Bind(&thisIsEmpty); + // Do nothing on fast path + Branch(IsCallable(GetCallArg0(numArgs)), exit, slowPath); +} + +// Note: unused arguments are reserved for further development +void BuiltinsArrayStubBuilder::IndexOf([[maybe_unused]] GateRef glue, GateRef thisValue, GateRef numArgs, + Variable *result, Label *exit, Label *slowPath) +{ + auto env = GetEnvironment(); + Label thisIsEmpty(env); + // Fast path if: (1) this is an empty array; (2) fromIndex is missing + JsArrayRequirements req; + Branch(IsJsArrayWithLengthLimit(glue, thisValue, MAX_LENGTH_ZERO, req), &thisIsEmpty, slowPath); + Bind(&thisIsEmpty); + { + Label atMostOneArg(env); + Branch(Int32LessThanOrEqual(TruncPtrToInt32(numArgs), Int32(1)), &atMostOneArg, slowPath); + // Returns -1 on fast path + Bind(&atMostOneArg); + result->WriteVariable(IntToTaggedPtr(Int32(-1))); + Jump(exit); + } +} + +// Note: unused arguments are reserved for further development +void BuiltinsArrayStubBuilder::LastIndexOf([[maybe_unused]] GateRef glue, GateRef thisValue, GateRef numArgs, + Variable *result, Label *exit, Label *slowPath) +{ + auto env = GetEnvironment(); + Label thisIsEmpty(env); + // Fast path if: (1) this is an empty array; (2) fromIndex is missing + JsArrayRequirements req; + Branch(IsJsArrayWithLengthLimit(glue, thisValue, MAX_LENGTH_ZERO, req), &thisIsEmpty, slowPath); + Bind(&thisIsEmpty); + { + Label atMostOneArg(env); + Branch(Int32LessThanOrEqual(TruncPtrToInt32(numArgs), Int32(1)), &atMostOneArg, slowPath); + // Returns -1 on fast path + Bind(&atMostOneArg); + result->WriteVariable(IntToTaggedPtr(Int32(-1))); + Jump(exit); + } +} + +void BuiltinsArrayStubBuilder::Slice(GateRef glue, GateRef thisValue, GateRef numArgs, + Variable *result, Label *exit, Label *slowPath) +{ + auto env = GetEnvironment(); + Label thisIsEmpty(env); + // Fast path if: + // (1) this is an empty array with constructor not reset (see ArraySpeciesCreate for details); + // (2) no arguments exist + JsArrayRequirements req; + req.defaultConstructor = true; + Branch(IsJsArrayWithLengthLimit(glue, thisValue, MAX_LENGTH_ZERO, req), &thisIsEmpty, slowPath); + Bind(&thisIsEmpty); + { + Label noArgs(env); + GateRef numArgsAsInt32 = TruncPtrToInt32(numArgs); + Branch(Int32Equal(numArgsAsInt32, Int32(0)), &noArgs, slowPath); + // Creates a new empty array on fast path + Bind(&noArgs); + NewObjectStubBuilder newBuilder(this); + result->WriteVariable(newBuilder.CreateEmptyArray(glue)); + Jump(exit); + } +} + +// Note: unused arguments are reserved for further development +void BuiltinsArrayStubBuilder::Reverse([[maybe_unused]] GateRef glue, GateRef thisValue, + [[maybe_unused]] GateRef numArgs, + Variable *result, Label *exit, Label *slowPath) +{ + auto env = GetEnvironment(); + Label thisIsEmpty(env); + // Fast path is this is an array of length 0 or 1 + JsArrayRequirements req; + Branch(IsJsArrayWithLengthLimit(glue, thisValue, MAX_LENGTH_ONE, req), &thisIsEmpty, slowPath); + Bind(&thisIsEmpty); + // Returns thisValue on fast path + result->WriteVariable(thisValue); + Jump(exit); +} + +GateRef BuiltinsArrayStubBuilder::IsJsArrayWithLengthLimit(GateRef glue, GateRef object, + uint32_t maxLength, JsArrayRequirements requirements) +{ + auto env = GetEnvironment(); + Label entry(env); + env->SubCfgEntry(&entry); + Label isHeapObject(env); + Label isJsArray(env); + Label stabilityCheckPassed(env); + Label defaultConstructorCheckPassed(env); + Label exit(env); + DEFVARIABLE(result, VariableType::BOOL(), False()); + + Branch(TaggedIsHeapObject(object), &isHeapObject, &exit); + Bind(&isHeapObject); + Branch(IsJsArray(object), &isJsArray, &exit); + Bind(&isJsArray); + if (requirements.stable) { + Branch(IsStableJSArray(glue, object), &stabilityCheckPassed, &exit); + } else { + Jump(&stabilityCheckPassed); + } + Bind(&stabilityCheckPassed); + if (requirements.defaultConstructor) { + // If HasConstructor bit is set to 1, then the constructor has been modified. + Branch(HasConstructor(object), &exit, &defaultConstructorCheckPassed); + } else { + Jump(&defaultConstructorCheckPassed); + } + Bind(&defaultConstructorCheckPassed); + result.WriteVariable(Int32UnsignedLessThanOrEqual(GetArrayLength(object), Int32(maxLength))); + Jump(&exit); + Bind(&exit); + GateRef ret = *result; + env->SubCfgExit(); + return ret; +} + +void BuiltinsArrayStubBuilder::Push(GateRef glue, GateRef thisValue, + GateRef numArgs, Variable *result, Label *exit, Label *slowPath) +{ + auto env = GetEnvironment(); + Label isHeapObject(env); + Label isJsArray(env); + Label isStability(env); + Label setLength(env); + Label smallArgs(env); + Label checkSmallArgs(env); + + Branch(TaggedIsHeapObject(thisValue), &isHeapObject, slowPath); + Bind(&isHeapObject); + Branch(IsJsArray(thisValue), &isJsArray, slowPath); + Bind(&isJsArray); + + Branch(IsStableJSArray(glue, thisValue), &isStability, slowPath); + Bind(&isStability); + + GateRef oldLength = GetArrayLength(thisValue); + *result = IntToTaggedPtr(oldLength); + + Branch(Int32Equal(ChangeIntPtrToInt32(numArgs), Int32(0)), exit, &checkSmallArgs); + Bind(&checkSmallArgs); + // now unsupport more than 2 args + Branch(Int32LessThanOrEqual(ChangeIntPtrToInt32(numArgs), Int32(2)), &smallArgs, slowPath); + Bind(&smallArgs); + GateRef newLength = Int32Add(oldLength, ChangeIntPtrToInt32(numArgs)); + + DEFVARIABLE(elements, VariableType::JS_ANY(), GetElementsArray(thisValue)); + GateRef capacity = GetLengthOfTaggedArray(*elements); + Label grow(env); + Label setValue(env); + Branch(Int32GreaterThan(newLength, capacity), &grow, &setValue); + Bind(&grow); + { + elements = + CallRuntime(glue, RTSTUB_ID(JSObjectGrowElementsCapacity), { thisValue, IntToTaggedInt(newLength) }); + Jump(&setValue); + } + Bind(&setValue); + { + Label oneArg(env); + Label twoArg(env); + DEFVARIABLE(index, VariableType::INT32(), Int32(0)); + DEFVARIABLE(value, VariableType::JS_ANY(), Undefined()); + Branch(Int64Equal(numArgs, IntPtr(1)), &oneArg, &twoArg); // 1 one arg + Bind(&oneArg); + { + value = GetCallArg0(numArgs); + index = Int32Add(oldLength, Int32(0)); // 0 slot index + SetValueToTaggedArray(VariableType::JS_ANY(), glue, *elements, *index, *value); + Jump(&setLength); + } + Bind(&twoArg); + { + value = GetCallArg0(numArgs); + index = Int32Add(oldLength, Int32(0)); // 0 slot index + SetValueToTaggedArray(VariableType::JS_ANY(), glue, *elements, *index, *value); + + value = GetCallArg1(numArgs); + index = Int32Add(oldLength, Int32(1)); // 1 slot index + SetValueToTaggedArray(VariableType::JS_ANY(), glue, *elements, *index, *value); + Jump(&setLength); + } + } + Bind(&setLength); + SetArrayLength(glue, thisValue, newLength); + result->WriteVariable(IntToTaggedPtr(newLength)); + Jump(exit); +} +} // namespace panda::ecmascript::kungfu diff --git a/ecmascript/compiler/builtins/builtins_array_stub_builder.h b/ecmascript/compiler/builtins/builtins_array_stub_builder.h new file mode 100644 index 0000000000000000000000000000000000000000..e95eaf5f4d4f82927539c321ac2278c8fb631b7c --- /dev/null +++ b/ecmascript/compiler/builtins/builtins_array_stub_builder.h @@ -0,0 +1,67 @@ +/* + * 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_COMPILER_BUILTINS_ARRAY_STUB_BUILDER_H +#define ECMASCRIPT_COMPILER_BUILTINS_ARRAY_STUB_BUILDER_H +#include "ecmascript/compiler/circuit_builder.h" +#include "ecmascript/compiler/gate.h" +#include "ecmascript/compiler/share_gate_meta_data.h" +#include "ecmascript/compiler/stub_builder-inl.h" + +namespace panda::ecmascript::kungfu { +class BuiltinsArrayStubBuilder : public BuiltinsStubBuilder { +public: + explicit BuiltinsArrayStubBuilder(StubBuilder *parent) + : BuiltinsStubBuilder(parent) {} + ~BuiltinsArrayStubBuilder() override = default; + NO_MOVE_SEMANTIC(BuiltinsArrayStubBuilder); + NO_COPY_SEMANTIC(BuiltinsArrayStubBuilder); + void GenerateCircuit() override {} + + void Concat(GateRef glue, GateRef thisValue, GateRef numArgs, + Variable *result, Label *exit, Label *slowPath); + + void Filter(GateRef glue, GateRef thisValue, GateRef numArgs, + Variable *result, Label *exit, Label *slowPath); + + void ForEach(GateRef glue, GateRef thisValue, GateRef numArgs, + Variable *result, Label *exit, Label *slowPath); + + void IndexOf(GateRef glue, GateRef thisValue, GateRef numArgs, + Variable *result, Label *exit, Label *slowPath); + + void LastIndexOf(GateRef glue, GateRef thisValue, GateRef numArgs, + Variable *result, Label *exit, Label *slowPath); + + void Slice(GateRef glue, GateRef thisValue, GateRef numArgs, + Variable *result, Label *exit, Label *slowPath); + + void Reverse(GateRef glue, GateRef thisValue, GateRef numArgs, + Variable *result, Label *exit, Label *slowPath); + + void Push(GateRef glue, GateRef thisValue, GateRef numArgs, + Variable *result, Label *exit, Label *slowPath); +private: + static constexpr uint32_t MAX_LENGTH_ZERO = 0; + static constexpr uint32_t MAX_LENGTH_ONE = 1; + struct JsArrayRequirements { + bool stable = false; + bool defaultConstructor = false; + }; + GateRef IsJsArrayWithLengthLimit(GateRef glue, GateRef object, + uint32_t maxLength, JsArrayRequirements requirements); +}; +} // namespace panda::ecmascript::kungfu +#endif // ECMASCRIPT_COMPILER_BUILTINS_ARRAY_STUB_BUILDER_H diff --git a/ecmascript/compiler/builtins/builtins_call_signature.cpp b/ecmascript/compiler/builtins/builtins_call_signature.cpp index 0a3f6b83389641ba75829733e38f1f714a11cd75..890741540963f072a846202947a36eb5720e62a6 100644 --- a/ecmascript/compiler/builtins/builtins_call_signature.cpp +++ b/ecmascript/compiler/builtins/builtins_call_signature.cpp @@ -38,15 +38,9 @@ void BuiltinsStubCSigns::Initialize() BuiltinsCallSignature::Initialize(&callSigns_[name]); \ COMMON_INIT(name) - BUILTINS_METHOD_STUB_LIST(INIT_BUILTINS_METHOD) + BUILTINS_STUB_LIST(INIT_BUILTINS_METHOD) #undef INIT_BUILTINS_METHOD -#define INIT_BUILTINS_CONSTRUCTOR(name) \ - BuiltinsWithArgvCallSignature::Initialize(&callSigns_[name]); \ - COMMON_INIT(name) - - BUILTINS_CONSTRUCTOR_STUB_LIST(INIT_BUILTINS_CONSTRUCTOR) -#undef INIT_BUILTINS_CONSTRUCTOR #undef COMMON_INIT BuiltinsCallSignature::Initialize(&builtinsCSign_); BuiltinsWithArgvCallSignature::Initialize(&builtinsWithArgvCSign_); diff --git a/ecmascript/compiler/builtins/builtins_call_signature.h b/ecmascript/compiler/builtins/builtins_call_signature.h index 95a464feef1eed62b825d2ee6e635554363d28d5..88fa931b2cb1364235507a5780a58bea37d5f5fe 100644 --- a/ecmascript/compiler/builtins/builtins_call_signature.h +++ b/ecmascript/compiler/builtins/builtins_call_signature.h @@ -32,10 +32,12 @@ namespace panda::ecmascript::kungfu { BUILTINS_CONSTRUCTOR_STUB_LIST(V) #define BUILTINS_METHOD_STUB_LIST(V) \ - V(CharCodeAt) \ - V(IndexOf) \ - V(Substring) \ - V(CharAt) \ + V(StringCharCodeAt) \ + V(StringIndexOf) \ + V(StringSubstring) \ + V(StringCharAt) \ + V(StringFromCharCode) \ + V(ObjectToString) \ V(VectorForEach) \ V(VectorReplaceAllElements) \ V(StackForEach) \ @@ -50,7 +52,30 @@ namespace panda::ecmascript::kungfu { V(ListForEach) \ V(ArrayListForEach) \ V(ArrayListReplaceAllElements) \ - V(FunctionPrototypeApply) + V(FunctionPrototypeApply) \ + V(ArrayConcat) \ + V(ArrayFilter) \ + V(ArrayForEach) \ + V(ArrayIndexOf) \ + V(ArrayLastIndexOf) \ + V(ArraySlice) \ + V(ArrayReverse) \ + V(ArrayPush) \ + V(SetClear) \ + V(SetValues) \ + V(SetEntries) \ + V(SetForEach) \ + V(SetAdd) \ + V(SetDelete) \ + V(SetHas) \ + V(MapClear) \ + V(MapValues) \ + V(MapEntries) \ + V(MapKeys) \ + V(MapForEach) \ + V(MapSet) \ + V(MapDelete) \ + V(MapHas) #define BUILTINS_CONSTRUCTOR_STUB_LIST(V) \ V(BooleanConstructor) \ @@ -58,13 +83,16 @@ namespace panda::ecmascript::kungfu { V(ArrayConstructor) #define AOT_BUILTINS_STUB_LIST(V) \ - V(SQRT) \ + V(SQRT) /* list start and math list start */ \ V(COS) \ V(SIN) \ V(ACOS) \ V(ATAN) \ V(ABS) \ - V(FLOOR) + V(FLOOR) /* math list end */ \ + V(LocaleCompare) \ + V(SORT) \ + V(STRINGIFY) class BuiltinsStubCSigns { public: @@ -78,6 +106,10 @@ public: AOT_BUILTINS_STUB_LIST(DEF_STUB_ID) #undef DEF_STUB_ID BUILTINS_CONSTRUCTOR_STUB_FIRST = BooleanConstructor, + TYPED_BUILTINS_FIRST = SQRT, + TYPED_BUILTINS_LAST = STRINGIFY, + TYPED_BUILTINS_MATH_FIRST = SQRT, + TYPED_BUILTINS_MATH_LAST = FLOOR, INVALID = 0xFF, }; @@ -114,18 +146,14 @@ public: static bool IsTypedBuiltin(ID builtinId) { - switch (builtinId) { - case BuiltinsStubCSigns::ID::COS: - case BuiltinsStubCSigns::ID::SIN: - case BuiltinsStubCSigns::ID::ACOS: - case BuiltinsStubCSigns::ID::ATAN: - case BuiltinsStubCSigns::ID::ABS: - case BuiltinsStubCSigns::ID::FLOOR: - case BuiltinsStubCSigns::ID::SQRT: - return true; - default: - return false; - } + return (BuiltinsStubCSigns::ID::TYPED_BUILTINS_FIRST <= builtinId) && + (builtinId <= BuiltinsStubCSigns::ID::TYPED_BUILTINS_LAST); + } + + static bool IsTypedBuiltinMath(ID builtinId) + { + return (BuiltinsStubCSigns::ID::TYPED_BUILTINS_MATH_FIRST <= builtinId) && + (builtinId <= BuiltinsStubCSigns::ID::TYPED_BUILTINS_MATH_LAST); } static ConstantIndex GetConstantIndex(ID builtinId) @@ -145,6 +173,12 @@ public: return ConstantIndex::MATH_FLOOR_FUNCTION_INDEX; case BuiltinsStubCSigns::ID::SQRT: return ConstantIndex::MATH_SQRT_FUNCTION_INDEX; + case BuiltinsStubCSigns::ID::LocaleCompare: + return ConstantIndex::LOCALE_COMPARE_FUNCTION_INDEX; + case BuiltinsStubCSigns::ID::SORT: + return ConstantIndex::ARRAY_SORT_FUNCTION_INDEX; + case BuiltinsStubCSigns::ID::STRINGIFY: + return ConstantIndex::JSON_STRINGIFY_FUNCTION_INDEX; default: LOG_COMPILER(FATAL) << "this branch is unreachable"; UNREACHABLE(); @@ -161,6 +195,9 @@ public: {"atan", ATAN}, {"abs", ABS}, {"floor", FLOOR}, + {"localeCompare", LocaleCompare}, + {"sort", SORT}, + {"stringify", STRINGIFY}, }; if (str2BuiltinId.count(idStr) > 0) { return str2BuiltinId.at(idStr); @@ -189,6 +226,7 @@ enum class BuiltinsArgs : size_t { #define BUILTINS_STUB_ID(name) kungfu::BuiltinsStubCSigns::name #define IS_TYPED_BUILTINS_ID(id) kungfu::BuiltinsStubCSigns::IsTypedBuiltin(id) +#define IS_TYPED_BUILTINS_MATH_ID(id) kungfu::BuiltinsStubCSigns::IsTypedBuiltinMath(id) #define GET_TYPED_CONSTANT_INDEX(id) kungfu::BuiltinsStubCSigns::GetConstantIndex(id) } // namespace panda::ecmascript::kungfu #endif // ECMASCRIPT_COMPILER_BUILTINS_CALL_SIGNATURE_H diff --git a/ecmascript/compiler/builtins/builtins_collection_stub_builder.cpp b/ecmascript/compiler/builtins/builtins_collection_stub_builder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..26ca3d5cbe199a375e05b070020cd0d60b8841f2 --- /dev/null +++ b/ecmascript/compiler/builtins/builtins_collection_stub_builder.cpp @@ -0,0 +1,253 @@ +/* + * 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/compiler/builtins/builtins_collection_stub_builder.h" + +#include "ecmascript/compiler/builtins/builtins_stubs.h" +#include "ecmascript/compiler/new_object_stub_builder.h" +#include "ecmascript/linked_hash_table.h" +#include "ecmascript/js_map.h" +#include "ecmascript/js_set.h" +#include "ecmascript/js_iterator.h" + +namespace panda::ecmascript::kungfu { + +template +void BuiltinsCollectionStubBuilder::CheckCollectionObj(Label *thisCollectionObj, Label *slowPath) +{ + // check target obj + auto jsType = std::is_same_v ? JSType::JS_SET : JSType::JS_MAP; + GateRef isJsCollectionObj = IsJSObjectType(thisValue_, jsType); + Branch(isJsCollectionObj, thisCollectionObj, slowPath); +} + +template +void BuiltinsCollectionStubBuilder::Clear(Variable *result, Label *exit, Label *slowPath) +{ + auto env = GetEnvironment(); + Label thisCollectionObj(env); + // check target obj + CheckCollectionObj(&thisCollectionObj, slowPath); + + Bind(&thisCollectionObj); + GateRef linkedTable = GetLinked(); + GateRef res = Circuit::NullGate(); + if constexpr (std::is_same_v) { + LinkedHashTableStubBuilder linkedHashTableStubBuilder(this, glue_); + res = linkedHashTableStubBuilder.Clear(linkedTable); + } else { + LinkedHashTableStubBuilder linkedHashTableStubBuilder(this, glue_); + res = linkedHashTableStubBuilder.Clear(linkedTable); + } + + Label exception(env); + Label noException(env); + Branch(TaggedIsException(res), &exception, &noException); + Bind(&noException); + SetLinked(res); + Jump(exit); + Bind(&exception); + *result = res; + Jump(exit); +} + +template void BuiltinsCollectionStubBuilder::Clear(Variable *result, Label *exit, Label *slowPath); +template void BuiltinsCollectionStubBuilder::Clear(Variable *result, Label *exit, Label *slowPath); + +template +void BuiltinsCollectionStubBuilder::CreateIterator(Variable *result, + Label *exit, Label *slowPath, GateRef kind) +{ + auto env = GetEnvironment(); + Label entry(env); + Label thisCollectionObj(env); + // check target obj + CheckCollectionObj(&thisCollectionObj, slowPath); + + Bind(&thisCollectionObj); + NewObjectStubBuilder newBuilder(this); + newBuilder.SetGlue(glue_); + if constexpr (std::is_same_v) { + newBuilder.CreateJSCollectionIterator(result, exit, thisValue_, kind); + } else { + newBuilder.CreateJSCollectionIterator(result, exit, thisValue_, kind); + } +} + +template +void BuiltinsCollectionStubBuilder::Values(Variable *result, Label *exit, Label *slowPath) +{ + GateRef kind = Int32(static_cast(IterationKind::VALUE)); + CreateIterator(result, exit, slowPath, kind); +} + +template void BuiltinsCollectionStubBuilder::Values(Variable *result, Label *exit, Label *slowPath); +template void BuiltinsCollectionStubBuilder::Values(Variable *result, Label *exit, Label *slowPath); + +template +void BuiltinsCollectionStubBuilder::Entries(Variable *result, Label *exit, Label *slowPath) +{ + GateRef kind = Int32(static_cast(IterationKind::KEY_AND_VALUE)); + CreateIterator(result, exit, slowPath, kind); +} + +template void BuiltinsCollectionStubBuilder::Entries(Variable *result, Label *exit, Label *slowPath); +template void BuiltinsCollectionStubBuilder::Entries(Variable *result, Label *exit, Label *slowPath); + +template +void BuiltinsCollectionStubBuilder::Keys(Variable *result, Label *exit, Label *slowPath) +{ + GateRef kind = Int32(static_cast(IterationKind::KEY)); + CreateIterator(result, exit, slowPath, kind); +} + +template void BuiltinsCollectionStubBuilder::Keys(Variable *result, Label *exit, Label *slowPath); + +template +void BuiltinsCollectionStubBuilder::ForEach(Variable *result, Label *exit, Label *slowPath) +{ + auto env = GetEnvironment(); + Label thisCollectionObj(env); + // check target obj + CheckCollectionObj(&thisCollectionObj, slowPath); + + Bind(&thisCollectionObj); + GateRef callbackFnHandle = GetCallArg0(numArgs_); + Label callable(env); + // check heap obj + Label heapObj(env); + Branch(TaggedIsHeapObject(callbackFnHandle), &heapObj, slowPath); + Bind(&heapObj); + Branch(IsCallable(callbackFnHandle), &callable, slowPath); + Bind(&callable); + + GateRef linkedTable = GetLinked(); + GateRef res = Circuit::NullGate(); + if constexpr (std::is_same_v) { + LinkedHashTableStubBuilder linkedHashTableStubBuilder(this, glue_); + res = linkedHashTableStubBuilder.ForEach(thisValue_, linkedTable, numArgs_); + } else { + LinkedHashTableStubBuilder linkedHashTableStubBuilder(this, glue_); + res = linkedHashTableStubBuilder.ForEach(thisValue_, linkedTable, numArgs_); + } + + Label exception(env); + Branch(TaggedIsException(res), &exception, exit); + Bind(&exception); + *result = res; + Jump(exit); +} + +template void BuiltinsCollectionStubBuilder::ForEach(Variable *result, Label *exit, Label *slowPath); +template void BuiltinsCollectionStubBuilder::ForEach(Variable *result, Label *exit, Label *slowPath); + +template +void BuiltinsCollectionStubBuilder::MapSetOrSetAdd( + Variable *result, Label *exit, Label *slowPath, bool isJsMapSet) +{ + auto env = GetEnvironment(); + Label thisCollectionObj(env); + // check target obj + CheckCollectionObj(&thisCollectionObj, slowPath); + Bind(&thisCollectionObj); + GateRef key = GetCallArg0(numArgs_); + // check key + Label keyNotHole(env); + Branch(TaggedIsHole(key), slowPath, &keyNotHole); + Bind(&keyNotHole); + GateRef value = isJsMapSet ? GetCallArg1(numArgs_) : key; + GateRef linkedTable = GetLinked(); + GateRef res = Circuit::NullGate(); + if constexpr (std::is_same_v) { + LinkedHashTableStubBuilder linkedHashTableStubBuilder(this, glue_); + res = linkedHashTableStubBuilder.Insert(linkedTable, key, value); + } else { + LinkedHashTableStubBuilder linkedHashTableStubBuilder(this, glue_); + res = linkedHashTableStubBuilder.Insert(linkedTable, key, value); + } + + SetLinked(res); + *result = thisValue_; + Jump(exit); +} + +template +void BuiltinsCollectionStubBuilder::Set(Variable *result, Label *exit, Label *slowPath) +{ + MapSetOrSetAdd(result, exit, slowPath, true); +} + +template void BuiltinsCollectionStubBuilder::Set(Variable *result, Label *exit, Label *slowPath); + +template +void BuiltinsCollectionStubBuilder::Add(Variable *result, Label *exit, Label *slowPath) +{ + MapSetOrSetAdd(result, exit, slowPath, false); +} + +template void BuiltinsCollectionStubBuilder::Add(Variable *result, Label *exit, Label *slowPath); + +template +void BuiltinsCollectionStubBuilder::Delete(Variable *result, Label *exit, Label *slowPath) +{ + auto env = GetEnvironment(); + Label thisCollectionObj(env); + // check target obj + CheckCollectionObj(&thisCollectionObj, slowPath); + + Bind(&thisCollectionObj); + GateRef key = GetCallArg0(numArgs_); + GateRef linkedTable = GetLinked(); + GateRef res = Circuit::NullGate(); + if constexpr (std::is_same_v) { + LinkedHashTableStubBuilder linkedHashTableStubBuilder(this, glue_); + res = linkedHashTableStubBuilder.Delete(linkedTable, key); + } else { + LinkedHashTableStubBuilder linkedHashTableStubBuilder(this, glue_); + res = linkedHashTableStubBuilder.Delete(linkedTable, key); + } + *result = res; + Jump(exit); +} + +template void BuiltinsCollectionStubBuilder::Delete(Variable *result, Label *exit, Label *slowPath); +template void BuiltinsCollectionStubBuilder::Delete(Variable *result, Label *exit, Label *slowPath); + +template +void BuiltinsCollectionStubBuilder::Has(Variable *result, Label *exit, Label *slowPath) +{ + auto env = GetEnvironment(); + Label thisCollectionObj(env); + // check target obj + CheckCollectionObj(&thisCollectionObj, slowPath); + + Bind(&thisCollectionObj); + GateRef key = GetCallArg0(numArgs_); + GateRef linkedTable = GetLinked(); + GateRef res = Circuit::NullGate(); + if constexpr (std::is_same_v) { + LinkedHashTableStubBuilder linkedHashTableStubBuilder(this, glue_); + res = linkedHashTableStubBuilder.Has(linkedTable, key); + } else { + LinkedHashTableStubBuilder linkedHashTableStubBuilder(this, glue_); + res = linkedHashTableStubBuilder.Has(linkedTable, key); + } + *result = res; + Jump(exit); +} + +template void BuiltinsCollectionStubBuilder::Has(Variable *result, Label *exit, Label *slowPath); +template void BuiltinsCollectionStubBuilder::Has(Variable *result, Label *exit, Label *slowPath); +} // namespace panda::ecmascript::kungfu diff --git a/ecmascript/compiler/builtins/builtins_collection_stub_builder.h b/ecmascript/compiler/builtins/builtins_collection_stub_builder.h new file mode 100644 index 0000000000000000000000000000000000000000..36298475e7b365a828cad7c18687400042993009 --- /dev/null +++ b/ecmascript/compiler/builtins/builtins_collection_stub_builder.h @@ -0,0 +1,76 @@ +/* + * 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_COMPILER_BUILTINS_COLLECTION_STUB_BUILDER_H +#define ECMASCRIPT_COMPILER_BUILTINS_COLLECTION_STUB_BUILDER_H +#include "ecmascript/compiler/stub_builder-inl.h" +#include "ecmascript/compiler/builtins/linked_hashtable_stub_builder.h" + +namespace panda::ecmascript::kungfu { +template +class BuiltinsCollectionStubBuilder : public BuiltinsStubBuilder { +public: + explicit BuiltinsCollectionStubBuilder(BuiltinsStubBuilder *parent, GateRef glue, GateRef thisValue, + GateRef numArgs) : BuiltinsStubBuilder(parent), glue_(glue), thisValue_(thisValue), numArgs_(numArgs) {} + ~BuiltinsCollectionStubBuilder() override = default; + NO_MOVE_SEMANTIC(BuiltinsCollectionStubBuilder); + NO_COPY_SEMANTIC(BuiltinsCollectionStubBuilder); + void GenerateCircuit() override {} + + void Clear(Variable *result, Label *exit, Label *slowPath); + void Values(Variable *result, Label *exit, Label *slowPath); + void Entries(Variable *result, Label *exit, Label *slowPath); + void Keys(Variable *result, Label *exit, Label *slowPath); + void ForEach(Variable *result, Label *exit, Label *slowPath); + void Set(Variable *result, Label *exit, Label *slowPath); + void Add(Variable *result, Label *exit, Label *slowPath); + void Delete(Variable *result, Label *exit, Label *slowPath); + void Has(Variable *result, Label *exit, Label *slowPath); + +private: + // check target obj + void CheckCollectionObj(Label *exit, Label *slowPath); + void CreateIterator(Variable *result, Label *exit, Label *slowPath, GateRef iterationKind); + void MapSetOrSetAdd(Variable *result, Label *exit, Label *slowPath, bool isJsMapSet); + + GateRef GetLinkedOffset() + { + int32_t linkedTableOffset = 0; + if constexpr (std::is_same_v) { + linkedTableOffset = CollectionType::LINKED_MAP_OFFSET; + } else { + linkedTableOffset = CollectionType::LINKED_SET_OFFSET; + } + return IntPtr(linkedTableOffset); + } + + GateRef GetLinked() + { + GateRef linkedTableOffset = GetLinkedOffset(); + return Load(VariableType::JS_ANY(), thisValue_, linkedTableOffset); + } + + void SetLinked(GateRef newTable) + { + GateRef linkedTableOffset = GetLinkedOffset(); + Store(VariableType::JS_ANY(), glue_, thisValue_, linkedTableOffset, newTable); + } + + GateRef glue_; + GateRef thisValue_; + GateRef numArgs_; +}; +} // namespace panda::ecmascript::kungfu +#endif // ECMASCRIPT_COMPILER_BUILTINS_COLLECTION_STUB_BUILDER_H \ No newline at end of file diff --git a/ecmascript/compiler/builtins/builtins_function_stub_builder.cpp b/ecmascript/compiler/builtins/builtins_function_stub_builder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..98dabbcadc691e5d113e8fde9b6e91e0ad9b2dea --- /dev/null +++ b/ecmascript/compiler/builtins/builtins_function_stub_builder.cpp @@ -0,0 +1,219 @@ +/* + * 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/compiler/builtins/builtins_function_stub_builder.h" + +#include "ecmascript/compiler/builtins/builtins_object_stub_builder.h" +#include "ecmascript/compiler/stub_builder-inl.h" +#include "ecmascript/js_arguments.h" + +namespace panda::ecmascript::kungfu { + +void BuiltinsFunctionStubBuilder::Apply(GateRef glue, GateRef thisValue, + GateRef numArgs, Variable* res, Label *exit, Label *slowPath) +{ + auto env = GetEnvironment(); + Label targetIsCallable(env); + Label targetIsUndefined(env); + Label targetNotUndefined(env); + Label isHeapObject(env); + //1. If IsCallable(func) is false, throw a TypeError exception + Branch(TaggedIsHeapObject(thisValue), &isHeapObject, slowPath); + Bind(&isHeapObject); + { + Branch(IsCallable(thisValue), &targetIsCallable, slowPath); + Bind(&targetIsCallable); + { + GateRef thisArg = GetCallArg0(numArgs); + GateRef arrayObj = GetCallArg1(numArgs); + // 2. If argArray is null or undefined, then + Branch(TaggedIsUndefined(arrayObj), &targetIsUndefined, &targetNotUndefined); + Bind(&targetIsUndefined); + { + // a. Return Call(func, thisArg). + res->WriteVariable(JSCallDispatch(glue, thisValue, Int32(0), 0, Circuit::NullGate(), + JSCallMode::CALL_GETTER, { thisArg })); + Jump(exit); + } + Bind(&targetNotUndefined); + { + // 3. Let argList be CreateListFromArrayLike(argArray). + GateRef elements = BuildArgumentsListFastElements(glue, arrayObj); + Label targetIsHole(env); + Label targetNotHole(env); + Branch(TaggedIsHole(elements), &targetIsHole, &targetNotHole); + Bind(&targetIsHole); + { + BuiltinsObjectStubBuilder objectStubBuilder(this); + GateRef argList = objectStubBuilder.CreateListFromArrayLike(glue, arrayObj); + // 4. ReturnIfAbrupt(argList). + Label isPendingException(env); + Label noPendingException(env); + Branch(HasPendingException(glue), &isPendingException, &noPendingException); + Bind(&isPendingException); + { + Jump(slowPath); + } + Bind(&noPendingException); + { + GateRef argsLength = GetLengthOfTaggedArray(argList); + GateRef argv = PtrAdd(argList, IntPtr(TaggedArray::DATA_OFFSET)); + res->WriteVariable(JSCallDispatch(glue, thisValue, argsLength, 0, Circuit::NullGate(), + JSCallMode::CALL_THIS_ARGV_WITH_RETURN, { argsLength, argv, thisArg })); + Jump(exit); + } + } + Bind(&targetNotHole); + { + // 6. Return Call(func, thisArg, argList). + Label taggedIsStableJsArg(env); + Label taggedNotStableJsArg(env); + Branch(IsStableJSArguments(glue, arrayObj), &taggedIsStableJsArg, &taggedNotStableJsArg); + Bind(&taggedIsStableJsArg); + { + GateRef hClass = LoadHClass(arrayObj); + GateRef PropertyInlinedPropsOffset = IntPtr(JSArguments::LENGTH_INLINE_PROPERTY_INDEX); + GateRef result = GetPropertyInlinedProps(arrayObj, hClass, PropertyInlinedPropsOffset); + GateRef length = TaggedGetInt(result); + GateRef argsLength = MakeArgListWithHole(glue, elements, length); + GateRef elementArgv = PtrAdd(elements, IntPtr(TaggedArray::DATA_OFFSET)); + res->WriteVariable(JSCallDispatch(glue, thisValue, argsLength, 0, Circuit::NullGate(), + JSCallMode::CALL_THIS_ARGV_WITH_RETURN, { argsLength, elementArgv, thisArg })); + Jump(exit); + } + Bind(&taggedNotStableJsArg); + { + GateRef length = GetArrayLength(arrayObj); + GateRef argsLength = MakeArgListWithHole(glue, elements, length); + GateRef elementArgv = PtrAdd(elements, IntPtr(TaggedArray::DATA_OFFSET)); + res->WriteVariable(JSCallDispatch(glue, thisValue, argsLength, 0, Circuit::NullGate(), + JSCallMode::CALL_THIS_ARGV_WITH_RETURN, { argsLength, elementArgv, thisArg })); + Jump(exit); + } + } + } + } + } +} + +// return elements +GateRef BuiltinsFunctionStubBuilder::BuildArgumentsListFastElements(GateRef glue, GateRef arrayObj) +{ + auto env = GetEnvironment(); + Label subentry(env); + env->SubCfgEntry(&subentry); + DEFVARIABLE(res, VariableType::JS_ANY(), Hole()); + Label exit(env); + Label hasStableElements(env); + Label targetIsStableJSArguments(env); + Label targetNotStableJSArguments(env); + Label targetIsInt(env); + Label hClassEqual(env); + Label targetIsStableJSArray(env); + Label targetNotStableJSArray(env); + + Branch(HasStableElements(glue, arrayObj), &hasStableElements, &exit); + Bind(&hasStableElements); + { + Branch(IsStableJSArguments(glue, arrayObj), &targetIsStableJSArguments, &targetNotStableJSArguments); + Bind(&targetIsStableJSArguments); + { + GateRef hClass = LoadHClass(arrayObj); + GateRef glueGlobalEnvOffset = IntPtr(JSThread::GlueData::GetGlueGlobalEnvOffset(env->Is32Bit())); + GateRef glueGlobalEnv = Load(VariableType::NATIVE_POINTER(), glue, glueGlobalEnvOffset); + GateRef argmentsClass = GetGlobalEnvValue(VariableType::JS_ANY(), glueGlobalEnv, + GlobalEnv::ARGUMENTS_CLASS); + Branch(Int64Equal(hClass, argmentsClass), &hClassEqual, &exit); + Bind(&hClassEqual); + { + GateRef PropertyInlinedPropsOffset = IntPtr(JSArguments::LENGTH_INLINE_PROPERTY_INDEX); + GateRef result = GetPropertyInlinedProps(arrayObj, hClass, PropertyInlinedPropsOffset); + Branch(TaggedIsInt(result), &targetIsInt, &exit); + Bind(&targetIsInt); + { + res = GetElementsArray(arrayObj); + Jump(&exit); + } + } + } + Bind(&targetNotStableJSArguments); + { + Branch(IsStableJSArray(glue, arrayObj), &targetIsStableJSArray, &targetNotStableJSArray); + Bind(&targetIsStableJSArray); + { + res = GetElementsArray(arrayObj); + Jump(&exit); + } + Bind(&targetNotStableJSArray); + { + FatalPrint(glue, { Int32(GET_MESSAGE_STRING_ID(ThisBranchIsUnreachable)) }); + Jump(&exit); + } + } + } + Bind(&exit); + auto ret = *res; + env->SubCfgExit(); + return ret; +} + +GateRef BuiltinsFunctionStubBuilder::MakeArgListWithHole(GateRef glue, GateRef argv, GateRef length) +{ + auto env = GetEnvironment(); + Label subentry(env); + env->SubCfgEntry(&subentry); + DEFVARIABLE(res, VariableType::INT32(), length); + DEFVARIABLE(i, VariableType::INT32(), Int32(0)); + Label exit(env); + + GateRef argsLength = GetLengthOfTaggedArray(argv); + + Label lengthGreaterThanArgsLength(env); + Label lengthLessThanArgsLength(env); + Branch(Int32GreaterThan(length, argsLength), &lengthGreaterThanArgsLength, &lengthLessThanArgsLength); + Bind(&lengthGreaterThanArgsLength); + { + res = argsLength; + Jump(&lengthLessThanArgsLength); + } + Bind(&lengthLessThanArgsLength); + { + Label loopHead(env); + Label loopEnd(env); + Label targetIsHole(env); + Label targetNotHole(env); + Branch(Int32UnsignedLessThan(*i, *res), &loopHead, &exit); + LoopBegin(&loopHead); + { + GateRef value = GetValueFromTaggedArray(argv, *i); + Branch(TaggedIsHole(value), &targetIsHole, &targetNotHole); + Bind(&targetIsHole); + { + SetValueToTaggedArray(VariableType::JS_ANY(), glue, argv, *i, Undefined()); + Jump(&targetNotHole); + } + Bind(&targetNotHole); + i = Int32Add(*i, Int32(1)); + Branch(Int32UnsignedLessThan(*i, *res), &loopEnd, &exit); + } + Bind(&loopEnd); + LoopEnd(&loopHead); + } + Bind(&exit); + auto ret = *res; + env->SubCfgExit(); + return ret; +} +} // namespace panda::ecmascript::kungfu diff --git a/ecmascript/compiler/builtins/builtins_function_stub_builder.h b/ecmascript/compiler/builtins/builtins_function_stub_builder.h new file mode 100644 index 0000000000000000000000000000000000000000..babea4fce2d3eb34dcf7356f5abc220a76717c9c --- /dev/null +++ b/ecmascript/compiler/builtins/builtins_function_stub_builder.h @@ -0,0 +1,35 @@ +/* + * 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_COMPILER_BUILTINS_FUNCTION_STUB_BUILDER_H +#define ECMASCRIPT_COMPILER_BUILTINS_FUNCTION_STUB_BUILDER_H +#include "ecmascript/compiler/builtins/builtins_stubs.h" + +namespace panda::ecmascript::kungfu { +class BuiltinsFunctionStubBuilder : public BuiltinsStubBuilder { +public: + explicit BuiltinsFunctionStubBuilder(StubBuilder *parent) + : BuiltinsStubBuilder(parent) {} + ~BuiltinsFunctionStubBuilder() override = default; + NO_MOVE_SEMANTIC(BuiltinsFunctionStubBuilder); + NO_COPY_SEMANTIC(BuiltinsFunctionStubBuilder); + void GenerateCircuit() override {} + void Apply(GateRef glue, GateRef thisValue, GateRef numArgs, Variable* res, Label *exit, Label *slowPath); + GateRef BuildArgumentsListFastElements(GateRef glue, GateRef arrayObj); +private: + GateRef MakeArgListWithHole(GateRef glue, GateRef argv, GateRef length); +}; +} // namespace panda::ecmascript::kungfu +#endif // ECMASCRIPT_COMPILER_BUILTINS_FUNCTION_STUB_BUILDER_H \ No newline at end of file diff --git a/ecmascript/compiler/builtins/builtins_object_stub_builder.cpp b/ecmascript/compiler/builtins/builtins_object_stub_builder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..71234fea49a38a97a6a785ddf7d6d1e65255fc27 --- /dev/null +++ b/ecmascript/compiler/builtins/builtins_object_stub_builder.cpp @@ -0,0 +1,195 @@ +/* + * 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/compiler/builtins/builtins_object_stub_builder.h" + +#include "ecmascript/compiler/new_object_stub_builder.h" +#include "ecmascript/compiler/stub_builder-inl.h" +#include "ecmascript/compiler/typed_array_stub_builder.h" +#include "ecmascript/js_arguments.h" +#include "ecmascript/message_string.h" +namespace panda::ecmascript::kungfu { +GateRef BuiltinsObjectStubBuilder::CreateListFromArrayLike(GateRef glue, GateRef arrayObj) +{ + auto env = GetEnvironment(); + Label entry(env); + env->SubCfgEntry(&entry); + DEFVARIABLE(res, VariableType::JS_ANY(), Hole()); + DEFVARIABLE(index, VariableType::INT32(), Int32(0)); + Label exit(env); + + // 3. If Type(obj) is Object, throw a TypeError exception. + Label targetIsHeapObject(env); + Label targetIsEcmaObject(env); + Label targetNotEcmaObject(env); + Branch(TaggedIsHeapObject(arrayObj), &targetIsHeapObject, &targetNotEcmaObject); + Bind(&targetIsHeapObject); + Branch(TaggedObjectIsEcmaObject(arrayObj), &targetIsEcmaObject, &targetNotEcmaObject); + Bind(&targetNotEcmaObject); + { + GateRef taggedId = Int32(GET_MESSAGE_STRING_ID(TargetTypeNotObject)); + CallRuntime(glue, RTSTUB_ID(ThrowTypeError), { IntToTaggedInt(taggedId) }); + Jump(&exit); + } + Bind(&targetIsEcmaObject); + { + // 4. Let len be ToLength(Get(obj, "length")). + GateRef lengthString = GetGlobalConstantValue(VariableType::JS_POINTER(), glue, + ConstantIndex::LENGTH_STRING_INDEX); + GateRef value = FastGetPropertyByName(glue, arrayObj, lengthString, ProfileOperation()); + GateRef number = ToLength(glue, value); + // 5. ReturnIfAbrupt(len). + Label isPendingException1(env); + Label noPendingException1(env); + Branch(HasPendingException(glue), &isPendingException1, &noPendingException1); + Bind(&isPendingException1); + { + Jump(&exit); + } + Bind(&noPendingException1); + { + Label indexInRange(env); + Label indexOutRange(env); + + GateRef doubleLen = GetDoubleOfTNumber(number); + Branch(DoubleGreaterThan(doubleLen, Double(JSObject::MAX_ELEMENT_INDEX)), &indexOutRange, &indexInRange); + Bind(&indexOutRange); + { + GateRef taggedId = Int32(GET_MESSAGE_STRING_ID(LenGreaterThanMax)); + CallRuntime(glue, RTSTUB_ID(ThrowTypeError), { IntToTaggedInt(taggedId) }); + Jump(&exit); + } + Bind(&indexInRange); + { + GateRef int32Len = DoubleToInt(glue, doubleLen); + // 6. Let list be an empty List. + NewObjectStubBuilder newBuilder(this); + GateRef array = newBuilder.NewTaggedArray(glue, int32Len); + Label targetIsTypeArray(env); + Label targetNotTypeArray(env); + Branch(IsTypedArray(arrayObj), &targetIsTypeArray, &targetNotTypeArray); + Bind(&targetIsTypeArray); + { + TypedArrayStubBuilder arrayStubBuilder(this); + arrayStubBuilder.FastCopyElementToArray(glue, arrayObj, array); + // c. ReturnIfAbrupt(next). + Label isPendingException2(env); + Label noPendingException2(env); + Branch(HasPendingException(glue), &isPendingException2, &noPendingException2); + Bind(&isPendingException2); + { + Jump(&exit); + } + Bind(&noPendingException2); + { + res = array; + Jump(&exit); + } + } + Bind(&targetNotTypeArray); + // 8. Repeat while index < len + Label loopHead(env); + Label loopEnd(env); + Label afterLoop(env); + Label isPendingException3(env); + Label noPendingException3(env); + Label storeValue(env); + Jump(&loopHead); + LoopBegin(&loopHead); + { + Branch(Int32UnsignedLessThan(*index, int32Len), &storeValue, &afterLoop); + Bind(&storeValue); + { + GateRef next = FastGetPropertyByIndex(glue, arrayObj, *index, ProfileOperation()); + // c. ReturnIfAbrupt(next). + Branch(HasPendingException(glue), &isPendingException3, &noPendingException3); + Bind(&isPendingException3); + { + Jump(&exit); + } + Bind(&noPendingException3); + SetValueToTaggedArray(VariableType::JS_ANY(), glue, array, *index, next); + index = Int32Add(*index, Int32(1)); + Jump(&loopEnd); + } + } + Bind(&loopEnd); + LoopEnd(&loopHead); + Bind(&afterLoop); + { + res = array; + Jump(&exit); + } + } + } + } + Bind(&exit); + GateRef ret = *res; + env->SubCfgExit(); + return ret; +} + +void BuiltinsObjectStubBuilder::ToString(Variable *result, Label *exit, Label *slowPath) +{ + auto env = GetEnvironment(); + Label ecmaObj(env); + // undefined + Label undefined(env); + Label checknull(env); + Branch(TaggedIsUndefined(thisValue_), &undefined, &checknull); + Bind(&undefined); + { + *result = GetGlobalConstantValue(VariableType::JS_POINTER(), glue_, ConstantIndex::UNDEFINED_TO_STRING_INDEX); + Jump(exit); + } + // null + Bind(&checknull); + Label null(env); + Label checkObject(env); + Branch(TaggedIsUndefined(thisValue_), &null, &checkObject); + Bind(&null); + { + *result = GetGlobalConstantValue(VariableType::JS_POINTER(), glue_, ConstantIndex::NULL_TO_STRING_INDEX); + Jump(exit); + } + + Bind(&checkObject); + Branch(IsEcmaObject(thisValue_), &ecmaObj, slowPath); + Bind(&ecmaObj); + { + GateRef glueGlobalEnvOffset = IntPtr(JSThread::GlueData::GetGlueGlobalEnvOffset(env->Is32Bit())); + GateRef glueGlobalEnv = Load(VariableType::NATIVE_POINTER(), glue_, glueGlobalEnvOffset); + GateRef toStringTagSymbol = GetGlobalEnvValue(VariableType::JS_ANY(), glueGlobalEnv, + GlobalEnv::TOSTRINGTAG_SYMBOL_INDEX); + GateRef tag = FastGetPropertyByName(glue_, thisValue_, toStringTagSymbol, ProfileOperation()); + + Label defaultToString(env); + Branch(TaggedIsString(tag), slowPath, &defaultToString); + Bind(&defaultToString); + { + // default object + Label objectTag(env); + Branch(IsJSObjectType(thisValue_, JSType::JS_OBJECT), &objectTag, slowPath); + Bind(&objectTag); + { + // [object object] + *result = GetGlobalConstantValue(VariableType::JS_POINTER(), glue_, + ConstantIndex::OBJECT_TO_STRING_INDEX); + Jump(exit); + } + } + } +} +} // namespace panda::ecmascript::kungfu diff --git a/ecmascript/compiler/builtins/builtins_object_stub_builder.h b/ecmascript/compiler/builtins/builtins_object_stub_builder.h new file mode 100644 index 0000000000000000000000000000000000000000..a5874a98b3f676bde213019bd46487c45f563e74 --- /dev/null +++ b/ecmascript/compiler/builtins/builtins_object_stub_builder.h @@ -0,0 +1,39 @@ +/* + * 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_COMPILER_BUILTINS_OBJECT_STUB_BUILDER_H +#define ECMASCRIPT_COMPILER_BUILTINS_OBJECT_STUB_BUILDER_H +#include "ecmascript/compiler/builtins/builtins_stubs.h" + +namespace panda::ecmascript::kungfu { +class BuiltinsObjectStubBuilder : public BuiltinsStubBuilder { +public: + explicit BuiltinsObjectStubBuilder(StubBuilder *parent) + : BuiltinsStubBuilder(parent) {} + BuiltinsObjectStubBuilder(BuiltinsStubBuilder *parent, GateRef glue, GateRef thisValue) + : BuiltinsStubBuilder(parent), glue_(glue), thisValue_(thisValue) {} + ~BuiltinsObjectStubBuilder() override = default; + NO_MOVE_SEMANTIC(BuiltinsObjectStubBuilder); + NO_COPY_SEMANTIC(BuiltinsObjectStubBuilder); + void GenerateCircuit() override {} + GateRef CreateListFromArrayLike(GateRef glue, GateRef arrayObj); + void ToString(Variable *result, Label *exit, Label *slowPath); + +private: + GateRef glue_; + GateRef thisValue_; +}; +} // namespace panda::ecmascript::kungfu +#endif // ECMASCRIPT_COMPILER_BUILTINS_OBJECT_STUB_BUILDER_H \ No newline at end of file diff --git a/ecmascript/compiler/builtins/builtins_string_stub_builder.cpp b/ecmascript/compiler/builtins/builtins_string_stub_builder.cpp index b9acfb50097885b62cb76d600f01383f773eef7b..078848541576fe0d9dc0678a4e1db8072952801b 100644 --- a/ecmascript/compiler/builtins/builtins_string_stub_builder.cpp +++ b/ecmascript/compiler/builtins/builtins_string_stub_builder.cpp @@ -19,7 +19,475 @@ #include "ecmascript/compiler/new_object_stub_builder.h" namespace panda::ecmascript::kungfu { -GateRef BuiltinsStringStubBuilder::StringAt(GateRef obj, GateRef index) +void BuiltinsStringStubBuilder::FromCharCode(GateRef glue, [[maybe_unused]] GateRef thisValue, + GateRef numArgs, Variable* res, Label *exit, Label *slowPath) +{ + auto env = GetEnvironment(); + DEFVARIABLE(value, VariableType::INT16(), Int16(0)); + Label lengthIsZero(env); + Label lengthNotZero(env); + Label lengthIsOne(env); + Label canBeCompress(env); + Label isInt(env); + Label notInt(env); + Label newObj(env); + Label canNotBeCompress(env); + Label isPendingException(env); + Label noPendingException(env); + Branch(Int64Equal(IntPtr(0), numArgs), &lengthIsZero, &lengthNotZero); + Bind(&lengthIsZero); + res->WriteVariable(GetGlobalConstantValue( + VariableType::JS_POINTER(), glue, ConstantIndex::EMPTY_STRING_OBJECT_INDEX)); + Jump(exit); + Bind(&lengthNotZero); + { + Branch(Int64Equal(IntPtr(1), numArgs), &lengthIsOne, slowPath); + Bind(&lengthIsOne); + { + GateRef codePointTag = GetCallArg0(numArgs); + GateRef codePointValue = ToNumber(glue, codePointTag); + Branch(HasPendingException(glue), &isPendingException, &noPendingException); + Bind(&isPendingException); + { + res->WriteVariable(Exception()); + Jump(exit); + } + Bind(&noPendingException); + { + Branch(TaggedIsInt(codePointValue), &isInt, ¬Int); + Bind(&isInt); + { + value = TruncInt32ToInt16(GetInt32OfTInt(codePointValue)); + Jump(&newObj); + } + Bind(¬Int); + { + value = TruncInt32ToInt16(DoubleToInt(glue, GetDoubleOfTDouble(codePointValue), base::INT16_BITS)); + Jump(&newObj); + } + Bind(&newObj); + Branch(IsASCIICharacter(ZExtInt16ToInt32(*value)), &canBeCompress, &canNotBeCompress); + NewObjectStubBuilder newBuilder(this); + newBuilder.SetParameters(glue, 0); + Bind(&canBeCompress); + { + Label afterNew(env); + newBuilder.AllocLineStringObject(res, &afterNew, Int32(1), true); + Bind(&afterNew); + { + GateRef dst = ChangeStringTaggedPointerToInt64( + PtrAdd(res->ReadVariable(), IntPtr(LineEcmaString::DATA_OFFSET))); + Store(VariableType::INT8(), glue, dst, IntPtr(0), TruncInt16ToInt8(*value)); + Jump(exit); + } + } + Bind(&canNotBeCompress); + { + Label afterNew1(env); + newBuilder.AllocLineStringObject(res, &afterNew1, Int32(1), false); + Bind(&afterNew1); + { + GateRef dst = ChangeStringTaggedPointerToInt64( + PtrAdd(res->ReadVariable(), IntPtr(LineEcmaString::DATA_OFFSET))); + Store(VariableType::INT16(), glue, dst, IntPtr(0), *value); + Jump(exit); + } + } + } + } + } +} + +void BuiltinsStringStubBuilder::CharAt(GateRef glue, GateRef thisValue, GateRef numArgs, + Variable* res, Label *exit, Label *slowPath) +{ + auto env = GetEnvironment(); + DEFVARIABLE(pos, VariableType::INT32(), Int32(0)); + Label objNotUndefinedAndNull(env); + Label isString(env); + Label next(env); + Label posTagNotUndefined(env); + Label posTagIsInt(env); + Label posTagNotInt(env); + Label posNotGreaterLen(env); + Label posGreaterLen(env); + Label posNotLessZero(env); + Label posTagIsDouble(env); + Label thisIsHeapobject(env); + Label flattenFastPath(env); + + Branch(TaggedIsUndefinedOrNull(thisValue), slowPath, &objNotUndefinedAndNull); + Bind(&objNotUndefinedAndNull); + { + Branch(TaggedIsHeapObject(thisValue), &thisIsHeapobject, slowPath); + Bind(&thisIsHeapobject); + Branch(IsString(thisValue), &isString, slowPath); + Bind(&isString); + { + FlatStringStubBuilder thisFlat(this); + thisFlat.FlattenString(glue, thisValue, &flattenFastPath); + Bind(&flattenFastPath); + GateRef thisLen = GetLengthFromString(thisValue); + Branch(Int64GreaterThanOrEqual(IntPtr(0), numArgs), &next, &posTagNotUndefined); + Bind(&posTagNotUndefined); + { + GateRef posTag = GetCallArg0(numArgs); + Branch(TaggedIsInt(posTag), &posTagIsInt, &posTagNotInt); + Bind(&posTagIsInt); + pos = GetInt32OfTInt(posTag); + Jump(&next); + Bind(&posTagNotInt); + Branch(TaggedIsDouble(posTag), &posTagIsDouble, slowPath); + Bind(&posTagIsDouble); + pos = DoubleToInt(glue, GetDoubleOfTDouble(posTag)); + Jump(&next); + } + Bind(&next); + { + Branch(Int32GreaterThanOrEqual(*pos, thisLen), &posGreaterLen, &posNotGreaterLen); + Bind(&posNotGreaterLen); + { + Branch(Int32LessThan(*pos, Int32(0)), &posGreaterLen, &posNotLessZero); + Bind(&posNotLessZero); + { + StringInfoGateRef stringInfoGate(&thisFlat); + res->WriteVariable(CreateFromEcmaString(glue, *pos, stringInfoGate)); + Jump(exit); + } + } + Bind(&posGreaterLen); + { + res->WriteVariable(GetGlobalConstantValue( + VariableType::JS_POINTER(), glue, ConstantIndex::EMPTY_STRING_OBJECT_INDEX)); + Jump(exit); + } + } + } + } +} + +void BuiltinsStringStubBuilder::CharCodeAt(GateRef glue, GateRef thisValue, GateRef numArgs, + Variable* res, Label *exit, Label *slowPath) +{ + auto env = GetEnvironment(); + DEFVARIABLE(pos, VariableType::INT32(), Int32(0)); + Label objNotUndefinedAndNull(env); + Label isString(env); + Label next(env); + Label posTagNotUndefined(env); + Label posTagIsInt(env); + Label posTagNotInt(env); + Label posNotGreaterLen(env); + Label posNotLessZero(env); + Label posTagIsDouble(env); + Label thisIsHeapobject(env); + Label flattenFastPath(env); + + Branch(TaggedIsUndefinedOrNull(thisValue), slowPath, &objNotUndefinedAndNull); + Bind(&objNotUndefinedAndNull); + { + Branch(TaggedIsHeapObject(thisValue), &thisIsHeapobject, slowPath); + Bind(&thisIsHeapobject); + Branch(IsString(thisValue), &isString, slowPath); + Bind(&isString); + { + FlatStringStubBuilder thisFlat(this); + thisFlat.FlattenString(glue, thisValue, &flattenFastPath); + Bind(&flattenFastPath); + GateRef thisLen = GetLengthFromString(thisValue); + Branch(Int64GreaterThanOrEqual(IntPtr(0), numArgs), &next, &posTagNotUndefined); + Bind(&posTagNotUndefined); + { + GateRef posTag = GetCallArg0(numArgs); + Branch(TaggedIsInt(posTag), &posTagIsInt, &posTagNotInt); + Bind(&posTagIsInt); + pos = GetInt32OfTInt(posTag); + Jump(&next); + Bind(&posTagNotInt); + Branch(TaggedIsDouble(posTag), &posTagIsDouble, slowPath); + Bind(&posTagIsDouble); + pos = DoubleToInt(glue, GetDoubleOfTDouble(posTag)); + Jump(&next); + } + Bind(&next); + { + Branch(Int32GreaterThanOrEqual(*pos, thisLen), exit, &posNotGreaterLen); + Bind(&posNotGreaterLen); + { + Branch(Int32LessThan(*pos, Int32(0)), exit, &posNotLessZero); + Bind(&posNotLessZero); + { + StringInfoGateRef stringInfoGate(&thisFlat); + res->WriteVariable(IntToTaggedPtr(StringAt(stringInfoGate, *pos))); + Jump(exit); + } + } + } + } + } +} + +void BuiltinsStringStubBuilder::IndexOf(GateRef glue, GateRef thisValue, GateRef numArgs, + Variable* res, Label *exit, Label *slowPath) +{ + auto env = GetEnvironment(); + DEFVARIABLE(pos, VariableType::INT32(), Int32(0)); + + Label objNotUndefinedAndNull(env); + Label isString(env); + Label isSearchString(env); + Label next(env); + Label resPosGreaterZero(env); + Label searchTagIsHeapObject(env); + Label posTagNotUndefined(env); + Label posTagIsInt(env); + Label posTagNotInt(env); + Label posTagIsDouble(env); + Label nextCount(env); + Label posNotLessThanLen(env); + Label thisIsHeapobject(env); + Label flattenFastPath(env); + Label flattenFastPath1(env); + + Branch(TaggedIsUndefinedOrNull(thisValue), slowPath, &objNotUndefinedAndNull); + Bind(&objNotUndefinedAndNull); + { + Branch(TaggedIsHeapObject(thisValue), &thisIsHeapobject, slowPath); + Bind(&thisIsHeapobject); + Branch(IsString(thisValue), &isString, slowPath); + Bind(&isString); + { + GateRef searchTag = GetCallArg0(numArgs); + Branch(TaggedIsHeapObject(searchTag), &searchTagIsHeapObject, slowPath); + Bind(&searchTagIsHeapObject); + Branch(IsString(searchTag), &isSearchString, slowPath); + Bind(&isSearchString); + { + GateRef thisLen = GetLengthFromString(thisValue); + Branch(Int64GreaterThanOrEqual(IntPtr(1), numArgs), &next, &posTagNotUndefined); + Bind(&posTagNotUndefined); + { + GateRef posTag = GetCallArg1(numArgs); + Branch(TaggedIsInt(posTag), &posTagIsInt, &posTagNotInt); + Bind(&posTagIsInt); + pos = GetInt32OfTInt(posTag); + Jump(&next); + Bind(&posTagNotInt); + Branch(TaggedIsDouble(posTag), &posTagIsDouble, slowPath); + Bind(&posTagIsDouble); + pos = DoubleToInt(glue, GetDoubleOfTDouble(posTag)); + Jump(&next); + } + Bind(&next); + { + Label posGreaterThanZero(env); + Label posNotGreaterThanZero(env); + Branch(Int32GreaterThan(*pos, Int32(0)), &posGreaterThanZero, &posNotGreaterThanZero); + Bind(&posNotGreaterThanZero); + { + pos = Int32(0); + Jump(&nextCount); + } + Bind(&posGreaterThanZero); + { + Branch(Int32LessThanOrEqual(*pos, thisLen), &nextCount, &posNotLessThanLen); + Bind(&posNotLessThanLen); + { + pos = thisLen; + Jump(&nextCount); + } + } + Bind(&nextCount); + { + FlatStringStubBuilder thisFlat(this); + thisFlat.FlattenString(glue, thisValue, &flattenFastPath); + Bind(&flattenFastPath); + FlatStringStubBuilder searchFlat(this); + searchFlat.FlattenString(glue, searchTag, &flattenFastPath1); + Bind(&flattenFastPath1); + StringInfoGateRef thisStringInfoGate(&thisFlat); + StringInfoGateRef searchStringInfoGate(&searchFlat); + GateRef resPos = StringIndexOf(thisStringInfoGate, searchStringInfoGate, *pos); + Branch(Int32GreaterThanOrEqual(resPos, Int32(0)), &resPosGreaterZero, exit); + Bind(&resPosGreaterZero); + { + Label resPosLessZero(env); + Branch(Int32LessThanOrEqual(resPos, thisLen), &resPosLessZero, exit); + Bind(&resPosLessZero); + { + res->WriteVariable(IntToTaggedPtr(resPos)); + Jump(exit); + } + } + } + } + } + } + } +} + +void BuiltinsStringStubBuilder::Substring(GateRef glue, GateRef thisValue, GateRef numArgs, + Variable* res, Label *exit, Label *slowPath) +{ + auto env = GetEnvironment(); + DEFVARIABLE(start, VariableType::INT32(), Int32(0)); + DEFVARIABLE(end, VariableType::INT32(), Int32(0)); + DEFVARIABLE(from, VariableType::INT32(), Int32(0)); + DEFVARIABLE(to, VariableType::INT32(), Int32(0)); + + Label objNotUndefinedAndNull(env); + Label isString(env); + Label isSearchString(env); + Label countStart(env); + Label endTagIsUndefined(env); + Label startNotGreatZero(env); + Label countEnd(env); + Label endNotGreatZero(env); + Label countFrom(env); + Label countRes(env); + Label startTagNotUndefined(env); + Label posTagIsInt(env); + Label posTagNotInt(env); + Label posTagIsDouble(env); + Label endTagNotUndefined(env); + Label endTagIsInt(env); + Label endTagNotInt(env); + Label endTagIsDouble(env); + Label endGreatZero(env); + Label endGreatLen(env); + Label startGreatZero(env); + Label startGreatEnd(env); + Label startNotGreatEnd(env); + Label thisIsHeapobject(env); + Label flattenFastPath(env); + Label sliceString(env); + Label fastSubstring(env); + + Branch(TaggedIsUndefinedOrNull(thisValue), slowPath, &objNotUndefinedAndNull); + Bind(&objNotUndefinedAndNull); + { + Branch(TaggedIsHeapObject(thisValue), &thisIsHeapobject, slowPath); + Bind(&thisIsHeapobject); + Branch(IsString(thisValue), &isString, slowPath); + Bind(&isString); + { + Label next(env); + GateRef thisLen = GetLengthFromString(thisValue); + Branch(Int64GreaterThanOrEqual(IntPtr(0), numArgs), &next, &startTagNotUndefined); + Bind(&startTagNotUndefined); + { + GateRef startTag = GetCallArg0(numArgs); + Branch(TaggedIsInt(startTag), &posTagIsInt, &posTagNotInt); + Bind(&posTagIsInt); + start = GetInt32OfTInt(startTag); + Jump(&next); + Bind(&posTagNotInt); + Branch(TaggedIsDouble(startTag), &posTagIsDouble, slowPath); + Bind(&posTagIsDouble); + start = DoubleToInt(glue, GetDoubleOfTDouble(startTag)); + Jump(&next); + } + Bind(&next); + { + Branch(Int64GreaterThanOrEqual(IntPtr(1), numArgs), &endTagIsUndefined, &endTagNotUndefined); + Bind(&endTagIsUndefined); + { + end = thisLen; + Jump(&countStart); + } + Bind(&endTagNotUndefined); + { + GateRef endTag = GetCallArg1(numArgs); + Branch(TaggedIsInt(endTag), &endTagIsInt, &endTagNotInt); + Bind(&endTagIsInt); + end = GetInt32OfTInt(endTag); + Jump(&countStart); + Bind(&endTagNotInt); + Branch(TaggedIsDouble(endTag), &endTagIsDouble, slowPath); + Bind(&endTagIsDouble); + end = DoubleToInt(glue, GetDoubleOfTDouble(endTag)); + Jump(&countStart); + } + } + Bind(&countStart); + { + Label startGreatLen(env); + Branch(Int32GreaterThan(*start, Int32(0)), &startGreatZero, &startNotGreatZero); + Bind(&startNotGreatZero); + { + start = Int32(0); + Jump(&countEnd); + } + Bind(&startGreatZero); + { + Branch(Int32GreaterThan(*start, thisLen), &startGreatLen, &countEnd); + Bind(&startGreatLen); + { + start = thisLen; + Jump(&countEnd); + } + } + } + Bind(&countEnd); + { + Branch(Int32GreaterThan(*end, Int32(0)), &endGreatZero, &endNotGreatZero); + Bind(&endNotGreatZero); + { + end = Int32(0); + Jump(&countFrom); + } + Bind(&endGreatZero); + { + Branch(Int32GreaterThan(*end, thisLen), &endGreatLen, &countFrom); + Bind(&endGreatLen); + { + end = thisLen; + Jump(&countFrom); + } + } + } + Bind(&countFrom); + { + Branch(Int32GreaterThan(*start, *end), &startGreatEnd, &startNotGreatEnd); + Bind(&startGreatEnd); + { + from = *end; + to = *start; + Jump(&countRes); + } + Bind(&startNotGreatEnd); + { + from = *start; + to = *end; + Jump(&countRes); + } + } + Bind(&countRes); + { + GateRef len = Int32Sub(*to, *from); + FlatStringStubBuilder thisFlat(this); + thisFlat.FlattenString(glue, thisValue, &flattenFastPath); + Bind(&flattenFastPath); + { + Branch(Int32GreaterThanOrEqual(len, Int32(SlicedString::MIN_SLICED_ECMASTRING_LENGTH)), + &sliceString, &fastSubstring); + Bind(&sliceString); + { + NewObjectStubBuilder newBuilder(this); + newBuilder.SetParameters(glue, 0); + newBuilder.AllocSlicedStringObject(res, exit, *from, len, &thisFlat); + } + Bind(&fastSubstring); + StringInfoGateRef stringInfoGate(&thisFlat); + res->WriteVariable(FastSubString(glue, thisValue, *from, len, stringInfoGate)); + Jump(exit); + } + } + } + } +} + +GateRef BuiltinsStringStubBuilder::StringAt(const StringInfoGateRef &stringInfoGate, GateRef index) { auto env = GetEnvironment(); Label entry(env); @@ -32,8 +500,8 @@ GateRef BuiltinsStringStubBuilder::StringAt(GateRef obj, GateRef index) Label doIntOp(env); Label leftIsNumber(env); Label rightIsNumber(env); - GateRef dataUtf16 = GetNormalStringData(obj); - Branch(IsUtf16String(obj), &isUtf16, &isUtf8); + GateRef dataUtf16 = GetNormalStringData(stringInfoGate); + Branch(IsUtf16String(stringInfoGate.GetString()), &isUtf16, &isUtf8); Bind(&isUtf16); { result = ZExtInt16ToInt32(Load(VariableType::INT16(), PtrAdd(dataUtf16, @@ -52,7 +520,169 @@ GateRef BuiltinsStringStubBuilder::StringAt(GateRef obj, GateRef index) return ret; } -GateRef BuiltinsStringStubBuilder::CreateFromEcmaString(GateRef glue, GateRef obj, GateRef index) +GateRef BuiltinsStringStubBuilder::GetSingleCharCodeByIndex(GateRef str, GateRef index) +{ + // Note: This method cannot handle treestring. + auto env = GetEnvironment(); + Label entry(env); + env->SubCfgEntry(&entry); + DEFVARIABLE(result, VariableType::INT32(), Int32(0)); + + Label isConstantString(env); + Label lineStringCheck(env); + Label isLineString(env); + Label slicedStringCheck(env); + Label isSlicedString(env); + Label exit(env); + + Branch(IsConstantString(str), &isConstantString, &lineStringCheck); + Bind(&isConstantString); + { + result = GetSingleCharCodeFromConstantString(str, index); + Jump(&exit); + } + Bind(&lineStringCheck); + Branch(IsLineString(str), &isLineString, &slicedStringCheck); + Bind(&isLineString); + { + result = GetSingleCharCodeFromLineString(str, index); + Jump(&exit); + } + Bind(&slicedStringCheck); + Branch(IsSlicedString(str), &isSlicedString, &exit); + Bind(&isSlicedString); + { + result = GetSingleCharCodeFromSlicedString(str, index); + Jump(&exit); + } + + Bind(&exit); + auto ret = *result; + env->SubCfgExit(); + return ret; +} + +GateRef BuiltinsStringStubBuilder::GetSingleCharCodeFromConstantString(GateRef str, GateRef index) +{ + auto env = GetEnvironment(); + Label entry(env); + env->SubCfgEntry(&entry); + GateRef offset = ChangeStringTaggedPointerToInt64(PtrAdd(str, IntPtr(ConstantString::CONSTANT_DATA_OFFSET))); + GateRef dataAddr = Load(VariableType::NATIVE_POINTER(), offset, IntPtr(0)); + GateRef result = ZExtInt8ToInt32(Load(VariableType::INT8(), PtrAdd(dataAddr, + PtrMul(ZExtInt32ToPtr(index), IntPtr(sizeof(uint8_t)))))); + env->SubCfgExit(); + return result; +} + +GateRef BuiltinsStringStubBuilder::GetSingleCharCodeFromLineString(GateRef str, GateRef index) +{ + auto env = GetEnvironment(); + Label entry(env); + env->SubCfgEntry(&entry); + DEFVARIABLE(result, VariableType::INT32(), Int32(0)); + GateRef dataAddr = ChangeStringTaggedPointerToInt64(PtrAdd(str, IntPtr(LineEcmaString::DATA_OFFSET))); + Label isUtf16(env); + Label isUtf8(env); + Label exit(env); + Branch(IsUtf16String(str), &isUtf16, &isUtf8); + Bind(&isUtf16); + { + result = ZExtInt16ToInt32(Load(VariableType::INT16(), PtrAdd(dataAddr, + PtrMul(ZExtInt32ToPtr(index), IntPtr(sizeof(uint16_t)))))); + Jump(&exit); + } + Bind(&isUtf8); + { + result = ZExtInt8ToInt32(Load(VariableType::INT8(), PtrAdd(dataAddr, + PtrMul(ZExtInt32ToPtr(index), IntPtr(sizeof(uint8_t)))))); + Jump(&exit); + } + Bind(&exit); + auto ret = *result; + env->SubCfgExit(); + return ret; +} + +GateRef BuiltinsStringStubBuilder::GetSingleCharCodeFromSlicedString(GateRef str, GateRef index) +{ + auto env = GetEnvironment(); + Label entry(env); + env->SubCfgEntry(&entry); + DEFVARIABLE(result, VariableType::INT32(), Int32(0)); + Label isLineString(env); + Label notLineString(env); + Label exit(env); + + GateRef parent = Load(VariableType::JS_POINTER(), str, IntPtr(SlicedString::PARENT_OFFSET)); + GateRef startIndex = Load(VariableType::INT32(), str, IntPtr(SlicedString::STARTINDEX_OFFSET)); + Branch(IsLineString(parent), &isLineString, ¬LineString); + Bind(&isLineString); + { + result = GetSingleCharCodeFromLineString(parent, Int32Add(startIndex, index)); + Jump(&exit); + } + Bind(¬LineString); + { + result = GetSingleCharCodeFromConstantString(parent, Int32Add(startIndex, index)); + Jump(&exit); + } + Bind(&exit); + auto ret = *result; + env->SubCfgExit(); + return ret; +} + +GateRef BuiltinsStringStubBuilder::CreateStringBySingleCharCode(GateRef glue, GateRef charCode) +{ + auto env = GetEnvironment(); + Label entry(env); + env->SubCfgEntry(&entry); + DEFVARIABLE(result, VariableType::JS_POINTER(), Hole()); + + NewObjectStubBuilder newBuilder(this); + newBuilder.SetParameters(glue, 0); + + Label exit(env); + Label utf8(env); + Label utf16(env); + Label afterNew(env); + GateRef canStoreAsUtf8 = IsASCIICharacter(charCode); + Branch(canStoreAsUtf8, &utf8, &utf16); + Bind(&utf8); + { + newBuilder.AllocLineStringObject(&result, &afterNew, Int32(1), true); + } + Bind(&utf16); + { + newBuilder.AllocLineStringObject(&result, &afterNew, Int32(1), false); + } + Bind(&afterNew); + { + Label isUtf8Copy(env); + Label isUtf16Copy(env); + GateRef dst = ChangeStringTaggedPointerToInt64(PtrAdd(*result, IntPtr(LineEcmaString::DATA_OFFSET))); + Branch(canStoreAsUtf8, &isUtf8Copy, &isUtf16Copy); + Bind(&isUtf8Copy); + { + Store(VariableType::INT8(), glue, dst, IntPtr(0), TruncInt32ToInt8(charCode)); + Jump(&exit); + } + Bind(&isUtf16Copy); + { + Store(VariableType::INT16(), glue, dst, IntPtr(0), TruncInt32ToInt16(charCode)); + Jump(&exit); + } + } + + Bind(&exit); + auto ret = *result; + env->SubCfgExit(); + return ret; +} + +GateRef BuiltinsStringStubBuilder::CreateFromEcmaString(GateRef glue, GateRef index, + const StringInfoGateRef &stringInfoGate) { auto env = GetEnvironment(); Label entry(env); @@ -65,8 +695,8 @@ GateRef BuiltinsStringStubBuilder::CreateFromEcmaString(GateRef glue, GateRef ob Label isUtf16(env); Label isUtf8(env); Label allocString(env); - GateRef dataUtf = GetNormalStringData(obj); - Branch(IsUtf16String(obj), &isUtf16, &isUtf8); + GateRef dataUtf = GetNormalStringData(stringInfoGate); + Branch(IsUtf16String(stringInfoGate.GetString()), &isUtf16, &isUtf8); Bind(&isUtf16); { GateRef dataAddr = PtrAdd(dataUtf, PtrMul(ZExtInt32ToPtr(index), IntPtr(sizeof(uint16_t)))); @@ -101,7 +731,7 @@ GateRef BuiltinsStringStubBuilder::CreateFromEcmaString(GateRef glue, GateRef ob { Label isUtf8Copy(env); Label isUtf16Copy(env); - GateRef dst = PtrAdd(*result, IntPtr(LineEcmaString::DATA_OFFSET)); + GateRef dst = ChangeStringTaggedPointerToInt64(PtrAdd(*result, IntPtr(LineEcmaString::DATA_OFFSET))); Branch(*canBeCompressed, &isUtf8Copy, &isUtf16Copy); Bind(&isUtf8Copy); { @@ -121,7 +751,8 @@ GateRef BuiltinsStringStubBuilder::CreateFromEcmaString(GateRef glue, GateRef ob return ret; } -GateRef BuiltinsStringStubBuilder::FastSubString(GateRef glue, GateRef thisValue, GateRef from, GateRef len) +GateRef BuiltinsStringStubBuilder::FastSubString(GateRef glue, GateRef thisValue, GateRef from, + GateRef len, const StringInfoGateRef &stringInfoGate) { auto env = GetEnvironment(); Label entry(env); @@ -148,7 +779,7 @@ GateRef BuiltinsStringStubBuilder::FastSubString(GateRef glue, GateRef thisValue Branch(Int32Equal(from, Int32(0)), &fromEqualZero, &next); Bind(&fromEqualZero); { - GateRef thisLen = GetLengthFromString(thisValue); + GateRef thisLen = stringInfoGate.GetLength(); Branch(Int32Equal(len, thisLen), &exit, &next); } Bind(&next); @@ -156,12 +787,12 @@ GateRef BuiltinsStringStubBuilder::FastSubString(GateRef glue, GateRef thisValue Branch(IsUtf8String(thisValue), &isUtf8, &isUtf16); Bind(&isUtf8); { - result = FastSubUtf8String(glue, thisValue, from, len); + result = FastSubUtf8String(glue, from, len, stringInfoGate); Jump(&exit); } Bind(&isUtf16); { - result = FastSubUtf16String(glue, thisValue, from, len); + result = FastSubUtf16String(glue, from, len, stringInfoGate); Jump(&exit); } } @@ -172,7 +803,8 @@ GateRef BuiltinsStringStubBuilder::FastSubString(GateRef glue, GateRef thisValue return ret; } -GateRef BuiltinsStringStubBuilder::FastSubUtf8String(GateRef glue, GateRef thisValue, GateRef from, GateRef len) +GateRef BuiltinsStringStubBuilder::FastSubUtf8String(GateRef glue, GateRef from, GateRef len, + const StringInfoGateRef &stringInfoGate) { auto env = GetEnvironment(); Label entry(env); @@ -186,8 +818,8 @@ GateRef BuiltinsStringStubBuilder::FastSubUtf8String(GateRef glue, GateRef thisV newBuilder.AllocLineStringObject(&result, &afterNew, len, true); Bind(&afterNew); { - GateRef dst = PtrAdd(*result, IntPtr(LineEcmaString::DATA_OFFSET)); - GateRef source = PtrAdd(GetNormalStringData(thisValue), ZExtInt32ToPtr(from)); + GateRef dst = ChangeStringTaggedPointerToInt64(PtrAdd(*result, IntPtr(LineEcmaString::DATA_OFFSET))); + GateRef source = PtrAdd(GetNormalStringData(stringInfoGate), ZExtInt32ToPtr(from)); CopyChars(glue, dst, source, len, IntPtr(sizeof(uint8_t)), VariableType::INT8()); Jump(&exit); } @@ -197,7 +829,8 @@ GateRef BuiltinsStringStubBuilder::FastSubUtf8String(GateRef glue, GateRef thisV return ret; } -GateRef BuiltinsStringStubBuilder::FastSubUtf16String(GateRef glue, GateRef thisValue, GateRef from, GateRef len) +GateRef BuiltinsStringStubBuilder::FastSubUtf16String(GateRef glue, GateRef from, GateRef len, + const StringInfoGateRef &stringInfoGate) { auto env = GetEnvironment(); Label entry(env); @@ -211,7 +844,7 @@ GateRef BuiltinsStringStubBuilder::FastSubUtf16String(GateRef glue, GateRef this Label isUtf16Next(env); GateRef fromOffset = PtrMul(ZExtInt32ToPtr(from), IntPtr(sizeof(uint16_t) / sizeof(uint8_t))); - GateRef source = PtrAdd(GetNormalStringData(thisValue), fromOffset); + GateRef source = PtrAdd(GetNormalStringData(stringInfoGate), fromOffset); GateRef canBeCompressed = CanBeCompressed(source, len, true); NewObjectStubBuilder newBuilder(this); newBuilder.SetParameters(glue, 0); @@ -227,12 +860,12 @@ GateRef BuiltinsStringStubBuilder::FastSubUtf16String(GateRef glue, GateRef this } Bind(&afterNew); { - GateRef source1 = PtrAdd(GetNormalStringData(thisValue), fromOffset); - GateRef dst = PtrAdd(*result, IntPtr(LineEcmaString::DATA_OFFSET)); + GateRef source1 = PtrAdd(GetNormalStringData(stringInfoGate), fromOffset); + GateRef dst = ChangeStringTaggedPointerToInt64(PtrAdd(*result, IntPtr(LineEcmaString::DATA_OFFSET))); Branch(canBeCompressed, &isUtf8Next, &isUtf16Next); Bind(&isUtf8Next); { - CopyUtf16AsUtf8(glue, source1, dst, len); + CopyUtf16AsUtf8(glue, dst, source1, len); Jump(&exit); } Bind(&isUtf16Next); @@ -253,8 +886,8 @@ void BuiltinsStringStubBuilder::CopyChars(GateRef glue, GateRef dst, GateRef sou auto env = GetEnvironment(); Label entry(env); env->SubCfgEntry(&entry); - DEFVARIABLE(dstTmp, VariableType::JS_ANY(), dst); - DEFVARIABLE(sourceTmp, VariableType::JS_ANY(), source); + DEFVARIABLE(dstTmp, VariableType::NATIVE_POINTER(), dst); + DEFVARIABLE(sourceTmp, VariableType::NATIVE_POINTER(), source); DEFVARIABLE(len, VariableType::INT32(), sourceLength); Label loopHead(env); Label loopEnd(env); @@ -327,14 +960,52 @@ GateRef BuiltinsStringStubBuilder::CanBeCompressed(GateRef data, GateRef len, bo return ret; } -void BuiltinsStringStubBuilder::CopyUtf16AsUtf8(GateRef glue, GateRef src, GateRef dst, +// source is utf8, dst is utf16 +void BuiltinsStringStubBuilder::CopyUtf8AsUtf16(GateRef glue, GateRef dst, GateRef src, + GateRef sourceLength) +{ + auto env = GetEnvironment(); + Label entry(env); + env->SubCfgEntry(&entry); + DEFVARIABLE(dstTmp, VariableType::NATIVE_POINTER(), dst); + DEFVARIABLE(sourceTmp, VariableType::NATIVE_POINTER(), src); + DEFVARIABLE(len, VariableType::INT32(), sourceLength); + Label loopHead(env); + Label loopEnd(env); + Label next(env); + Label exit(env); + Jump(&loopHead); + LoopBegin(&loopHead); + { + Branch(Int32GreaterThan(*len, Int32(0)), &next, &exit); + Bind(&next); + { + len = Int32Sub(*len, Int32(1)); + GateRef i = Load(VariableType::INT8(), *sourceTmp); + Store(VariableType::INT16(), glue, *dstTmp, IntPtr(0), ZExtInt8ToInt16(i)); + Jump(&loopEnd); + } + } + + Bind(&loopEnd); + sourceTmp = PtrAdd(*sourceTmp, IntPtr(sizeof(uint8_t))); + dstTmp = PtrAdd(*dstTmp, IntPtr(sizeof(uint16_t))); + LoopEnd(&loopHead); + + Bind(&exit); + env->SubCfgExit(); + return; +} + +// source is utf16, dst is utf8 +void BuiltinsStringStubBuilder::CopyUtf16AsUtf8(GateRef glue, GateRef dst, GateRef src, GateRef sourceLength) { auto env = GetEnvironment(); Label entry(env); env->SubCfgEntry(&entry); - DEFVARIABLE(dstTmp, VariableType::JS_ANY(), dst); - DEFVARIABLE(sourceTmp, VariableType::JS_ANY(), src); + DEFVARIABLE(dstTmp, VariableType::NATIVE_POINTER(), dst); + DEFVARIABLE(sourceTmp, VariableType::NATIVE_POINTER(), src); DEFVARIABLE(len, VariableType::INT32(), sourceLength); Label loopHead(env); Label loopEnd(env); @@ -371,7 +1042,7 @@ GateRef BuiltinsStringStubBuilder::GetUtf16Data(GateRef stringData, GateRef inde GateRef BuiltinsStringStubBuilder::IsASCIICharacter(GateRef data) { - return Int32LessThan(Int32Sub(data, Int32(1)), Int32(base::utf_helper::UTF8_1B_MAX)); + return Int32UnsignedLessThan(Int32Sub(data, Int32(1)), Int32(base::utf_helper::UTF8_1B_MAX)); } GateRef BuiltinsStringStubBuilder::GetUtf8Data(GateRef stringData, GateRef index) @@ -503,7 +1174,19 @@ GateRef BuiltinsStringStubBuilder::StringIndexOf(GateRef lhsData, bool lhsIsUtf8 return ret; } -GateRef BuiltinsStringStubBuilder::StringIndexOf(GateRef lhs, GateRef rhs, GateRef pos) + +void BuiltinsStringStubBuilder::StoreParent(GateRef glue, GateRef object, GateRef parent) +{ + Store(VariableType::JS_POINTER(), glue, object, IntPtr(SlicedString::PARENT_OFFSET), parent); +} + +void BuiltinsStringStubBuilder::StoreStartIndex(GateRef glue, GateRef object, GateRef startIndex) +{ + Store(VariableType::INT32(), glue, object, IntPtr(SlicedString::STARTINDEX_OFFSET), startIndex); +} + +GateRef BuiltinsStringStubBuilder::StringIndexOf(const StringInfoGateRef &lStringInfoGate, + const StringInfoGateRef &rStringInfoGate, GateRef pos) { auto env = GetEnvironment(); Label entry(env); @@ -521,8 +1204,8 @@ GateRef BuiltinsStringStubBuilder::StringIndexOf(GateRef lhs, GateRef rhs, GateR Label rhsIsUtf16(env); Label posRMaxNotGreaterLhs(env); - GateRef lhsCount = GetLengthFromString(lhs); - GateRef rhsCount = GetLengthFromString(rhs); + GateRef lhsCount = lStringInfoGate.GetLength(); + GateRef rhsCount = rStringInfoGate.GetLength(); Branch(Int32GreaterThan(pos, lhsCount), &exit, &nextCount); Bind(&nextCount); @@ -550,14 +1233,14 @@ GateRef BuiltinsStringStubBuilder::StringIndexOf(GateRef lhs, GateRef rhs, GateR GateRef posRMax = Int32Add(*posTag, rhsCount); Branch(Int32GreaterThan(posRMax, lhsCount), &exit, &posRMaxNotGreaterLhs); Bind(&posRMaxNotGreaterLhs); - GateRef rhsData = GetNormalStringData(rhs); - GateRef lhsData = GetNormalStringData(lhs); - Branch(IsUtf8String(rhs), &rhsIsUtf8, &rhsIsUtf16); + GateRef rhsData = GetNormalStringData(rStringInfoGate); + GateRef lhsData = GetNormalStringData(lStringInfoGate); + Branch(IsUtf8String(rStringInfoGate.GetString()), &rhsIsUtf8, &rhsIsUtf16); Bind(&rhsIsUtf8); { Label lhsIsUtf8(env); Label lhsIsUtf16(env); - Branch(IsUtf8String(lhs), &lhsIsUtf8, &lhsIsUtf16); + Branch(IsUtf8String(lStringInfoGate.GetString()), &lhsIsUtf8, &lhsIsUtf16); Bind(&lhsIsUtf8); { result = StringIndexOf(lhsData, true, rhsData, true, *posTag, max, rhsCount); @@ -573,7 +1256,7 @@ GateRef BuiltinsStringStubBuilder::StringIndexOf(GateRef lhs, GateRef rhs, GateR { Label lhsIsUtf8(env); Label lhsIsUtf16(env); - Branch(IsUtf8String(lhs), &lhsIsUtf8, &lhsIsUtf16); + Branch(IsUtf8String(lStringInfoGate.GetString()), &lhsIsUtf8, &lhsIsUtf16); Bind(&lhsIsUtf8); { result = StringIndexOf(lhsData, true, rhsData, false, *posTag, max, rhsCount); @@ -594,4 +1277,212 @@ GateRef BuiltinsStringStubBuilder::StringIndexOf(GateRef lhs, GateRef rhs, GateR env->SubCfgExit(); return ret; } + +void FlatStringStubBuilder::FlattenString(GateRef glue, GateRef str, Label *fastPath) +{ + auto env = GetEnvironment(); + Label notLineString(env); + Label exit(env); + length_ = GetLengthFromString(str); + Branch(BoolOr(IsLineString(str), IsConstantString(str)), &exit, ¬LineString); + Bind(¬LineString); + { + Label isTreeString(env); + Label notTreeString(env); + Label isSlicedString(env); + Branch(IsTreeString(str), &isTreeString, ¬TreeString); + Bind(&isTreeString); + { + Label isFlat(env); + Label notFlat(env); + Branch(TreeStringIsFlat(str), &isFlat, ¬Flat); + Bind(&isFlat); + { + flatString_.WriteVariable(GetFirstFromTreeString(str)); + Jump(fastPath); + } + Bind(¬Flat); + { + flatString_.WriteVariable(CallRuntime(glue, RTSTUB_ID(SlowFlattenString), { str })); + Jump(fastPath); + } + } + Bind(¬TreeString); + Branch(IsSlicedString(str), &isSlicedString, &exit); + Bind(&isSlicedString); + { + flatString_.WriteVariable(GetParentFromSlicedString(str)); + startIndex_.WriteVariable(GetStartIndexFromSlicedString(str)); + Jump(fastPath); + } + } + Bind(&exit); + { + flatString_.WriteVariable(str); + Jump(fastPath); + } +} + +GateRef BuiltinsStringStubBuilder::GetStringDataFromLineOrConstantString(GateRef str) +{ + auto env = GetEnvironment(); + Label entry(env); + env->SubCfgEntry(&entry); + Label exit(env); + Label isConstantString(env); + Label isLineString(env); + DEFVARIABLE(result, VariableType::NATIVE_POINTER(), IntPtr(0)); + Branch(IsConstantString(str), &isConstantString, &isLineString); + Bind(&isConstantString); + { + GateRef address = ChangeStringTaggedPointerToInt64(PtrAdd(str, IntPtr(ConstantString::CONSTANT_DATA_OFFSET))); + result = Load(VariableType::NATIVE_POINTER(), address, IntPtr(0)); + Jump(&exit); + } + Bind(&isLineString); + { + result = ChangeStringTaggedPointerToInt64(PtrAdd(str, IntPtr(LineEcmaString::DATA_OFFSET))); + Jump(&exit); + } + Bind(&exit); + auto ret = *result; + env->SubCfgExit(); + return ret; +} + +GateRef BuiltinsStringStubBuilder::StringConcat(GateRef glue, GateRef leftString, GateRef rightString) +{ + auto env = GetEnvironment(); + Label entry(env); + env->SubCfgEntry(&entry); + DEFVARIABLE(result, VariableType::JS_POINTER(), Undefined()); + Label exit(env); + Label equalZero(env); + Label notEqualZero(env); + + GateRef leftLength = GetLengthFromString(leftString); + GateRef rightLength = GetLengthFromString(rightString); + GateRef newLength = Int32Add(leftLength, rightLength); + Branch(Int32Equal(newLength, Int32(0)), &equalZero, ¬EqualZero); + Bind(&equalZero); + { + result = GetGlobalConstantValue( + VariableType::JS_POINTER(), glue, ConstantIndex::EMPTY_STRING_OBJECT_INDEX); + Jump(&exit); + } + Bind(¬EqualZero); + { + Label leftEqualZero(env); + Label leftNotEqualZero(env); + Label rightEqualZero(env); + Label rightNotEqualZero(env); + Label newLineString(env); + Label newTreeString(env); + Branch(Int32Equal(leftLength, Int32(0)), &leftEqualZero, &leftNotEqualZero); + Bind(&leftEqualZero); + { + result = rightString; + Jump(&exit); + } + Bind(&leftNotEqualZero); + Branch(Int32Equal(rightLength, Int32(0)), &rightEqualZero, &rightNotEqualZero); + Bind(&rightEqualZero); + { + result = leftString; + Jump(&exit); + } + Bind(&rightNotEqualZero); + { + GateRef leftIsUtf8 = IsUtf8String(leftString); + GateRef rightIsUtf8 = IsUtf8String(rightString); + GateRef canBeCompressed = BoolAnd(leftIsUtf8, rightIsUtf8); + NewObjectStubBuilder newBuilder(this); + newBuilder.SetParameters(glue, 0); + GateRef isTreeOrSlicedString = Int32LessThan(newLength, + Int32(std::min(TreeEcmaString::MIN_TREE_ECMASTRING_LENGTH, + SlicedString::MIN_SLICED_ECMASTRING_LENGTH))); + Branch(isTreeOrSlicedString, &newLineString, &newTreeString); + Bind(&newLineString); + { + Label isUtf8(env); + Label isUtf16(env); + Label isUtf8Next(env); + Label isUtf16Next(env); + Branch(canBeCompressed, &isUtf8, &isUtf16); + Bind(&isUtf8); + { + newBuilder.AllocLineStringObject(&result, &isUtf8Next, newLength, true); + } + Bind(&isUtf16); + { + newBuilder.AllocLineStringObject(&result, &isUtf16Next, newLength, false); + } + Bind(&isUtf8Next); + { + GateRef leftSource = GetStringDataFromLineOrConstantString(leftString); + GateRef rightSource = GetStringDataFromLineOrConstantString(rightString); + GateRef leftDst = ChangeStringTaggedPointerToInt64( + PtrAdd(*result, IntPtr(LineEcmaString::DATA_OFFSET))); + GateRef rightDst = ChangeStringTaggedPointerToInt64(PtrAdd(leftDst, ZExtInt32ToPtr(leftLength))); + CopyChars(glue, leftDst, leftSource, leftLength, IntPtr(sizeof(uint8_t)), VariableType::INT8()); + CopyChars(glue, rightDst, rightSource, rightLength, IntPtr(sizeof(uint8_t)), VariableType::INT8()); + Jump(&exit); + } + Bind(&isUtf16Next); + { + Label leftIsUtf8L(env); + Label leftIsUtf16L(env); + Label rightIsUtf8L(env); + Label rightIsUtf16L(env); + GateRef leftSource = GetStringDataFromLineOrConstantString(leftString); + GateRef rightSource = GetStringDataFromLineOrConstantString(rightString); + GateRef leftDst = ChangeStringTaggedPointerToInt64( + PtrAdd(*result, IntPtr(LineEcmaString::DATA_OFFSET))); + GateRef rightDst = ChangeStringTaggedPointerToInt64( + PtrAdd(leftDst, PtrMul(ZExtInt32ToPtr(leftLength), IntPtr(sizeof(uint16_t))))); + Branch(leftIsUtf8, &leftIsUtf8L, &leftIsUtf16L); + Bind(&leftIsUtf8L); + { + // left is utf8,right string must utf16 + CopyUtf8AsUtf16(glue, leftDst, leftSource, leftLength); + CopyChars(glue, rightDst, rightSource, rightLength, + IntPtr(sizeof(uint16_t)), VariableType::INT16()); + Jump(&exit); + } + Bind(&leftIsUtf16L); + { + CopyChars(glue, leftDst, leftSource, leftLength, + IntPtr(sizeof(uint16_t)), VariableType::INT16()); + Branch(rightIsUtf8, &rightIsUtf8L, &rightIsUtf16L); + Bind(&rightIsUtf8L); + CopyUtf8AsUtf16(glue, rightDst, rightSource, rightLength); + Jump(&exit); + Bind(&rightIsUtf16L); + CopyChars(glue, rightDst, rightSource, rightLength, + IntPtr(sizeof(uint16_t)), VariableType::INT16()); + Jump(&exit); + } + } + } + Bind(&newTreeString); + { + Label isUtf8(env); + Label isUtf16(env); + Branch(canBeCompressed, &isUtf8, &isUtf16); + Bind(&isUtf8); + { + newBuilder.AllocTreeStringObject(&result, &exit, leftString, rightString, newLength, true); + } + Bind(&isUtf16); + { + newBuilder.AllocTreeStringObject(&result, &exit, leftString, rightString, newLength, false); + } + } + } + } + Bind(&exit); + auto ret = *result; + env->SubCfgExit(); + return ret; +} } // namespace panda::ecmascript::kungfu diff --git a/ecmascript/compiler/builtins/builtins_string_stub_builder.h b/ecmascript/compiler/builtins/builtins_string_stub_builder.h index 471b83c1e74a47aea900c63c8dcaa900e37fa18c..972e458d76462e8d6355f78dbd296bfc3cf25354 100644 --- a/ecmascript/compiler/builtins/builtins_string_stub_builder.h +++ b/ecmascript/compiler/builtins/builtins_string_stub_builder.h @@ -18,30 +18,120 @@ #include "ecmascript/compiler/stub_builder-inl.h" namespace panda::ecmascript::kungfu { -class BuiltinsStringStubBuilder : public StubBuilder { +class FlatStringStubBuilder; +struct StringInfoGateRef; + +class BuiltinsStringStubBuilder : public BuiltinsStubBuilder { public: explicit BuiltinsStringStubBuilder(StubBuilder *parent) - : StubBuilder(parent) {} + : BuiltinsStubBuilder(parent) {} ~BuiltinsStringStubBuilder() override = default; NO_MOVE_SEMANTIC(BuiltinsStringStubBuilder); NO_COPY_SEMANTIC(BuiltinsStringStubBuilder); void GenerateCircuit() override {} - GateRef StringAt(GateRef obj, GateRef index); - GateRef FastSubString(GateRef glue, GateRef thisValue, GateRef from, GateRef len); - GateRef FastSubUtf8String(GateRef glue, GateRef thisValue, GateRef from, GateRef len); - GateRef FastSubUtf16String(GateRef glue, GateRef thisValue, GateRef from, GateRef len); + void FromCharCode(GateRef glue, GateRef thisValue, GateRef numArgs, Variable* res, Label *exit, Label *slowPath); + void CharAt(GateRef glue, GateRef thisValue, GateRef numArgs, Variable* res, Label *exit, Label *slowPath); + void CharCodeAt(GateRef glue, GateRef thisValue, GateRef numArgs, Variable* res, Label *exit, Label *slowPath); + void IndexOf(GateRef glue, GateRef thisValue, GateRef numArgs, Variable* res, Label *exit, Label *slowPath); + void Substring(GateRef glue, GateRef thisValue, GateRef numArgs, Variable* res, Label *exit, Label *slowPath); + + GateRef StringAt(const StringInfoGateRef &stringInfoGate, GateRef index); + GateRef FastSubString(GateRef glue, GateRef thisValue, GateRef from, GateRef len, + const StringInfoGateRef &stringInfoGate); + GateRef FastSubUtf8String(GateRef glue, GateRef from, GateRef len, const StringInfoGateRef &stringInfoGate); + GateRef FastSubUtf16String(GateRef glue, GateRef from, GateRef len, const StringInfoGateRef &stringInfoGate); void CopyChars(GateRef glue, GateRef dst, GateRef source, GateRef sourceLength, GateRef size, VariableType type); - void CopyUtf16AsUtf8(GateRef glue, GateRef src, GateRef dst, GateRef sourceLength); + void CopyUtf16AsUtf8(GateRef glue, GateRef dst, GateRef src, GateRef sourceLength); + void CopyUtf8AsUtf16(GateRef glue, GateRef dst, GateRef src, GateRef sourceLength); GateRef StringIndexOf(GateRef lhsData, bool lhsIsUtf8, GateRef rhsData, bool rhsIsUtf8, GateRef pos, GateRef max, GateRef rhsCount); - GateRef StringIndexOf(GateRef lhs, GateRef rhs, GateRef pos); - GateRef CreateFromEcmaString(GateRef glue, GateRef obj, GateRef index); + GateRef StringIndexOf(const StringInfoGateRef &lStringInfoGate, + const StringInfoGateRef &rStringInfoGate, GateRef pos); + GateRef GetSingleCharCodeByIndex(GateRef str, GateRef index); + GateRef CreateStringBySingleCharCode(GateRef glue, GateRef charCode); + GateRef CreateFromEcmaString(GateRef glue, GateRef index, const StringInfoGateRef &stringInfoGate); + GateRef StringConcat(GateRef glue, GateRef leftString, GateRef rightString); + void StoreParent(GateRef glue, GateRef object, GateRef parent); + void StoreStartIndex(GateRef glue, GateRef object, GateRef startIndex); private: + GateRef ChangeStringTaggedPointerToInt64(GateRef x) + { + return GetEnvironment()->GetBuilder()->ChangeTaggedPointerToInt64(x); + } + GateRef GetStringDataFromLineOrConstantString(GateRef str); GateRef CanBeCompressed(GateRef utf16Data, GateRef utf16Len, bool isUtf16); GateRef GetUtf16Data(GateRef stringData, GateRef index); GateRef IsASCIICharacter(GateRef data); GateRef GetUtf8Data(GateRef stringData, GateRef index); + GateRef GetSingleCharCodeFromConstantString(GateRef str, GateRef index); + GateRef GetSingleCharCodeFromLineString(GateRef str, GateRef index); + GateRef GetSingleCharCodeFromSlicedString(GateRef str, GateRef index); +}; + +class FlatStringStubBuilder : public StubBuilder { +public: + explicit FlatStringStubBuilder(StubBuilder *parent) + : StubBuilder(parent) {} + ~FlatStringStubBuilder() override = default; + NO_MOVE_SEMANTIC(FlatStringStubBuilder); + NO_COPY_SEMANTIC(FlatStringStubBuilder); + void GenerateCircuit() override {} + + void FlattenString(GateRef glue, GateRef str, Label *fastPath); + GateRef GetParentFromSlicedString(GateRef string) + { + GateRef offset = IntPtr(SlicedString::PARENT_OFFSET); + return Load(VariableType::JS_POINTER(), string, offset); + } + GateRef GetStartIndexFromSlicedString(GateRef string) + { + GateRef offset = IntPtr(SlicedString::STARTINDEX_OFFSET); + return Load(VariableType::INT32(), string, offset); + } + + GateRef GetFlatString() + { + return flatString_.ReadVariable(); + } + + GateRef GetStartIndex() + { + return startIndex_.ReadVariable(); + } + + GateRef GetLength() + { + return length_; + } + +private: + Variable flatString_ { GetEnvironment(), VariableType::JS_POINTER(), NextVariableId(), Undefined() }; + Variable startIndex_ { GetEnvironment(), VariableType::INT32(), NextVariableId(), Int32(0) }; + GateRef length_ { Circuit::NullGate() }; +}; + +struct StringInfoGateRef { + GateRef string_ { Circuit::NullGate() }; + GateRef startIndex_ { Circuit::NullGate() }; + GateRef length_ { Circuit::NullGate() }; + StringInfoGateRef(FlatStringStubBuilder *flatString) : string_(flatString->GetFlatString()), + startIndex_(flatString->GetStartIndex()), + length_(flatString->GetLength()) {} + GateRef GetString() const + { + return string_; + } + + GateRef GetStartIndex() const + { + return startIndex_; + } + + GateRef GetLength() const + { + return length_; + } }; } // namespace panda::ecmascript::kungfu #endif // ECMASCRIPT_COMPILER_BUILTINS_STRING_STUB_BUILDER_H \ No newline at end of file diff --git a/ecmascript/compiler/builtins/builtins_stubs.cpp b/ecmascript/compiler/builtins/builtins_stubs.cpp index cd7719479064840652f8d3d24bdab3ce40577803..3d9d4ab6a6cc78e2111ef80eebcbcf692930d18d 100644 --- a/ecmascript/compiler/builtins/builtins_stubs.cpp +++ b/ecmascript/compiler/builtins/builtins_stubs.cpp @@ -16,14 +16,19 @@ #include "ecmascript/compiler/builtins/builtins_stubs.h" #include "ecmascript/base/number_helper.h" +#include "ecmascript/compiler/builtins/builtins_array_stub_builder.h" #include "ecmascript/compiler/builtins/builtins_call_signature.h" +#include "ecmascript/compiler/builtins/builtins_function_stub_builder.h" #include "ecmascript/compiler/builtins/builtins_string_stub_builder.h" #include "ecmascript/compiler/builtins/containers_vector_stub_builder.h" #include "ecmascript/compiler/builtins/containers_stub_builder.h" +#include "ecmascript/compiler/builtins/builtins_collection_stub_builder.h" +#include "ecmascript/compiler/builtins/builtins_object_stub_builder.h" #include "ecmascript/compiler/interpreter_stub-inl.h" #include "ecmascript/compiler/llvm_ir_builder.h" #include "ecmascript/compiler/new_object_stub_builder.h" #include "ecmascript/compiler/stub_builder-inl.h" +#include "ecmascript/compiler/stub_builder.h" #include "ecmascript/compiler/variable_type.h" #include "ecmascript/js_date.h" #include "ecmascript/js_primitive_ref.h" @@ -142,650 +147,48 @@ GateRef BuiltinsStubBuilder::CallSlowPath(GateRef nativeCode, GateRef glue, Gate return ret; } -DECLARE_BUILTINS(CharCodeAt) -{ - auto env = GetEnvironment(); - DEFVARIABLE(res, VariableType::JS_ANY(), DoubleToTaggedDoublePtr(Double(base::NAN_VALUE))); - DEFVARIABLE(pos, VariableType::INT32(), Int32(0)); - - Label objNotUndefinedAndNull(env); - Label isString(env); - Label slowPath(env); - Label next(env); - Label posTagNotUndefined(env); - Label posTagIsInt(env); - Label posTagNotInt(env); - Label posNotGreaterLen(env); - Label posNotLessZero(env); - Label exit(env); - Label posTagIsDouble(env); - Label thisIsHeapobject(env); - Label flattenFastPath(env); - - Branch(TaggedIsUndefinedOrNull(thisValue), &slowPath, &objNotUndefinedAndNull); - Bind(&objNotUndefinedAndNull); - { - Branch(TaggedIsHeapObject(thisValue), &thisIsHeapobject, &slowPath); - Bind(&thisIsHeapobject); - Branch(IsString(thisValue), &isString, &slowPath); - Bind(&isString); - { - DEFVARIABLE(thisFlat, VariableType::JS_POINTER(), thisValue); - FlattenString(thisValue, &thisFlat, &flattenFastPath, &slowPath); - Bind(&flattenFastPath); - GateRef thisLen = GetLengthFromString(*thisFlat); - Branch(Int64GreaterThanOrEqual(IntPtr(0), numArgs), &next, &posTagNotUndefined); - Bind(&posTagNotUndefined); - { - GateRef posTag = GetCallArg0(numArgs); - Branch(TaggedIsInt(posTag), &posTagIsInt, &posTagNotInt); - Bind(&posTagIsInt); - pos = GetInt32OfTInt(posTag); - Jump(&next); - Bind(&posTagNotInt); - Branch(TaggedIsDouble(posTag), &posTagIsDouble, &slowPath); - Bind(&posTagIsDouble); - pos = DoubleToInt(glue, GetDoubleOfTDouble(posTag)); - Jump(&next); - } - Bind(&next); - { - Branch(Int32GreaterThanOrEqual(*pos, thisLen), &exit, &posNotGreaterLen); - Bind(&posNotGreaterLen); - { - Branch(Int32LessThan(*pos, Int32(0)), &exit, &posNotLessZero); - Bind(&posNotLessZero); - { - BuiltinsStringStubBuilder stringBuilder(this); - res = IntToTaggedPtr(stringBuilder.StringAt(*thisFlat, *pos)); - Jump(&exit); - } - } - } - } - } - Bind(&slowPath); - { - auto name = BuiltinsStubCSigns::GetName(BUILTINS_STUB_ID(CharCodeAt)); - res = CallSlowPath(nativeCode, glue, thisValue, numArgs, func, newTarget); - Jump(&exit); - } - Bind(&exit); - Return(*res); -} - -DECLARE_BUILTINS(IndexOf) -{ - auto env = GetEnvironment(); - DEFVARIABLE(res, VariableType::JS_ANY(), IntToTaggedPtr(Int32(-1))); - DEFVARIABLE(pos, VariableType::INT32(), Int32(0)); - - Label objNotUndefinedAndNull(env); - Label isString(env); - Label isSearchString(env); - Label slowPath(env); - Label next(env); - Label resPosGreaterZero(env); - Label searchTagIsHeapObject(env); - Label posTagNotUndefined(env); - Label posTagIsInt(env); - Label posTagNotInt(env); - Label exit(env); - Label posTagIsDouble(env); - Label nextCount(env); - Label posNotLessThanLen(env); - Label thisIsHeapobject(env); - Label flattenFastPath(env); - Label flattenFastPath1(env); - - Branch(TaggedIsUndefinedOrNull(thisValue), &slowPath, &objNotUndefinedAndNull); - Bind(&objNotUndefinedAndNull); - { - Branch(TaggedIsHeapObject(thisValue), &thisIsHeapobject, &slowPath); - Bind(&thisIsHeapobject); - Branch(IsString(thisValue), &isString, &slowPath); - Bind(&isString); - { - GateRef searchTag = GetCallArg0(numArgs); - Branch(TaggedIsHeapObject(searchTag), &searchTagIsHeapObject, &slowPath); - Bind(&searchTagIsHeapObject); - Branch(IsString(searchTag), &isSearchString, &slowPath); - Bind(&isSearchString); - { - GateRef thisLen = GetLengthFromString(thisValue); - Branch(Int64GreaterThanOrEqual(IntPtr(1), numArgs), &next, &posTagNotUndefined); - Bind(&posTagNotUndefined); - { - GateRef posTag = GetCallArg1(numArgs); - Branch(TaggedIsInt(posTag), &posTagIsInt, &posTagNotInt); - Bind(&posTagIsInt); - pos = GetInt32OfTInt(posTag); - Jump(&next); - Bind(&posTagNotInt); - Branch(TaggedIsDouble(posTag), &posTagIsDouble, &slowPath); - Bind(&posTagIsDouble); - pos = DoubleToInt(glue, GetDoubleOfTDouble(posTag)); - Jump(&next); - } - Bind(&next); - { - Label posGreaterThanZero(env); - Label posNotGreaterThanZero(env); - Branch(Int32GreaterThan(*pos, Int32(0)), &posGreaterThanZero, &posNotGreaterThanZero); - Bind(&posNotGreaterThanZero); - { - pos = Int32(0); - Jump(&nextCount); - } - Bind(&posGreaterThanZero); - { - Branch(Int32LessThanOrEqual(*pos, thisLen), &nextCount, &posNotLessThanLen); - Bind(&posNotLessThanLen); - { - pos = thisLen; - Jump(&nextCount); - } - } - Bind(&nextCount); - { - DEFVARIABLE(thisFlat, VariableType::JS_POINTER(), thisValue); - DEFVARIABLE(searchFlat, VariableType::JS_POINTER(), searchTag); - FlattenString(thisValue, &thisFlat, &flattenFastPath, &slowPath); - Bind(&flattenFastPath); - FlattenString(searchTag, &searchFlat, &flattenFastPath1, &slowPath); - Bind(&flattenFastPath1); - BuiltinsStringStubBuilder stringBuilder(this); - GateRef resPos = stringBuilder.StringIndexOf(*thisFlat, *searchFlat, *pos); - Branch(Int32GreaterThanOrEqual(resPos, Int32(0)), &resPosGreaterZero, &exit); - Bind(&resPosGreaterZero); - { - Label resPosLessZero(env); - Branch(Int32LessThanOrEqual(resPos, thisLen), &resPosLessZero, &exit); - Bind(&resPosLessZero); - { - res = IntToTaggedPtr(resPos); - Jump(&exit); - } - } - } - } - } - } - } - Bind(&slowPath); - { - auto name = BuiltinsStubCSigns::GetName(BUILTINS_STUB_ID(IndexOf)); - res = CallSlowPath(nativeCode, glue, thisValue, numArgs, func, newTarget, name.c_str()); - Jump(&exit); - } - Bind(&exit); - Return(*res); -} - -DECLARE_BUILTINS(Substring) -{ - auto env = GetEnvironment(); - DEFVARIABLE(res, VariableType::JS_ANY(), IntToTaggedPtr(Int32(-1))); - DEFVARIABLE(start, VariableType::INT32(), Int32(0)); - DEFVARIABLE(end, VariableType::INT32(), Int32(0)); - DEFVARIABLE(from, VariableType::INT32(), Int32(0)); - DEFVARIABLE(to, VariableType::INT32(), Int32(0)); - - Label objNotUndefinedAndNull(env); - Label isString(env); - Label isSearchString(env); - Label slowPath(env); - Label countStart(env); - Label endTagIsUndefined(env); - Label startNotGreatZero(env); - Label countEnd(env); - Label endNotGreatZero(env); - Label countFrom(env); - Label countRes(env); - Label startTagNotUndefined(env); - Label posTagIsInt(env); - Label posTagNotInt(env); - Label exit(env); - Label posTagIsDouble(env); - Label endTagNotUndefined(env); - Label endTagIsInt(env); - Label endTagNotInt(env); - Label endTagIsDouble(env); - Label endGreatZero(env); - Label endGreatLen(env); - Label startGreatZero(env); - Label startGreatEnd(env); - Label startNotGreatEnd(env); - Label thisIsHeapobject(env); - Label flattenFastPath(env); - - Branch(TaggedIsUndefinedOrNull(thisValue), &slowPath, &objNotUndefinedAndNull); - Bind(&objNotUndefinedAndNull); - { - Branch(TaggedIsHeapObject(thisValue), &thisIsHeapobject, &slowPath); - Bind(&thisIsHeapobject); - Branch(IsString(thisValue), &isString, &slowPath); - Bind(&isString); - { - Label next(env); - GateRef thisLen = GetLengthFromString(thisValue); - Branch(Int64GreaterThanOrEqual(IntPtr(0), numArgs), &next, &startTagNotUndefined); - Bind(&startTagNotUndefined); - { - GateRef startTag = GetCallArg0(numArgs); - Branch(TaggedIsInt(startTag), &posTagIsInt, &posTagNotInt); - Bind(&posTagIsInt); - start = GetInt32OfTInt(startTag); - Jump(&next); - Bind(&posTagNotInt); - Branch(TaggedIsDouble(startTag), &posTagIsDouble, &slowPath); - Bind(&posTagIsDouble); - start = DoubleToInt(glue, GetDoubleOfTDouble(startTag)); - Jump(&next); - } - Bind(&next); - { - Branch(Int64GreaterThanOrEqual(IntPtr(1), numArgs), &endTagIsUndefined, &endTagNotUndefined); - Bind(&endTagIsUndefined); - { - end = thisLen; - Jump(&countStart); - } - Bind(&endTagNotUndefined); - { - GateRef endTag = GetCallArg1(numArgs); - Branch(TaggedIsInt(endTag), &endTagIsInt, &endTagNotInt); - Bind(&endTagIsInt); - end = GetInt32OfTInt(endTag); - Jump(&countStart); - Bind(&endTagNotInt); - Branch(TaggedIsDouble(endTag), &endTagIsDouble, &slowPath); - Bind(&endTagIsDouble); - end = DoubleToInt(glue, GetDoubleOfTDouble(endTag)); - Jump(&countStart); - } - } - Bind(&countStart); - { - Label startGreatLen(env); - Branch(Int32GreaterThan(*start, Int32(0)), &startGreatZero, &startNotGreatZero); - Bind(&startNotGreatZero); - { - start = Int32(0); - Jump(&countEnd); - } - Bind(&startGreatZero); - { - Branch(Int32GreaterThan(*start, thisLen), &startGreatLen, &countEnd); - Bind(&startGreatLen); - { - start = thisLen; - Jump(&countEnd); - } - } - } - Bind(&countEnd); - { - Branch(Int32GreaterThan(*end, Int32(0)), &endGreatZero, &endNotGreatZero); - Bind(&endNotGreatZero); - { - end = Int32(0); - Jump(&countFrom); - } - Bind(&endGreatZero); - { - Branch(Int32GreaterThan(*end, thisLen), &endGreatLen, &countFrom); - Bind(&endGreatLen); - { - end = thisLen; - Jump(&countFrom); - } - } - } - Bind(&countFrom); - { - Branch(Int32GreaterThan(*start, *end), &startGreatEnd, &startNotGreatEnd); - Bind(&startGreatEnd); - { - from = *end; - to = *start; - Jump(&countRes); - } - Bind(&startNotGreatEnd); - { - from = *start; - to = *end; - Jump(&countRes); - } - } - Bind(&countRes); - { - GateRef len = Int32Sub(*to, *from); - DEFVARIABLE(thisFlat, VariableType::JS_POINTER(), thisValue); - FlattenString(thisValue, &thisFlat, &flattenFastPath, &slowPath); - Bind(&flattenFastPath); - { - BuiltinsStringStubBuilder stringBuilder(this); - res = stringBuilder.FastSubString(glue, *thisFlat, *from, len); - Jump(&exit); - } - } - } - } - - Bind(&slowPath); - { - auto name = BuiltinsStubCSigns::GetName(BUILTINS_STUB_ID(Substring)); - res = CallSlowPath(nativeCode, glue, thisValue, numArgs, func, newTarget, name.c_str()); - Jump(&exit); - } - Bind(&exit); - Return(*res); -} - -DECLARE_BUILTINS(CharAt) -{ - auto env = GetEnvironment(); - DEFVARIABLE(res, VariableType::JS_POINTER(), Hole()); - DEFVARIABLE(pos, VariableType::INT32(), Int32(0)); - - Label objNotUndefinedAndNull(env); - Label isString(env); - Label slowPath(env); - Label next(env); - Label posTagNotUndefined(env); - Label posTagIsInt(env); - Label posTagNotInt(env); - Label posNotGreaterLen(env); - Label posGreaterLen(env); - Label posNotLessZero(env); - Label exit(env); - Label posTagIsDouble(env); - Label thisIsHeapobject(env); - Label flattenFastPath(env); - - Branch(TaggedIsUndefinedOrNull(thisValue), &slowPath, &objNotUndefinedAndNull); - Bind(&objNotUndefinedAndNull); - { - Branch(TaggedIsHeapObject(thisValue), &thisIsHeapobject, &slowPath); - Bind(&thisIsHeapobject); - Branch(IsString(thisValue), &isString, &slowPath); - Bind(&isString); - { - DEFVARIABLE(thisFlat, VariableType::JS_POINTER(), thisValue); - FlattenString(thisValue, &thisFlat, &flattenFastPath, &slowPath); - Bind(&flattenFastPath); - GateRef thisLen = GetLengthFromString(*thisFlat); - Branch(Int64GreaterThanOrEqual(IntPtr(0), numArgs), &next, &posTagNotUndefined); - Bind(&posTagNotUndefined); - { - GateRef posTag = GetCallArg0(numArgs); - Branch(TaggedIsInt(posTag), &posTagIsInt, &posTagNotInt); - Bind(&posTagIsInt); - pos = GetInt32OfTInt(posTag); - Jump(&next); - Bind(&posTagNotInt); - Branch(TaggedIsDouble(posTag), &posTagIsDouble, &slowPath); - Bind(&posTagIsDouble); - pos = DoubleToInt(glue, GetDoubleOfTDouble(posTag)); - Jump(&next); - } - Bind(&next); - { - Branch(Int32GreaterThanOrEqual(*pos, thisLen), &posGreaterLen, &posNotGreaterLen); - Bind(&posNotGreaterLen); - { - Branch(Int32LessThan(*pos, Int32(0)), &posGreaterLen, &posNotLessZero); - Bind(&posNotLessZero); - { - BuiltinsStringStubBuilder stringBuilder(this); - res = stringBuilder.CreateFromEcmaString(glue, *thisFlat, *pos); - Jump(&exit); - } - } - Bind(&posGreaterLen); - { - res = GetGlobalConstantValue( - VariableType::JS_POINTER(), glue, ConstantIndex::EMPTY_STRING_OBJECT_INDEX); - Jump(&exit); - } - } - } - } - Bind(&slowPath); - { - auto name = BuiltinsStubCSigns::GetName(BUILTINS_STUB_ID(CharAt)); - res = CallSlowPath(nativeCode, glue, thisValue, numArgs, func, newTarget, name.c_str()); - Jump(&exit); - } - Bind(&exit); - Return(*res); -} - -DECLARE_BUILTINS(VectorForEach) -{ - auto env = GetEnvironment(); - DEFVARIABLE(res, VariableType::JS_POINTER(), Undefined()); - - Label exit(env); - Label slowPath(env); - - ContainersStubBuilder containersBuilder(this); - containersBuilder.ContainersCommonFuncCall(glue, thisValue, numArgs, &res, &exit, - &slowPath, ContainersType::VECTOR_FOREACH); - Bind(&slowPath); - { - auto name = BuiltinsStubCSigns::GetName(BUILTINS_STUB_ID(VectorForEach)); - res = CallSlowPath(nativeCode, glue, thisValue, numArgs, func, newTarget, name.c_str()); - Jump(&exit); - } - Bind(&exit); - Return(*res); -} - -DECLARE_BUILTINS(VectorReplaceAllElements) -{ - auto env = GetEnvironment(); - DEFVARIABLE(res, VariableType::JS_POINTER(), Undefined()); - - Label exit(env); - Label slowPath(env); - - ContainersStubBuilder containersBuilder(this); - containersBuilder.ContainersCommonFuncCall(glue, thisValue, numArgs, &res, &exit, - &slowPath, ContainersType::VECTOR_REPLACEALLELEMENTS); - Bind(&slowPath); - { - auto name = BuiltinsStubCSigns::GetName(BUILTINS_STUB_ID(VectorReplaceAllElements)); - res = CallSlowPath(nativeCode, glue, thisValue, numArgs, func, newTarget, name.c_str()); - Jump(&exit); - } - Bind(&exit); - Return(*res); -} - -DECLARE_BUILTINS(StackForEach) -{ - auto env = GetEnvironment(); - DEFVARIABLE(res, VariableType::JS_POINTER(), Undefined()); - - Label exit(env); - Label slowPath(env); - - ContainersStubBuilder containersBuilder(this); - containersBuilder.ContainersCommonFuncCall(glue, thisValue, numArgs, &res, &exit, - &slowPath, ContainersType::STACK_FOREACH); - Bind(&slowPath); - { - auto name = BuiltinsStubCSigns::GetName(BUILTINS_STUB_ID(StackForEach)); - res = CallSlowPath(nativeCode, glue, thisValue, numArgs, func, newTarget, name.c_str()); - Jump(&exit); - } - Bind(&exit); - Return(*res); -} - -DECLARE_BUILTINS(PlainArrayForEach) -{ - auto env = GetEnvironment(); - DEFVARIABLE(res, VariableType::JS_POINTER(), Undefined()); - - Label exit(env); - Label slowPath(env); - - ContainersStubBuilder containersBuilder(this); - containersBuilder.ContainersCommonFuncCall(glue, thisValue, numArgs, &res, &exit, - &slowPath, ContainersType::PLAINARRAY_FOREACH); - Bind(&slowPath); - { - auto name = BuiltinsStubCSigns::GetName(BUILTINS_STUB_ID(PlainArrayForEach)); - res = CallSlowPath(nativeCode, glue, thisValue, numArgs, func, newTarget, name.c_str()); - Jump(&exit); - } - Bind(&exit); - Return(*res); -} - -DECLARE_BUILTINS(QueueForEach) -{ - auto env = GetEnvironment(); - DEFVARIABLE(res, VariableType::JS_POINTER(), Undefined()); - - Label exit(env); - Label slowPath(env); - - ContainersStubBuilder containersBuilder(this); - containersBuilder.QueueCommonFuncCall(glue, thisValue, numArgs, &res, &exit, - &slowPath, ContainersType::QUEUE_FOREACH); - Bind(&slowPath); - { - auto name = BuiltinsStubCSigns::GetName(BUILTINS_STUB_ID(QueueForEach)); - res = CallSlowPath(nativeCode, glue, thisValue, numArgs, func, newTarget, name.c_str()); - Jump(&exit); - } - Bind(&exit); - Return(*res); -} - -DECLARE_BUILTINS(DequeForEach) -{ - auto env = GetEnvironment(); - DEFVARIABLE(res, VariableType::JS_POINTER(), Undefined()); - - Label exit(env); - Label slowPath(env); - - ContainersStubBuilder containersBuilder(this); - containersBuilder.DequeCommonFuncCall(glue, thisValue, numArgs, &res, &exit, - &slowPath, ContainersType::DEQUE_FOREACH); - Bind(&slowPath); - { - auto name = BuiltinsStubCSigns::GetName(BUILTINS_STUB_ID(DequeForEach)); - res = CallSlowPath(nativeCode, glue, thisValue, numArgs, func, newTarget, name.c_str()); - Jump(&exit); - } - Bind(&exit); - Return(*res); -} - -DECLARE_BUILTINS(LightWeightMapForEach) -{ - auto env = GetEnvironment(); - DEFVARIABLE(res, VariableType::JS_POINTER(), Undefined()); - - Label exit(env); - Label slowPath(env); - - ContainersStubBuilder containersBuilder(this); - containersBuilder.ContainersLightWeightCall(glue, thisValue, numArgs, &res, &exit, - &slowPath, ContainersType::LIGHTWEIGHTMAP_FOREACH); - Bind(&slowPath); - { - auto name = BuiltinsStubCSigns::GetName(BUILTINS_STUB_ID(LightWeightMapForEach)); - res = CallSlowPath(nativeCode, glue, thisValue, numArgs, func, newTarget, name.c_str()); - Jump(&exit); - } - Bind(&exit); - Return(*res); -} - -DECLARE_BUILTINS(LightWeightSetForEach) -{ - auto env = GetEnvironment(); - DEFVARIABLE(res, VariableType::JS_POINTER(), Undefined()); - - Label exit(env); - Label slowPath(env); - - ContainersStubBuilder containersBuilder(this); - containersBuilder.ContainersLightWeightCall(glue, thisValue, numArgs, &res, &exit, - &slowPath, ContainersType::LIGHTWEIGHTSET_FOREACH); - Bind(&slowPath); - { - auto name = BuiltinsStubCSigns::GetName(BUILTINS_STUB_ID(LightWeightSetForEach)); - res = CallSlowPath(nativeCode, glue, thisValue, numArgs, func, newTarget, name.c_str()); - Jump(&exit); - } - Bind(&exit); - Return(*res); +#define DECLARE_BUILTINS_WITH_STRING_STUB_BUILDER(method, resultVariableType, initValue) \ +DECLARE_BUILTINS(String##method) \ +{ \ + auto env = GetEnvironment(); \ + DEFVARIABLE(res, VariableType::resultVariableType(), initValue); \ + Label exit(env); \ + Label slowPath(env); \ + BuiltinsStringStubBuilder stringStubBuilder(this); \ + stringStubBuilder.method(glue, thisValue, numArgs, &res, &exit, &slowPath); \ + Bind(&slowPath); \ + { \ + auto name = BuiltinsStubCSigns::GetName(BUILTINS_STUB_ID(String##method)); \ + res = CallSlowPath(nativeCode, glue, thisValue, numArgs, func, newTarget, name.c_str()); \ + Jump(&exit); \ + } \ + Bind(&exit); \ + Return(*res); \ } -DECLARE_BUILTINS(HashMapForEach) -{ - auto env = GetEnvironment(); - DEFVARIABLE(res, VariableType::JS_POINTER(), Undefined()); - - Label exit(env); - Label slowPath(env); - - ContainersStubBuilder containersBuilder(this); - containersBuilder.ContainersHashCall(glue, thisValue, numArgs, &res, &exit, - &slowPath, ContainersType::HASHMAP_FOREACH); - Bind(&slowPath); - { - auto name = BuiltinsStubCSigns::GetName(BUILTINS_STUB_ID(HashMapForEach)); - res = CallSlowPath(nativeCode, glue, thisValue, numArgs, func, newTarget, name.c_str()); - Jump(&exit); - } - Bind(&exit); - Return(*res); -} +#define BUILTINS_WITH_STRING_STUB_BUILDER(V) \ + V(CharAt, JS_POINTER, Hole()) \ + V(FromCharCode, JS_ANY, Hole()) \ + V(CharCodeAt, JS_ANY, DoubleToTaggedDoublePtr(Double(base::NAN_VALUE))) \ + V(IndexOf, JS_ANY, IntToTaggedPtr(Int32(-1))) \ + V(Substring, JS_ANY, IntToTaggedPtr(Int32(-1))) -DECLARE_BUILTINS(HashSetForEach) -{ - auto env = GetEnvironment(); - DEFVARIABLE(res, VariableType::JS_POINTER(), Undefined()); +BUILTINS_WITH_STRING_STUB_BUILDER(DECLARE_BUILTINS_WITH_STRING_STUB_BUILDER) - Label exit(env); - Label slowPath(env); +#undef DECLARE_BUILTINS_WITH_STRING_STUB_BUILDER +#undef BUILTINS_WITH_STRING_STUB_BUILDER - ContainersStubBuilder containersBuilder(this); - containersBuilder.ContainersHashCall(glue, thisValue, numArgs, &res, &exit, - &slowPath, ContainersType::HASHSET_FOREACH); - Bind(&slowPath); - { - auto name = BuiltinsStubCSigns::GetName(BUILTINS_STUB_ID(HashSetForEach)); - res = CallSlowPath(nativeCode, glue, thisValue, numArgs, func, newTarget, name.c_str()); - Jump(&exit); - } - Bind(&exit); - Return(*res); -} - -DECLARE_BUILTINS(LinkedListForEach) +DECLARE_BUILTINS(FunctionPrototypeApply) { auto env = GetEnvironment(); - DEFVARIABLE(res, VariableType::JS_POINTER(), Undefined()); - + DEFVARIABLE(res, VariableType::JS_ANY(), Undefined()); Label exit(env); Label slowPath(env); - - ContainersStubBuilder containersBuilder(this); - containersBuilder.ContainersLinkedListCall(glue, thisValue, numArgs, &res, &exit, - &slowPath, ContainersType::LINKEDLIST_FOREACH); + BuiltinsFunctionStubBuilder functionStubBuilder(this); + functionStubBuilder.Apply(glue, thisValue, numArgs, &res, &exit, &slowPath); Bind(&slowPath); { - auto name = BuiltinsStubCSigns::GetName(BUILTINS_STUB_ID(LinkedListForEach)); + auto name = BuiltinsStubCSigns::GetName(BUILTINS_STUB_ID(FunctionPrototypeApply)); res = CallSlowPath(nativeCode, glue, thisValue, numArgs, func, newTarget, name.c_str()); Jump(&exit); } @@ -793,174 +196,92 @@ DECLARE_BUILTINS(LinkedListForEach) Return(*res); } -DECLARE_BUILTINS(ListForEach) -{ - auto env = GetEnvironment(); - DEFVARIABLE(res, VariableType::JS_POINTER(), Undefined()); - - Label exit(env); - Label slowPath(env); - - ContainersStubBuilder containersBuilder(this); - containersBuilder.ContainersLinkedListCall(glue, thisValue, numArgs, &res, &exit, - &slowPath, ContainersType::LIST_FOREACH); - Bind(&slowPath); - { - auto name = BuiltinsStubCSigns::GetName(BUILTINS_STUB_ID(ListForEach)); - res = CallSlowPath(nativeCode, glue, thisValue, numArgs, func, newTarget, name.c_str()); - Jump(&exit); - } - Bind(&exit); - Return(*res); +#define DECLARE_BUILTINS_WITH_CONTAINERS_STUB_BUILDER(StubName, Method, methodType, resultVariableType) \ +DECLARE_BUILTINS(StubName) \ +{ \ + auto env = GetEnvironment(); \ + DEFVARIABLE(res, VariableType::resultVariableType(), Undefined()); \ + Label exit(env); \ + Label slowPath(env); \ + ContainersStubBuilder containersBuilder(this); \ + containersBuilder.Method(glue, thisValue, numArgs, &res, &exit, &slowPath, ContainersType::methodType); \ + Bind(&slowPath); \ + { \ + auto name = BuiltinsStubCSigns::GetName(BUILTINS_STUB_ID(StubName)); \ + res = CallSlowPath(nativeCode, glue, thisValue, numArgs, func, newTarget, name.c_str()); \ + Jump(&exit); \ + } \ + Bind(&exit); \ + Return(*res); \ } -DECLARE_BUILTINS(ArrayListForEach) -{ - auto env = GetEnvironment(); - DEFVARIABLE(res, VariableType::JS_POINTER(), Undefined()); - - Label exit(env); - Label slowPath(env); - - ContainersStubBuilder containersBuilder(this); - containersBuilder.ContainersCommonFuncCall(glue, thisValue, numArgs, &res, &exit, - &slowPath, ContainersType::ARRAYLIST_FOREACH); - Bind(&slowPath); - { - auto name = BuiltinsStubCSigns::GetName(BUILTINS_STUB_ID(ArrayListForEach)); - res = CallSlowPath(nativeCode, glue, thisValue, numArgs, func, newTarget, name.c_str()); - Jump(&exit); - } - Bind(&exit); - Return(*res); +#define BUILTINS_WITH_CONTAINERS_STUB_BUILDER(V) \ + V(ArrayListForEach, ContainersCommonFuncCall, ARRAYLIST_FOREACH, JS_POINTER) \ + V(DequeForEach, DequeCommonFuncCall, DEQUE_FOREACH, JS_POINTER) \ + V(HashMapForEach, ContainersHashCall, HASHMAP_FOREACH, JS_POINTER) \ + V(HashSetForEach, ContainersHashCall, HASHSET_FOREACH, JS_POINTER) \ + V(LightWeightMapForEach, ContainersLightWeightCall, LIGHTWEIGHTMAP_FOREACH, JS_POINTER) \ + V(LightWeightSetForEach, ContainersLightWeightCall, LIGHTWEIGHTSET_FOREACH, JS_POINTER) \ + V(LinkedListForEach, ContainersLinkedListCall, LINKEDLIST_FOREACH, JS_POINTER) \ + V(ListForEach, ContainersLinkedListCall, LIST_FOREACH, JS_POINTER) \ + V(PlainArrayForEach, ContainersCommonFuncCall, PLAINARRAY_FOREACH, JS_POINTER) \ + V(QueueForEach, QueueCommonFuncCall, QUEUE_FOREACH, JS_POINTER) \ + V(StackForEach, ContainersCommonFuncCall, STACK_FOREACH, JS_POINTER) \ + V(VectorForEach, ContainersCommonFuncCall, VECTOR_FOREACH, JS_POINTER) \ + V(ArrayListReplaceAllElements, ContainersCommonFuncCall, ARRAYLIST_REPLACEALLELEMENTS, JS_POINTER) \ + V(VectorReplaceAllElements, ContainersCommonFuncCall, VECTOR_REPLACEALLELEMENTS, JS_POINTER) + +BUILTINS_WITH_CONTAINERS_STUB_BUILDER(DECLARE_BUILTINS_WITH_CONTAINERS_STUB_BUILDER) + +#undef DECLARE_BUILTINS_WITH_CONTAINERS_STUB_BUILDER +#undef BUILTINS_WITH_CONTAINERS_STUB_BUILDER + +#define DECLARE_BUILTINS_WITH_ARRAY_STUB_BUILDER(Method, resultVariableType) \ +DECLARE_BUILTINS(Array##Method) \ +{ \ + auto env = GetEnvironment(); \ + DEFVARIABLE(res, VariableType::resultVariableType(), Undefined()); \ + Label exit(env); \ + Label slowPath(env); \ + BuiltinsArrayStubBuilder arrayStubBuilder(this); \ + arrayStubBuilder.Method(glue, thisValue, numArgs, &res, &exit, &slowPath); \ + Bind(&slowPath); \ + { \ + auto name = BuiltinsStubCSigns::GetName(BUILTINS_STUB_ID(Array##Method)); \ + res = CallSlowPath(nativeCode, glue, thisValue, numArgs, func, newTarget, name.c_str()); \ + Jump(&exit); \ + } \ + Bind(&exit); \ + Return(*res); \ } -DECLARE_BUILTINS(ArrayListReplaceAllElements) -{ - auto env = GetEnvironment(); - DEFVARIABLE(res, VariableType::JS_POINTER(), Undefined()); - - Label exit(env); - Label slowPath(env); - - ContainersStubBuilder containersBuilder(this); - containersBuilder.ContainersCommonFuncCall(glue, thisValue, numArgs, &res, &exit, - &slowPath, ContainersType::ARRAYLIST_REPLACEALLELEMENTS); - Bind(&slowPath); - { - auto name = BuiltinsStubCSigns::GetName(BUILTINS_STUB_ID(ArrayListReplaceAllElements)); - res = CallSlowPath(nativeCode, glue, thisValue, numArgs, func, newTarget, name.c_str()); - Jump(&exit); - } - Bind(&exit); - Return(*res); -} +#define BUILTINS_WITH_ARRAY_STUB_BUILDER(V) \ + V(Concat, JS_POINTER) \ + V(Filter, JS_POINTER) \ + V(ForEach, JS_ANY) \ + V(IndexOf, JS_ANY) \ + V(LastIndexOf, JS_ANY) \ + V(Slice, JS_POINTER) \ + V(Reverse, JS_POINTER) \ + V(Push, JS_ANY) -DECLARE_BUILTINS(FunctionPrototypeApply) -{ - auto env = GetEnvironment(); - DEFVARIABLE(res, VariableType::JS_ANY(), Undefined()); - Label exit(env); - Label slowPath(env); - Label targetIsCallable(env); - Label targetIsUndefined(env); - Label targetNotUndefined(env); - Label isHeapObject(env); - //1. If IsCallable(func) is false, throw a TypeError exception - Branch(TaggedIsHeapObject(thisValue), &isHeapObject, &slowPath); - Bind(&isHeapObject); - { - Branch(IsCallable(thisValue), &targetIsCallable, &slowPath); - Bind(&targetIsCallable); - { - GateRef thisArg = GetCallArg0(numArgs); - GateRef arrayObj = GetCallArg1(numArgs); - // 2. If argArray is null or undefined, then - Branch(TaggedIsUndefined(arrayObj), &targetIsUndefined, &targetNotUndefined); - Bind(&targetIsUndefined); - { - // a. Return Call(func, thisArg). - res = JSCallDispatch(glue, thisValue, Int32(0), 0, Circuit::NullGate(), - JSCallMode::CALL_GETTER, { thisArg }); - Jump(&exit); - } - Bind(&targetNotUndefined); - { - // 3. Let argList be CreateListFromArrayLike(argArray). - GateRef elements = BuildArgumentsListFastElements(glue, arrayObj); - Label targetIsHole(env); - Label targetNotHole(env); - Branch(TaggedIsHole(elements), &targetIsHole, &targetNotHole); - Bind(&targetIsHole); - { - GateRef argList = CreateListFromArrayLike(glue, arrayObj); - // 4. ReturnIfAbrupt(argList). - Label isPendingException(env); - Label noPendingException(env); - Branch(HasPendingException(glue), &isPendingException, &noPendingException); - Bind(&isPendingException); - { - Jump(&slowPath); - } - Bind(&noPendingException); - { - GateRef argsLength = GetLengthOfTaggedArray(argList); - GateRef argv = PtrAdd(argList, IntPtr(TaggedArray::DATA_OFFSET)); - res = JSCallDispatch(glue, thisValue, argsLength, 0, Circuit::NullGate(), - JSCallMode::CALL_THIS_ARGV_WITH_RETURN, { argsLength, argv, thisArg }); - Jump(&exit); - } - } - Bind(&targetNotHole); - { - // 6. Return Call(func, thisArg, argList). - Label taggedIsStableJsArg(env); - Label taggedNotStableJsArg(env); - Branch(IsStableJSArguments(glue, arrayObj), &taggedIsStableJsArg, &taggedNotStableJsArg); - Bind(&taggedIsStableJsArg); - { - GateRef hClass = LoadHClass(arrayObj); - GateRef PropertyInlinedPropsOffset = IntPtr(JSArguments::LENGTH_INLINE_PROPERTY_INDEX); - GateRef result = GetPropertyInlinedProps(arrayObj, hClass, PropertyInlinedPropsOffset); - GateRef length = TaggedGetInt(result); - GateRef argsLength = MakeArgListWithHole(glue, elements, length); - GateRef elementArgv = PtrAdd(elements, IntPtr(TaggedArray::DATA_OFFSET)); - res = JSCallDispatch(glue, thisValue, argsLength, 0, Circuit::NullGate(), - JSCallMode::CALL_THIS_ARGV_WITH_RETURN, { argsLength, elementArgv, thisArg }); - Jump(&exit); - } - Bind(&taggedNotStableJsArg); - { - GateRef length = GetLengthOfJsArray(glue, arrayObj); - GateRef argsLength = MakeArgListWithHole(glue, elements, length); - GateRef elementArgv = PtrAdd(elements, IntPtr(TaggedArray::DATA_OFFSET)); - res = JSCallDispatch(glue, thisValue, argsLength, 0, Circuit::NullGate(), - JSCallMode::CALL_THIS_ARGV_WITH_RETURN, { argsLength, elementArgv, thisArg }); - Jump(&exit); - } - } - } - } - } +BUILTINS_WITH_ARRAY_STUB_BUILDER(DECLARE_BUILTINS_WITH_ARRAY_STUB_BUILDER) - Bind(&slowPath); - { - res = CallSlowPath(nativeCode, glue, thisValue, numArgs, func, newTarget); - Jump(&exit); - } - Bind(&exit); - Return(*res); -} +#undef DECLARE_BUILTINS_WITH_ARRAY_STUB_BUILDER +#undef BUILTINS_WITH_ARRAY_STUB_BUILDER DECLARE_BUILTINS(BooleanConstructor) { auto env = GetEnvironment(); DEFVARIABLE(res, VariableType::JS_ANY(), Undefined()); + Label newTargetIsHeapObject(env); Label newTargetIsJSFunction(env); Label slowPath(env); Label exit(env); + Branch(TaggedIsHeapObject(newTarget), &newTargetIsHeapObject, &slowPath); + Bind(&newTargetIsHeapObject); Branch(IsJSFunction(newTarget), &newTargetIsJSFunction, &slowPath); Bind(&newTargetIsJSFunction); { @@ -1000,10 +321,13 @@ DECLARE_BUILTINS(DateConstructor) auto env = GetEnvironment(); DEFVARIABLE(res, VariableType::JS_ANY(), Undefined()); + Label newTargetIsHeapObject(env); Label newTargetIsJSFunction(env); Label slowPath(env); Label exit(env); + Branch(TaggedIsHeapObject(newTarget), &newTargetIsHeapObject, &slowPath); + Bind(&newTargetIsHeapObject); Branch(IsJSFunction(newTarget), &newTargetIsJSFunction, &slowPath); Bind(&newTargetIsJSFunction); { @@ -1082,10 +406,14 @@ DECLARE_BUILTINS(ArrayConstructor) auto env = GetEnvironment(); DEFVARIABLE(res, VariableType::JS_ANY(), Undefined()); + Label newTargetIsHeapObject(env); Label newTargetIsJSFunction(env); Label slowPath(env); + Label slowPath1(env); Label exit(env); + Branch(TaggedIsHeapObject(newTarget), &newTargetIsHeapObject, &slowPath1); + Bind(&newTargetIsHeapObject); Branch(IsJSFunction(newTarget), &newTargetIsJSFunction, &slowPath); Bind(&newTargetIsJSFunction); { @@ -1165,7 +493,7 @@ DECLARE_BUILTINS(ArrayConstructor) newBuilder.SetParameters(glue, 0); res = newBuilder.NewJSArrayWithSize(intialHClass, *arrayLength); GateRef lengthOffset = IntPtr(JSArray::LENGTH_OFFSET); - Store(VariableType::JS_ANY(), glue, *res, lengthOffset, Int64ToTaggedInt(*arrayLength)); + Store(VariableType::INT32(), glue, *res, lengthOffset, TruncInt64ToInt32(*arrayLength)); GateRef accessor = GetGlobalConstantValue(VariableType::JS_ANY(), glue, ConstantIndex::ARRAY_LENGTH_ACCESSOR); SetPropertyInlinedProps(glue, *res, intialHClass, accessor, @@ -1183,8 +511,90 @@ DECLARE_BUILTINS(ArrayConstructor) res = CallBuiltinRuntime(glue, { glue, nativeCode, func, thisValue, numArgs, argv }, true, name.c_str()); Jump(&exit); } + Bind(&slowPath1); + { + auto name = BuiltinsStubCSigns::GetName(BUILTINS_STUB_ID(ArrayConstructor)); + res = CallSlowPath(nativeCode, glue, thisValue, numArgs, func, newTarget, name.c_str()); + Jump(&exit); + } Bind(&exit); Return(*res); } -} // namespace panda::ecmascript::kungfu \ No newline at end of file + +#define DECLARE_BUILTINS_OBJECT_STUB_BUILDER(type, method, retType, retDefaultValue) \ +DECLARE_BUILTINS(type##method) \ +{ \ + auto env = GetEnvironment(); \ + DEFVARIABLE(res, retType, retDefaultValue); \ + Label thisCollectionObj(env); \ + Label slowPath(env); \ + Label exit(env); \ + BuiltinsObjectStubBuilder builder(this, glue, thisValue); \ + builder.method(&res, &exit, &slowPath); \ + Bind(&slowPath); \ + { \ + auto name = BuiltinsStubCSigns::GetName(BUILTINS_STUB_ID(type##method)); \ + res = CallSlowPath(nativeCode, glue, thisValue, numArgs, func, newTarget, name.c_str()); \ + Jump(&exit); \ + } \ + Bind(&exit); \ + Return(*res); \ +} + +// Object.protetype.ToString +DECLARE_BUILTINS_OBJECT_STUB_BUILDER(Object, ToString, VariableType::JS_ANY(), Undefined()); +#undef DECLARE_BUILTINS_OBJECT_STUB_BUILDER + +#define DECLARE_BUILTINS_COLLECTION_STUB_BUILDER(type, method, retType, retDefaultValue) \ +DECLARE_BUILTINS(type##method) \ +{ \ + auto env = GetEnvironment(); \ + DEFVARIABLE(res, retType, retDefaultValue); \ + Label thisCollectionObj(env); \ + Label slowPath(env); \ + Label exit(env); \ + BuiltinsCollectionStubBuilder builder(this, glue, thisValue, numArgs); \ + builder.method(&res, &exit, &slowPath); \ + Bind(&slowPath); \ + { \ + auto name = BuiltinsStubCSigns::GetName(BUILTINS_STUB_ID(type##method)); \ + res = CallSlowPath(nativeCode, glue, thisValue, numArgs, func, newTarget, name.c_str()); \ + Jump(&exit); \ + } \ + Bind(&exit); \ + Return(*res); \ +} + +// Set.protetype.Clear +DECLARE_BUILTINS_COLLECTION_STUB_BUILDER(Set, Clear, VariableType::JS_ANY(), Undefined()); +// Set.protetype.Values +DECLARE_BUILTINS_COLLECTION_STUB_BUILDER(Set, Values, VariableType::JS_ANY(), Undefined()); +// Set.protetype.Entries +DECLARE_BUILTINS_COLLECTION_STUB_BUILDER(Set, Entries, VariableType::JS_ANY(), Undefined()); +// Set.protetype.ForEach +DECLARE_BUILTINS_COLLECTION_STUB_BUILDER(Set, ForEach, VariableType::JS_ANY(), Undefined()); +// Set.protetype.Add +DECLARE_BUILTINS_COLLECTION_STUB_BUILDER(Set, Add, VariableType::JS_ANY(), Undefined()); +// Set.protetype.Delete +DECLARE_BUILTINS_COLLECTION_STUB_BUILDER(Set, Delete, VariableType::JS_ANY(), Undefined()); +// Set.protetype.Has +DECLARE_BUILTINS_COLLECTION_STUB_BUILDER(Set, Has, VariableType::JS_ANY(), Undefined()); +// Map.protetype.Clear +DECLARE_BUILTINS_COLLECTION_STUB_BUILDER(Map, Clear, VariableType::JS_ANY(), Undefined()); +// Map.protetype.Values +DECLARE_BUILTINS_COLLECTION_STUB_BUILDER(Map, Values, VariableType::JS_ANY(), Undefined()); +// Map.protetype.Entries +DECLARE_BUILTINS_COLLECTION_STUB_BUILDER(Map, Entries, VariableType::JS_ANY(), Undefined()); +// Map.protetype.Keys +DECLARE_BUILTINS_COLLECTION_STUB_BUILDER(Map, Keys, VariableType::JS_ANY(), Undefined()); +// Map.protetype.ForEach +DECLARE_BUILTINS_COLLECTION_STUB_BUILDER(Map, ForEach, VariableType::JS_ANY(), Undefined()); +// Map.protetype.set +DECLARE_BUILTINS_COLLECTION_STUB_BUILDER(Map, Set, VariableType::JS_ANY(), Undefined()); +// Map.protetype.Delete +DECLARE_BUILTINS_COLLECTION_STUB_BUILDER(Map, Delete, VariableType::JS_ANY(), Undefined()); +// Map.protetype.Has +DECLARE_BUILTINS_COLLECTION_STUB_BUILDER(Map, Has, VariableType::JS_ANY(), Undefined()); +#undef DECLARE_BUILTINS_COLLECTION_STUB_BUILDER +} // namespace panda::ecmascript::kungfu diff --git a/ecmascript/compiler/builtins/containers_stub_builder.cpp b/ecmascript/compiler/builtins/containers_stub_builder.cpp index 1a9c4032764a8673c39eac530ccb834690312f9a..9f00e6c7d8d7e90df77f4f0905a01b031bcb83c8 100644 --- a/ecmascript/compiler/builtins/containers_stub_builder.cpp +++ b/ecmascript/compiler/builtins/containers_stub_builder.cpp @@ -119,7 +119,7 @@ void ContainersStubBuilder::ContainersCommonFuncCall(GateRef glue, GateRef thisV Branch(Int32NotEqual(tempLen, *length), &lenChange, &setValue); Bind(&lenChange); length = tempLen; - Jump(&setValue); + Branch(Int32GreaterThanOrEqual(*k, *length), &afterLoop, &setValue); Bind(&setValue); if (IsReplaceAllElements(type)) { ContainerSet(glue, *thisObj, *k, retValue, type); diff --git a/ecmascript/compiler/builtins/linked_hashtable_stub_builder.cpp b/ecmascript/compiler/builtins/linked_hashtable_stub_builder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2404124e5e30fe631a2e419773de93d419762793 --- /dev/null +++ b/ecmascript/compiler/builtins/linked_hashtable_stub_builder.cpp @@ -0,0 +1,641 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/compiler/builtins/linked_hashtable_stub_builder.h" + +#include "ecmascript/compiler/builtins/builtins_stubs.h" +#include "ecmascript/compiler/new_object_stub_builder.h" +#include "ecmascript/linked_hash_table.h" +#include "ecmascript/js_set.h" +#include "ecmascript/js_map.h" + +namespace panda::ecmascript::kungfu { +template +void LinkedHashTableStubBuilder::Rehash( + GateRef linkedTable, GateRef newTable) +{ + auto env = GetEnvironment(); + Label entryLabel(env); + env->SubCfgEntry(&entryLabel); + + GateRef numberOfAllElements = Int32Add(GetNumberOfElements(linkedTable), + GetNumberOfDeletedElements(linkedTable)); + + DEFVARIABLE(desEntry, VariableType::INT32(), Int32(0)); + DEFVARIABLE(currentDeletedElements, VariableType::INT32(), Int32(0)); + SetNextTable(linkedTable, newTable); + + Label loopHead(env); + Label loopEnd(env); + Label next(env); + Label loopExit(env); + + DEFVARIABLE(i, VariableType::INT32(), Int32(0)); + Jump(&loopHead); + LoopBegin(&loopHead); + { + Branch(Int32LessThan(*i, numberOfAllElements), &next, &loopExit); + Bind(&next); + + GateRef fromIndex = EntryToIndex(linkedTable, *i); + DEFVARIABLE(key, VariableType::JS_ANY(), GetElement(linkedTable, fromIndex)); + Label hole(env); + Label notHole(env); + Branch(TaggedIsHole(*key), &hole, ¬Hole); + Bind(&hole); + { + currentDeletedElements = Int32Add(*currentDeletedElements, Int32(1)); + SetDeletedNum(linkedTable, *i, *currentDeletedElements); + Jump(&loopEnd); + } + Bind(¬Hole); + { + Label weak(env); + Label notWeak(env); + Branch(TaggedIsWeak(*key), &weak, ¬Weak); + Bind(&weak); + { + key = RemoveTaggedWeakTag(*key); + Jump(¬Weak); + } + Bind(¬Weak); + + GateRef hash = GetHash(*key); + GateRef bucket = HashToBucket(newTable, hash); + InsertNewEntry(newTable, bucket, *desEntry); + GateRef desIndex = EntryToIndex(newTable, *desEntry); + + Label loopHead1(env); + Label loopEnd1(env); + Label next1(env); + Label loopExit1(env); + DEFVARIABLE(j, VariableType::INT32(), Int32(0)); + Jump(&loopHead1); + LoopBegin(&loopHead1); + { + Branch(Int32LessThan(*j, Int32(LinkedHashTableObject::ENTRY_SIZE)), &next1, &loopExit1); + Bind(&next1); + GateRef ele = GetElement(linkedTable, Int32Add(fromIndex, *j)); + SetElement(newTable, Int32Add(desIndex, *j), ele); + Jump(&loopEnd1); + } + Bind(&loopEnd1); + j = Int32Add(*j, Int32(1)); + LoopEnd(&loopHead1); + Bind(&loopExit1); + desEntry = Int32Add(*desEntry, Int32(1)); + Jump(&loopEnd); + } + } + Bind(&loopEnd); + i = Int32Add(*i, Int32(1)); + LoopEnd(&loopHead); + Bind(&loopExit); + + SetNumberOfElements(newTable, GetNumberOfElements(linkedTable)); + SetNumberOfDeletedElements(newTable, Int32(0)); + env->SubCfgExit(); +} + +template +GateRef LinkedHashTableStubBuilder::GrowCapacity( + GateRef linkedTable, GateRef numberOfAddedElements) +{ + auto env = GetEnvironment(); + Label entryLabel(env); + env->SubCfgEntry(&entryLabel); + Label exit(env); + DEFVARIABLE(res, VariableType::JS_ANY(), linkedTable); + + GateRef hasSufficient = HasSufficientCapacity(linkedTable, numberOfAddedElements); + Label grow(env); + Branch(hasSufficient, &exit, &grow); + Bind(&grow); + { + GateRef newCapacity = ComputeCapacity(Int32Add(GetNumberOfElements(linkedTable), numberOfAddedElements)); + GateRef newTable = Create(newCapacity); + Rehash(linkedTable, newTable); + res = newTable; + Jump(&exit); + } + Bind(&exit); + auto ret = *res; + env->SubCfgExit(); + return ret; +} + +template +GateRef LinkedHashTableStubBuilder::ComputeCapacity( + GateRef atLeastSpaceFor) +{ + if constexpr (std::is_same_v) { + return TaggedGetInt(CallRuntime(glue_, RTSTUB_ID(LinkedHashMapComputeCapacity), { + IntToTaggedInt(atLeastSpaceFor) })); + } else { + return TaggedGetInt(CallRuntime(glue_, RTSTUB_ID(LinkedHashSetComputeCapacity), { + IntToTaggedInt(atLeastSpaceFor) })); + } +} + +template +void LinkedHashTableStubBuilder::RemoveEntry( + GateRef linkedTable, GateRef entry) +{ + auto env = GetEnvironment(); + Label entryLabel(env); + Label exit(env); + env->SubCfgEntry(&entryLabel); + DEFVARIABLE(i, VariableType::INT32(), Int32(0)); + + Label loopHead(env); + Label loopEnd(env); + Label next(env); + Label loopExit(env); + GateRef index = EntryToIndex(linkedTable, entry); + Jump(&loopHead); + LoopBegin(&loopHead); + { + Branch(Int32LessThan(*i, Int32(LinkedHashTableObject::ENTRY_SIZE)), &next, &loopExit); + Bind(&next); + + GateRef idx = Int32Add(index, *i); + SetElement(linkedTable, idx, Hole()); + Jump(&loopEnd); + } + Bind(&loopEnd); + i = Int32Add(*i, Int32(1)); + LoopEnd(&loopHead); + Bind(&loopExit); + + GateRef newNofe = Int32Sub(GetNumberOfElements(linkedTable), Int32(1)); + SetNumberOfElements(linkedTable, newNofe); + GateRef newNofd = Int32Add(GetNumberOfDeletedElements(linkedTable), Int32(1)); + SetNumberOfDeletedElements(linkedTable, newNofd); + env->SubCfgExit(); +} + +template +GateRef LinkedHashTableStubBuilder::HasSufficientCapacity( + GateRef linkedTable, GateRef numOfAddElements) +{ + auto env = GetEnvironment(); + Label entryLabel(env); + Label exit(env); + env->SubCfgEntry(&entryLabel); + DEFVARIABLE(res, VariableType::BOOL(), False()); + + GateRef numberOfElements = GetNumberOfElements(linkedTable); + GateRef numOfDelElements = GetNumberOfDeletedElements(linkedTable); + GateRef nof = Int32Add(numberOfElements, numOfAddElements); + GateRef capacity = GetCapacity(linkedTable); + GateRef less = Int32LessThan(nof, capacity); + GateRef half = Int32Div(Int32Sub(capacity, nof), Int32(2)); + GateRef lessHalf = Int32LessThanOrEqual(numOfDelElements, half); + + Label lessLable(env); + Branch(BoolAnd(less, lessHalf), &lessLable, &exit); + Bind(&lessLable); + { + Label need(env); + Branch(Int32LessThanOrEqual(Int32Add(nof, Int32Div(nof, Int32(2))), capacity), &need, &exit); + Bind(&need); + { + res = True(); + Jump(&exit); + } + } + Bind(&exit); + auto ret = *res; + env->SubCfgExit(); + return ret; +} + +template +GateRef LinkedHashTableStubBuilder::GetHash(GateRef key) +{ + auto env = GetEnvironment(); + Label entryLabel(env); + Label exit(env); + env->SubCfgEntry(&entryLabel); + DEFVARIABLE(res, VariableType::INT32(), Int32(0)); + + Label symbolKey(env); + Label stringCheck(env); + Branch(TaggedIsSymbol(key), &symbolKey, &stringCheck); + Bind(&symbolKey); + { + res = Load(VariableType::INT32(), key, IntPtr(JSSymbol::HASHFIELD_OFFSET)); + Jump(&exit); + } + Bind(&stringCheck); + Label stringKey(env); + Label slowGetHash(env); + Branch(TaggedIsString(key), &stringKey, &slowGetHash); + Bind(&stringKey); + { + res = GetHashcodeFromString(glue_, key); + Jump(&exit); + } + Bind(&slowGetHash); + { + // GetHash(); + GateRef hash = CallRuntime(glue_, RTSTUB_ID(GetLinkedHash), { key }); + res = GetInt32OfTInt(hash); + Jump(&exit); + } + Bind(&exit); + auto ret = *res; + env->SubCfgExit(); + return ret; +} + +template +GateRef LinkedHashTableStubBuilder::HashObjectIsMatch( + GateRef key, GateRef other) +{ + return SameValueZero(glue_, key, other); +} + +template +GateRef LinkedHashTableStubBuilder::FindElement( + GateRef linkedTable, GateRef key) +{ + auto env = GetEnvironment(); + Label entryLabel(env); + env->SubCfgEntry(&entryLabel); + + DEFVARIABLE(res, VariableType::INT32(), Int32(-1)); + Label exit(env); + Label isKey(env); + Branch(IsKey(key), &isKey, &exit); + Bind(&isKey); + { + GateRef hash = GetHash(key); + GateRef bucket = HashToBucket(linkedTable, hash); + GateRef index = BucketToIndex(bucket); + DEFVARIABLE(entry, VariableType::JS_ANY(), GetElement(linkedTable, index)); + Label loopHead(env); + Label loopEnd(env); + Label next(env); + Label loopExit(env); + + Jump(&loopHead); + LoopBegin(&loopHead); + { + Branch(TaggedIsHole(*entry), &loopExit, &next); + Bind(&next); + + DEFVARIABLE(element, VariableType::JS_ANY(), GetKey(linkedTable, TaggedGetInt(*entry))); + Label notHole(env); + Branch(TaggedIsHole(*element), &loopEnd, ¬Hole); + Bind(¬Hole); + { + Label weak(env); + Label notWeak(env); + Branch(TaggedIsWeak(*element), &weak, ¬Weak); + Bind(&weak); + { + element = RemoveTaggedWeakTag(*element); + Jump(¬Weak); + } + Bind(¬Weak); + Label match(env); + Branch(HashObjectIsMatch(key, *element), &match, &loopEnd); + Bind(&match); + { + res = TaggedGetInt(*entry); + Jump(&loopExit); + } + } + } + Bind(&loopEnd); + entry = GetNextEntry(linkedTable, TaggedGetInt(*entry)); + LoopEnd(&loopHead); + Bind(&loopExit); + Jump(&exit); + } + Bind(&exit); + auto ret = *res; + env->SubCfgExit(); + return ret; +} + +template +GateRef LinkedHashTableStubBuilder::GetDeletedElementsAt( + GateRef linkedTable, GateRef entry) +{ + auto env = GetEnvironment(); + Label entryLabel(env); + env->SubCfgEntry(&entryLabel); + Label exit(env); + DEFVARIABLE(res, VariableType::INT32(), Int32(0)); + DEFVARIABLE(currentEntry, VariableType::INT32(), Int32Sub(entry, Int32(1))); + Label loopHead(env); + Label loopEnd(env); + Label next(env); + Label loopExit(env); + + Jump(&loopHead); + LoopBegin(&loopHead); + { + Branch(Int32GreaterThanOrEqual(*currentEntry, Int32(0)), &next, &loopExit); + Bind(&next); + GateRef key = GetKey(linkedTable, *currentEntry); + Label hole(env); + Branch(TaggedIsHole(key), &hole, &loopEnd); + Bind(&hole); + { + GateRef deletedNum = GetDeletedNum(linkedTable, *currentEntry); + res = deletedNum; + Jump(&exit); + } + } + Bind(&loopEnd); + currentEntry = Int32Sub(*currentEntry, Int32(1)); + LoopEnd(&loopHead); + Bind(&loopExit); + Jump(&exit); + Bind(&exit); + auto ret = *res; + env->SubCfgExit(); + return ret; +} + +template +GateRef LinkedHashTableStubBuilder::Create(GateRef numberOfElements) +{ + auto env = GetEnvironment(); + Label entry(env); + env->SubCfgEntry(&entry); + Label exit(env); + + // new LinkedHashTable + GateRef length = CalNewTaggedArrayLength(numberOfElements); + NewObjectStubBuilder newBuilder(this); + GateRef array = newBuilder.NewTaggedArray(glue_, length); + + Label noException(env); + Branch(TaggedIsException(array), &exit, &noException); + Bind(&noException); + { + // SetNumberOfElements + SetNumberOfElements(array, Int32(0)); + // SetNumberOfDeletedElements + SetNumberOfDeletedElements(array, Int32(0)); + // SetCapacity + SetCapacity(array, numberOfElements); + Jump(&exit); + } + Bind(&exit); + env->SubCfgExit(); + return array; +} + +template +GateRef LinkedHashTableStubBuilder::Clear(GateRef linkedTable) +{ + auto env = GetEnvironment(); + Label entry(env); + env->SubCfgEntry(&entry); + Label exit(env); + Label setLinked(env); + + GateRef newTable = Create(Int32(LinkedHashTableType::MIN_CAPACITY)); + Label noException(env); + Branch(TaggedIsException(newTable), &exit, &noException); + Bind(&noException); + + GateRef cap = GetCapacity(linkedTable); + Label capGreaterZero(env); + Branch(Int32GreaterThan(cap, Int32(0)), &capGreaterZero, &exit); + Bind(&capGreaterZero); + { + // NextTable + SetNextTable(linkedTable, newTable); + // SetNumberOfDeletedElements + SetNumberOfDeletedElements(linkedTable, Int32(-1)); + Jump(&exit); + } + + Bind(&exit); + env->SubCfgExit(); + return newTable; +} + +template GateRef LinkedHashTableStubBuilder::Clear(GateRef); +template GateRef LinkedHashTableStubBuilder::Clear(GateRef); + +template +GateRef LinkedHashTableStubBuilder::ForEach(GateRef thisValue, + GateRef srcLinkedTable, GateRef numArgs) +{ + auto env = GetEnvironment(); + Label entry(env); + env->SubCfgEntry(&entry); + Label exit(env); + DEFVARIABLE(res, VariableType::JS_ANY(), Undefined()); + + // caller checked callbackFnHandle callable + GateRef callbackFnHandle = GetCallArg0(numArgs); + GateRef thisArg = GetCallArg1(numArgs); + DEFVARIABLE(linkedTable, VariableType::JS_ANY(), srcLinkedTable); + + GateRef numberOfElements = GetNumberOfElements(*linkedTable); + GateRef numberOfDeletedElements = GetNumberOfDeletedElements(*linkedTable); + GateRef tmpTotalElements = Int32Add(numberOfElements, numberOfDeletedElements); + DEFVARIABLE(totalElements, VariableType::INT32(), tmpTotalElements); + DEFVARIABLE(index, VariableType::INT32(), Int32(0)); + + Label loopHead(env); + Label loopEnd(env); + Label next(env); + Label loopExit(env); + Jump(&loopHead); + LoopBegin(&loopHead); + { + Branch(Int32LessThan(*index, *totalElements), &next, &loopExit); + Bind(&next); + GateRef valueIndex = *index; + + GateRef key = GetKey(*linkedTable, *index); + index = Int32Add(*index, Int32(1)); + Label keyNotHole(env); + Branch(TaggedIsHole(key), &loopEnd, &keyNotHole); + Bind(&keyNotHole); + + GateRef value = key; + if constexpr (std::is_same_v) { + value = GetValue(*linkedTable, valueIndex); + } + Label hasException(env); + Label notHasException(env); + GateRef retValue = JSCallDispatch(glue_, callbackFnHandle, Int32(NUM_MANDATORY_JSFUNC_ARGS), 0, + Circuit::NullGate(), JSCallMode::CALL_THIS_ARG3_WITH_RETURN, { thisArg, value, key, thisValue }); + Branch(HasPendingException(glue_), &hasException, ¬HasException); + Bind(&hasException); + { + res = retValue; + Jump(&exit); + } + Bind(¬HasException); + { + // Maybe add or delete, get next table + GateRef tmpNextTable = GetNextTable(*linkedTable); + DEFVARIABLE(nextTable, VariableType::JS_ANY(), tmpNextTable); + Label loopHead1(env); + Label loopEnd1(env); + Label next1(env); + Label loopExit1(env); + Jump(&loopHead1); + LoopBegin(&loopHead1); + { + Branch(TaggedIsHole(*nextTable), &loopExit1, &next1); + Bind(&next1); + GateRef deleted = GetDeletedElementsAt(*linkedTable, *index); + index = Int32Sub(*index, deleted); + linkedTable = *nextTable; + nextTable = GetNextTable(*linkedTable); + Jump(&loopEnd1); + } + Bind(&loopEnd1); + LoopEnd(&loopHead1); + Bind(&loopExit1); + // update totalElements + GateRef numberOfEle = GetNumberOfElements(*linkedTable); + GateRef numberOfDeletedEle = GetNumberOfDeletedElements(*linkedTable); + totalElements = Int32Add(numberOfEle, numberOfDeletedEle); + Jump(&loopEnd); + } + } + Bind(&loopEnd); + LoopEnd(&loopHead); + Bind(&loopExit); + Jump(&exit); + + Bind(&exit); + env->SubCfgExit(); + return *res; +} + +template GateRef LinkedHashTableStubBuilder::ForEach(GateRef thisValue, + GateRef linkedTable, GateRef numArgs); +template GateRef LinkedHashTableStubBuilder::ForEach(GateRef thisValue, + GateRef linkedTable, GateRef numArgs); + +template +GateRef LinkedHashTableStubBuilder::Insert( + GateRef linkedTable, GateRef key, GateRef value) +{ + auto env = GetEnvironment(); + Label cfgEntry(env); + env->SubCfgEntry(&cfgEntry); + Label exit(env); + DEFVARIABLE(res, VariableType::JS_ANY(), linkedTable); + GateRef entry = FindElement(linkedTable, key); + Label findEntry(env); + Label notFind(env); + Branch(Int32Equal(entry, Int32(-1)), ¬Find, &findEntry); + Bind(&findEntry); + { + SetValue(linkedTable, entry, value); + Jump(&exit); + } + Bind(¬Find); + { + GateRef newTable = GrowCapacity(linkedTable, Int32(1)); + res = newTable; + GateRef hash = GetHash(key); + GateRef bucket = HashToBucket(newTable, hash); + GateRef numberOfElements = GetNumberOfElements(newTable); + + GateRef newEntry = Int32Add(numberOfElements, GetNumberOfDeletedElements(newTable)); + InsertNewEntry(newTable, bucket, newEntry); + SetKey(newTable, newEntry, key); + SetValue(newTable, newEntry, value); + GateRef newNumberOfElements = Int32Add(numberOfElements, Int32(1)); + SetNumberOfElements(newTable, newNumberOfElements); + Jump(&exit); + } + + Bind(&exit); + auto ret = *res; + env->SubCfgExit(); + return ret; +} + +template GateRef LinkedHashTableStubBuilder::Insert( + GateRef linkedTable, GateRef key, GateRef value); +template GateRef LinkedHashTableStubBuilder::Insert( + GateRef linkedTable, GateRef key, GateRef value); + +template +GateRef LinkedHashTableStubBuilder::Delete( + GateRef linkedTable, GateRef key) +{ + auto env = GetEnvironment(); + Label cfgEntry(env); + env->SubCfgEntry(&cfgEntry); + Label exit(env); + DEFVARIABLE(res, VariableType::JS_ANY(), TaggedFalse()); + GateRef entry = FindElement(linkedTable, key); + Label findEntry(env); + Branch(Int32Equal(entry, Int32(-1)), &exit, &findEntry); + Bind(&findEntry); + { + RemoveEntry(linkedTable, entry); + res = TaggedTrue(); + Jump(&exit); + } + + Bind(&exit); + auto ret = *res; + env->SubCfgExit(); + return ret; +} + +template GateRef LinkedHashTableStubBuilder::Delete( + GateRef linkedTable, GateRef key); +template GateRef LinkedHashTableStubBuilder::Delete( + GateRef linkedTable, GateRef key); + +template +GateRef LinkedHashTableStubBuilder::Has( + GateRef linkedTable, GateRef key) +{ + auto env = GetEnvironment(); + Label cfgEntry(env); + env->SubCfgEntry(&cfgEntry); + Label exit(env); + DEFVARIABLE(res, VariableType::JS_ANY(), TaggedFalse()); + GateRef entry = FindElement(linkedTable, key); + Label findEntry(env); + Branch(Int32Equal(entry, Int32(-1)), &exit, &findEntry); + Bind(&findEntry); + { + res = TaggedTrue(); + Jump(&exit); + } + + Bind(&exit); + auto ret = *res; + env->SubCfgExit(); + return ret; +} + +template GateRef LinkedHashTableStubBuilder::Has( + GateRef linkedTable, GateRef key); +template GateRef LinkedHashTableStubBuilder::Has( + GateRef linkedTable, GateRef key); +} // namespace panda::ecmascript::kungfu diff --git a/ecmascript/compiler/builtins/linked_hashtable_stub_builder.h b/ecmascript/compiler/builtins/linked_hashtable_stub_builder.h new file mode 100644 index 0000000000000000000000000000000000000000..74d60f3b52d48cc7b22decd44fdd8e018caf8d23 --- /dev/null +++ b/ecmascript/compiler/builtins/linked_hashtable_stub_builder.h @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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_COMPILER_BUILTINS_LINKED_HASHTABLE_STUB_BUILDER_H +#define ECMASCRIPT_COMPILER_BUILTINS_LINKED_HASHTABLE_STUB_BUILDER_H +#include "ecmascript/compiler/stub_builder-inl.h" + +namespace panda::ecmascript::kungfu { + +template +class LinkedHashTableStubBuilder : public BuiltinsStubBuilder { +public: + explicit LinkedHashTableStubBuilder(BuiltinsStubBuilder *parent, GateRef glue) + : BuiltinsStubBuilder(parent), glue_(glue) {} + ~LinkedHashTableStubBuilder() override = default; + NO_MOVE_SEMANTIC(LinkedHashTableStubBuilder); + NO_COPY_SEMANTIC(LinkedHashTableStubBuilder); + void GenerateCircuit() override {} + + GateRef Create(GateRef numberOfElements); + GateRef Clear(GateRef linkedTable); + GateRef ForEach(GateRef thisValue, GateRef linkedTable, GateRef numArgs); + GateRef Insert(GateRef linkedTable, GateRef key, GateRef value); + GateRef Delete(GateRef linkedTable, GateRef key); + GateRef Has(GateRef linkedTable, GateRef key); + +private: + GateRef IsKey(GateRef key) + { + return TaggedIsNotHole(key); + } + + GateRef HashToBucket(GateRef linkedTable, GateRef hash) + { + GateRef cap = GetCapacity(linkedTable); + return Int32And(hash, Int32Sub(cap, Int32(1))); + } + + GateRef BucketToIndex(GateRef bucket) + { + return Int32Add(bucket, Int32(LinkedHashTableType::ELEMENTS_START_INDEX)); + } + + GateRef GetHash(GateRef key); + GateRef HashObjectIsMatch(GateRef key, GateRef other); + GateRef FindElement(GateRef linkedTable, GateRef key); + GateRef GetKey(GateRef linkedTable, GateRef entry) + { + GateRef index = EntryToIndex(linkedTable, entry); + return GetElement(linkedTable, index); + } + + void SetKey(GateRef linkedTable, GateRef entry, GateRef key) + { + GateRef index = EntryToIndex(linkedTable, entry); + SetElement(linkedTable, index, key); + } + + GateRef GetValue(GateRef linkedTable, GateRef entry) + { + GateRef index = EntryToIndex(linkedTable, entry); + GateRef valueIndex = Int32(LinkedHashTableObject::ENTRY_VALUE_INDEX); + return GetElement(linkedTable, Int32Add(index, valueIndex)); + } + + void SetValue(GateRef linkedTable, GateRef entry, GateRef value) + { + GateRef index = EntryToIndex(linkedTable, entry); + GateRef valueIndex = Int32(LinkedHashTableObject::ENTRY_VALUE_INDEX); + SetElement(linkedTable, Int32Add(index, valueIndex), value); + } + + GateRef EntryToIndex(GateRef linkedTable, GateRef entry) + { + int32_t startIndex = LinkedHashTableType::ELEMENTS_START_INDEX; + int32_t entrySize = LinkedHashTableObject::ENTRY_SIZE; + GateRef sumEntrySize = Int32Mul(entry, Int32Add(Int32(entrySize), Int32(1))); + return Int32Add(Int32(startIndex), Int32Add(GetCapacity(linkedTable), sumEntrySize)); + } + + GateRef GetElement(GateRef linkedTable, GateRef index) + { + return GetValueFromTaggedArray(linkedTable, index); + } + + void SetElement(GateRef linkedTable, GateRef index, GateRef value) + { + SetValueToTaggedArray(VariableType::JS_ANY(), glue_, linkedTable, index, value); + } + + GateRef GetDeletedNum(GateRef linkedTable, GateRef entry) + { + return TaggedGetInt(GetNextEntry(linkedTable, entry)); + } + + void SetDeletedNum(GateRef linkedTable, GateRef entry, GateRef num) + { + SetNextEntry(linkedTable, entry, IntToTaggedInt(num)); + } + + GateRef GetNextEntry(GateRef linkedTable, GateRef entry) + { + GateRef entryIndex = EntryToIndex(linkedTable, entry); + return GetElement(linkedTable, Int32Add(entryIndex, Int32(LinkedHashTableObject::ENTRY_SIZE))); + } + + void SetNextEntry(GateRef linkedTable, GateRef entry, GateRef nextEntry) + { + GateRef entryIndex = EntryToIndex(linkedTable, entry); + SetElement(linkedTable, Int32Add(entryIndex, Int32(LinkedHashTableObject::ENTRY_SIZE)), nextEntry); + } + + GateRef GetCapacity(GateRef linkedTable) + { + GateRef capacityIndex = Int32(LinkedHashTableType::CAPACITY_INDEX); + GateRef capacity = GetValueFromTaggedArray(linkedTable, capacityIndex); + return TaggedGetInt(capacity); + } + + void SetCapacity(GateRef linkedTable, GateRef numberOfElements) + { + GateRef capacityIndex = Int32(LinkedHashTableType::CAPACITY_INDEX); + SetValueToTaggedArray(VariableType::JS_NOT_POINTER(), glue_, linkedTable, capacityIndex, + IntToTaggedInt(numberOfElements)); + } + + GateRef GetNumberOfElements(GateRef linkedTable) + { + int32_t elementsIndex = LinkedHashTableType::NUMBER_OF_ELEMENTS_INDEX; + GateRef tmpNumberOfElements = GetValueFromTaggedArray(linkedTable, Int32(elementsIndex)); + return TaggedGetInt(tmpNumberOfElements); + } + + void SetNumberOfElements(GateRef linkedTable, GateRef num) + { + int32_t elementsIndex = LinkedHashTableType::NUMBER_OF_ELEMENTS_INDEX; + SetValueToTaggedArray(VariableType::JS_NOT_POINTER(), glue_, linkedTable, Int32(elementsIndex), + IntToTaggedInt(num)); + } + + GateRef GetNumberOfDeletedElements(GateRef linkedTable) + { + GateRef deletedIndex = Int32(LinkedHashTableType::NUMBER_OF_DELETED_ELEMENTS_INDEX); + GateRef tmpNumberOfDeletedElements = GetValueFromTaggedArray(linkedTable, deletedIndex); + return TaggedGetInt(tmpNumberOfDeletedElements); + } + + void SetNumberOfDeletedElements(GateRef linkedTable, GateRef num) + { + GateRef deletedIndex = Int32(LinkedHashTableType::NUMBER_OF_DELETED_ELEMENTS_INDEX); + SetValueToTaggedArray(VariableType::JS_NOT_POINTER(), glue_, linkedTable, deletedIndex, IntToTaggedInt(num)); + } + + GateRef GetNextTable(GateRef linkedTable) + { + GateRef nextTableIndex = Int32(LinkedHashTableType::NEXT_TABLE_INDEX); + return GetValueFromTaggedArray(linkedTable, nextTableIndex); + } + + void SetNextTable(GateRef linkedTable, GateRef nexTable) + { + GateRef nextTableIndex = Int32(LinkedHashTableType::NEXT_TABLE_INDEX); + SetValueToTaggedArray(VariableType::JS_POINTER(), glue_, linkedTable, nextTableIndex, nexTable); + } + + GateRef CalNewTaggedArrayLength(GateRef numberOfElements) + { + GateRef startIndex = Int32(LinkedHashTableType::ELEMENTS_START_INDEX); + GateRef entrySize = Int32(LinkedHashTableObject::ENTRY_SIZE); + GateRef nEntrySize = Int32Mul(numberOfElements, Int32Add(entrySize, Int32(1))); + GateRef length = Int32Add(startIndex, Int32Add(numberOfElements, nEntrySize)); + return length; + } + + void InsertNewEntry(GateRef linkedTable, GateRef bucket, GateRef entry) + { + GateRef bucketIndex = BucketToIndex(bucket); + GateRef previousEntry = GetElement(linkedTable, bucketIndex); + SetNextEntry(linkedTable, entry, previousEntry); + SetElement(linkedTable, bucketIndex, IntToTaggedInt(entry)); + } + + GateRef GetDeletedElementsAt(GateRef linkedTable, GateRef entry); + GateRef GrowCapacity(GateRef linkedTable, GateRef numberOfAddedElements); + GateRef HasSufficientCapacity(GateRef linkedTable, GateRef numOfAddElements); + void Rehash(GateRef linkedTable, GateRef newTable); + GateRef ComputeCapacity(GateRef atLeastSpaceFor); + void RemoveEntry(GateRef linkedTable, GateRef entry); + + GateRef glue_; +}; +} // namespace panda::ecmascript::kungfu +#endif // ECMASCRIPT_COMPILER_BUILTINS_LINKED_HASHTABLE_STUB_BUILDER_H diff --git a/ecmascript/compiler/builtins_lowering.cpp b/ecmascript/compiler/builtins_lowering.cpp index f258082c724c0527a056647a93b4fde2130f02ef..0d4bf368e807fa72e2a6c4ff753f5fc3b5e3e95c 100644 --- a/ecmascript/compiler/builtins_lowering.cpp +++ b/ecmascript/compiler/builtins_lowering.cpp @@ -33,6 +33,15 @@ void BuiltinLowering::LowerTypedCallBuitin(GateRef gate) case BUILTINS_STUB_ID(ATAN): LowerTypedTrigonometric(gate, id); break; + case BUILTINS_STUB_ID(LocaleCompare): + LowerTypedLocaleCompare(gate); + break; + case BUILTINS_STUB_ID(SORT): + LowerTypedArraySort(gate); + break; + case BUILTINS_STUB_ID(STRINGIFY): + LowerTypedStringify(gate); + break; default: break; } @@ -176,6 +185,72 @@ GateRef BuiltinLowering::TypedAbs(GateRef gate) return ret; } +GateRef BuiltinLowering::LowerCallRuntime(GateRef glue, GateRef gate, int index, const std::vector &args, + bool useLabel) +{ + const std::string name = RuntimeStubCSigns::GetRTName(index); + if (useLabel) { + GateRef result = builder_.CallRuntime(glue, index, Gate::InvalidGateRef, args, gate, name.c_str()); + return result; + } else { + const CallSignature *cs = RuntimeStubCSigns::Get(RTSTUB_ID(CallRuntime)); + GateRef target = builder_.IntPtr(index); + GateRef result = builder_.Call(cs, glue, target, builder_.GetDepend(), args, gate, name.c_str()); + return result; + } +} + +void BuiltinLowering::ReplaceHirWithValue(GateRef hirGate, GateRef value, bool noThrow) +{ + if (!noThrow) { + GateRef state = builder_.GetState(); + // copy depend-wire of hirGate to value + GateRef depend = builder_.GetDepend(); + // exception value + GateRef exceptionVal = builder_.ExceptionConstant(); + // compare with trampolines result + GateRef equal = builder_.Equal(value, exceptionVal); + auto ifBranch = builder_.Branch(state, equal); + + GateRef ifTrue = builder_.IfTrue(ifBranch); + GateRef ifFalse = builder_.IfFalse(ifBranch); + GateRef eDepend = builder_.DependRelay(ifTrue, depend); + GateRef sDepend = builder_.DependRelay(ifFalse, depend); + StateDepend success(ifFalse, sDepend); + StateDepend exception(ifTrue, eDepend); + acc_.ReplaceHirWithIfBranch(hirGate, success, exception, value); + } else { + acc_.ReplaceHirDirectly(hirGate, builder_.GetStateDepend(), value); + } +} + +void BuiltinLowering::LowerTypedLocaleCompare(GateRef gate) +{ + GateRef glue = acc_.GetGlueFromArgList(); + uint32_t index = 0; + GateRef thisObj = acc_.GetValueIn(gate, index++); + GateRef a0 = acc_.GetValueIn(gate, index++); + GateRef a1 = acc_.GetValueIn(gate, index++); + GateRef a2 = acc_.GetValueIn(gate, index++); + + std::vector args; + args.reserve(index); + args.emplace_back(thisObj); + args.emplace_back(a0); + args.emplace_back(a1); + args.emplace_back(a2); + GateRef result = LowerCallRuntime(glue, gate, RTSTUB_ID(LocaleCompare), args); + ReplaceHirWithValue(gate, result); +} + +void BuiltinLowering::LowerTypedArraySort(GateRef gate) +{ + GateRef glue = acc_.GetGlueFromArgList(); + GateRef thisObj = acc_.GetValueIn(gate, 0); + GateRef result = LowerCallRuntime(glue, gate, RTSTUB_ID(ArraySort), { thisObj }); + ReplaceHirWithValue(gate, result); +} + GateRef BuiltinLowering::LowerCallTargetCheck(Environment *env, GateRef gate) { builder_.SetEnvironment(env); @@ -205,10 +280,24 @@ GateRef BuiltinLowering::CheckPara(GateRef gate, GateRef funcCheck) case BuiltinsStubCSigns::ID::SQRT: // NumberSpeculativeRetype is checked return funcCheck; + case BuiltinsStubCSigns::ID::LocaleCompare: + case BuiltinsStubCSigns::ID::SORT: + // Don't need check para + return funcCheck; default: { LOG_COMPILER(FATAL) << "this branch is unreachable"; UNREACHABLE(); } } } + +void BuiltinLowering::LowerTypedStringify(GateRef gate) +{ + GateRef glue = acc_.GetGlueFromArgList(); + GateRef value = acc_.GetValueIn(gate, 0); + std::vector args; + args.emplace_back(value); + GateRef result = LowerCallRuntime(glue, gate, RTSTUB_ID(FastStringify), args); + ReplaceHirWithValue(gate, result); +} } // namespace panda::ecmascript::kungfu diff --git a/ecmascript/compiler/builtins_lowering.h b/ecmascript/compiler/builtins_lowering.h index d6924176256df32b8bbd6d122ef9188cdad07b02..068bcc494f39ec3cfd0e3111e2007491040f38d7 100644 --- a/ecmascript/compiler/builtins_lowering.h +++ b/ecmascript/compiler/builtins_lowering.h @@ -30,12 +30,19 @@ public: GateRef LowerCallTargetCheck(Environment *env, GateRef gate); void LowerTypedSqrt(GateRef gate); GateRef CheckPara(GateRef gate, GateRef funcCheck); + void LowerTypedLocaleCompare(GateRef gate); + void LowerTypedArraySort(GateRef gate); + private: void LowerTypedTrigonometric(GateRef gate, BuiltinsStubCSigns::ID id); GateRef TypedTrigonometric(GateRef gate, BuiltinsStubCSigns::ID id); GateRef IntToTaggedIntPtr(GateRef x); void LowerTypedAbs(GateRef gate); GateRef TypedAbs(GateRef gate); + GateRef LowerCallRuntime(GateRef glue, GateRef gate, int index, const std::vector &args, + bool useLabel = false); + void ReplaceHirWithValue(GateRef hirGate, GateRef value, bool noThrow = false); + void LowerTypedStringify(GateRef gate); Circuit *circuit_ {nullptr}; CircuitBuilder builder_; diff --git a/ecmascript/compiler/bytecode_circuit_builder.cpp b/ecmascript/compiler/bytecode_circuit_builder.cpp index c3e3bc8a0d3e8a3cb5aeacfbadba5cd8b1478642..b20f4474047c2ff1de607f00da4bb11fd1158198 100644 --- a/ecmascript/compiler/bytecode_circuit_builder.cpp +++ b/ecmascript/compiler/bytecode_circuit_builder.cpp @@ -538,20 +538,25 @@ void BytecodeCircuitBuilder::BuildCircuitArgs() void BytecodeCircuitBuilder::BuildFrameArgs() { - auto metaData = circuit_->FrameArgs(); - size_t numArgs = static_cast(FrameArgIdx::NUM_OF_ARGS); + UInt32PairAccessor accessor(0, 0); + auto metaData = circuit_->FrameArgs(accessor.ToValue()); + size_t numArgs = metaData->GetNumIns(); std::vector args(numArgs, Circuit::NullGate()); size_t idx = 0; args[idx++] = argAcc_.GetCommonArgGate(CommonArgIdx::FUNC); args[idx++] = argAcc_.GetCommonArgGate(CommonArgIdx::NEW_TARGET); args[idx++] = argAcc_.GetCommonArgGate(CommonArgIdx::THIS_OBJECT); args[idx++] = argAcc_.GetCommonArgGate(CommonArgIdx::ACTUAL_ARGC); + args[idx++] = GetPreFrameArgs(); GateRef frameArgs = circuit_->NewGate(metaData, args); argAcc_.SetFrameArgs(frameArgs); } bool BytecodeCircuitBuilder::ShouldBeDead(BytecodeRegion &curBlock) { + if (curBlock.iDominator->isDead) { + return true; + } auto isDead = false; for (auto bbPred : curBlock.preds) { if (!bbPred->isDead) { @@ -880,7 +885,7 @@ void BytecodeCircuitBuilder::NewJSGate(BytecodeRegion &bb, GateRef &state, GateR size_t numValueInputs = bytecodeInfo.ComputeValueInputCount(); GateRef gate = 0; bool writable = !bytecodeInfo.NoSideEffects(); - bool hasFrameState = bytecodeInfo.HasFrameArgs(); + bool hasFrameState = bytecodeInfo.HasFrameState(); size_t pcOffset = GetPcOffset(iterator.Index()); auto meta = circuit_->JSBytecode(numValueInputs, bytecodeInfo.GetOpcode(), pcOffset, writable, hasFrameState); std::vector inList = CreateGateInList(bytecodeInfo, meta); @@ -1052,8 +1057,10 @@ void BytecodeCircuitBuilder::BuildSubCircuit() ASSERT(stateCur != Circuit::NullGate()); ASSERT(dependCur != Circuit::NullGate()); if (IsEntryBlock(bb.id)) { - stateCur = circuit_->NewGate(circuit_->UpdateHotness(), {stateCur, dependCur}); - dependCur = stateCur; + if (NeedCheckSafePointAndStackOver()) { + stateCur = circuit_->NewGate(circuit_->CheckSafePointAndStackOver(), {stateCur, dependCur}); + dependCur = stateCur; + } auto &bbNext = graph_[bb.id + 1]; SetBlockPred(bb, bbNext, stateCur, dependCur); bbNext.expandedPreds.push_back({bb.id, bb.end, false}); @@ -1120,7 +1127,7 @@ size_t BytecodeCircuitBuilder::LoopExitCount(size_t from, size_t to) ASSERT(bbNext.loopDepth >= headDep); size_t nextDep = bbNext.loopDepth - headDep; ASSERT(bb.loopDepth >= nextDep); - return bb.loopDepth > nextDep; + return bb.loopDepth - nextDep; } GateRef BytecodeCircuitBuilder::NewValueFromPredBB(BytecodeRegion &bb, size_t idx, @@ -1246,8 +1253,18 @@ GateRef BytecodeCircuitBuilder::NewLoopExitValue(GateRef loopExit, uint16_t reg, return value; } +GateRef BytecodeCircuitBuilder::ResolveDef(const BytecodeRegion &bb, int32_t bcId, const uint16_t reg, const bool acc) +{ + // Ensure that bcId is not negative + if (bcId == 0) { + return ResolveDef(bb.id, bcId, reg, acc, false); + } + return ResolveDef(bb.id, bcId - 1, reg, acc); +} + // recursive variables renaming algorithm -GateRef BytecodeCircuitBuilder::ResolveDef(const size_t bbId, int32_t bcId, const uint16_t reg, const bool acc) +GateRef BytecodeCircuitBuilder::ResolveDef(const size_t bbId, int32_t bcId, + const uint16_t reg, const bool acc, bool needIter) { auto tmpReg = reg; // find def-site in bytecodes of basic block @@ -1256,54 +1273,56 @@ GateRef BytecodeCircuitBuilder::ResolveDef(const size_t bbId, int32_t bcId, cons GateType type = GateType::AnyType(); auto tmpAcc = acc; - BytecodeIterator iterator(this, bb.start, bcId); - for (iterator.Goto(bcId); !iterator.Done(); --iterator) { - const BytecodeInfo& curInfo = iterator.GetBytecodeInfo(); - // original bc use acc as input && current bc use acc as output - bool isTransByAcc = tmpAcc && curInfo.AccOut(); - // 0 : the index in vreg-out list - bool isTransByVreg = (!tmpAcc && curInfo.IsOut(tmpReg, 0)); - if (isTransByAcc || isTransByVreg) { - if (curInfo.IsMov()) { - tmpAcc = curInfo.AccIn(); - if (!curInfo.inputs.empty()) { - ASSERT(!tmpAcc); - ASSERT(curInfo.inputs.size() == 1); - tmpReg = std::get(curInfo.inputs.at(0)).GetId(); - } - if (HasTypes()) { - type = typeRecorder_.UpdateType(iterator.Index(), type); - } - } else { - ans = byteCodeToJSGates_.at(iterator.Index()).at(0); - auto oldType = gateAcc_.GetGateType(ans); - if (!type.IsAnyType() && oldType.IsAnyType()) { - typeRecorder_.GetOrUpdatePGOType(tsManager_, gateAcc_.TryGetPcOffset(ans), type); - gateAcc_.SetGateType(ans, type); + if (needIter) { + BytecodeIterator iterator(this, bb.start, bcId); + for (iterator.Goto(bcId); !iterator.Done(); --iterator) { + const BytecodeInfo& curInfo = iterator.GetBytecodeInfo(); + // original bc use acc as input && current bc use acc as output + bool isTransByAcc = tmpAcc && curInfo.AccOut(); + // 0 : the index in vreg-out list + bool isTransByVreg = (!tmpAcc && curInfo.IsOut(tmpReg, 0)); + if (isTransByAcc || isTransByVreg) { + if (curInfo.IsMov()) { + tmpAcc = curInfo.AccIn(); + if (!curInfo.inputs.empty()) { + ASSERT(!tmpAcc); + ASSERT(curInfo.inputs.size() == 1); + tmpReg = std::get(curInfo.inputs.at(0)).GetId(); + } + if (HasTypes()) { + type = typeRecorder_.UpdateType(iterator.Index(), type); + } + } else { + ans = byteCodeToJSGates_.at(iterator.Index()).at(0); + auto oldType = gateAcc_.GetGateType(ans); + if (!type.IsAnyType() && oldType.IsAnyType()) { + typeRecorder_.GetOrUpdatePGOType(tsManager_, gateAcc_.TryGetPcOffset(ans), type); + gateAcc_.SetGateType(ans, type); + } + break; } + } + if (curInfo.GetOpcode() != EcmaOpcode::RESUMEGENERATOR) { + continue; + } + // New RESTORE_REGISTER HIR, used to restore the register content when processing resume instruction. + // New SAVE_REGISTER HIR, used to save register content when processing suspend instruction. + auto resumeGate = byteCodeToJSGates_.at(iterator.Index()).at(0); + ans = GetExistingRestore(resumeGate, tmpReg); + if (ans != Circuit::NullGate()) { break; } - } - if (curInfo.GetOpcode() != EcmaOpcode::RESUMEGENERATOR) { - continue; - } - // New RESTORE_REGISTER HIR, used to restore the register content when processing resume instruction. - // New SAVE_REGISTER HIR, used to save register content when processing suspend instruction. - auto resumeGate = byteCodeToJSGates_.at(iterator.Index()).at(0); - ans = GetExistingRestore(resumeGate, tmpReg); - if (ans != Circuit::NullGate()) { + ans = circuit_->NewGate(circuit_->RestoreRegister(tmpReg), MachineType::I64, + { resumeGate }, GateType::AnyType()); + SetExistingRestore(resumeGate, tmpReg, ans); + auto saveRegGate = ResolveDef(bbId, iterator.Index() - 1, tmpReg, tmpAcc); + [[maybe_unused]] EcmaOpcode opcode = Bytecodes::GetOpcode(iterator.PeekPrevPc(2)); // 2: prev bc + ASSERT(opcode == EcmaOpcode::SUSPENDGENERATOR_V8 || opcode == EcmaOpcode::ASYNCGENERATORRESOLVE_V8_V8_V8); + GateRef suspendGate = byteCodeToJSGates_.at(iterator.Index() - 2).at(0); // 2: prev bc + GateRef saveRegs = gateAcc_.GetDep(suspendGate); + gateAcc_.ReplaceValueIn(saveRegs, saveRegGate, tmpReg); break; } - ans = circuit_->NewGate(circuit_->RestoreRegister(tmpReg), MachineType::I64, - { resumeGate }, GateType::AnyType()); - SetExistingRestore(resumeGate, tmpReg, ans); - auto saveRegGate = ResolveDef(bbId, iterator.Index() - 1, tmpReg, tmpAcc); - [[maybe_unused]] EcmaOpcode opcode = Bytecodes::GetOpcode(iterator.PeekPrevPc(2)); // 2: prev bc - ASSERT(opcode == EcmaOpcode::SUSPENDGENERATOR_V8 || opcode == EcmaOpcode::ASYNCGENERATORRESOLVE_V8_V8_V8); - GateRef suspendGate = byteCodeToJSGates_.at(iterator.Index() - 2).at(0); // 2: prev bc - GateRef saveRegs = gateAcc_.GetDep(suspendGate); - gateAcc_.ReplaceValueIn(saveRegs, saveRegGate, tmpReg); - break; } // find GET_EXCEPTION gate if this is a catch block if (ans == Circuit::NullGate() && tmpAcc) { @@ -1402,18 +1421,11 @@ void BytecodeCircuitBuilder::BuildCircuit() return true; } - if (HasTypes()) { - auto type = typeRecorder_.GetType(bcIndex); - if (!type.IsAnyType()) { - gateAcc_.SetGateType(gate, type); - } - auto pgoType = typeRecorder_.GetOrUpdatePGOType(tsManager_, gateAcc_.TryGetPcOffset(gate), type); - gateAcc_.TrySetPGOType(gate, pgoType); - } else { - auto type = GateType::AnyType(); - auto pgoType = typeRecorder_.GetOrUpdatePGOType(tsManager_, gateAcc_.TryGetPcOffset(gate), type); - gateAcc_.TrySetPGOType(gate, pgoType); - } + auto type = typeRecorder_.GetType(bcIndex); + gateAcc_.SetGateType(gate, type); + auto pgoType = typeRecorder_.GetOrUpdatePGOType(tsManager_, gateAcc_.TryGetPcOffset(gate), type); + gateAcc_.TrySetPGOType(gate, pgoType); + auto valueCount = gateAcc_.GetInValueCount(gate); [[maybe_unused]] size_t numValueInputs = bytecodeInfo.ComputeValueInputCount(); [[maybe_unused]] size_t numValueOutputs = bytecodeInfo.ComputeOutCount(); @@ -1431,7 +1443,7 @@ void BytecodeCircuitBuilder::BuildCircuit() size_t depCount = gateAcc_.GetNumValueIn(depIn); GateRef defVreg = Circuit::NullGate(); for (size_t idx = 0; idx < depCount; idx++) { - defVreg = ResolveDef(bbIndex, bcIndex - 1, idx, false); + defVreg = ResolveDef(bb, bcIndex, idx, false); gateAcc_.ReplaceValueIn(depIn, defVreg, idx); } } @@ -1441,11 +1453,20 @@ void BytecodeCircuitBuilder::BuildCircuit() if (IsFirstBCEnvIn(bbIndex, bcIndex, vregId)) { defVreg = gateAcc_.GetInitialEnvGate(argAcc_.GetCommonArgGate(CommonArgIdx::FUNC)); } else { - defVreg = ResolveDef(bbIndex, bcIndex - 1, vregId, false); + defVreg = ResolveDef(bb, bcIndex, vregId, false); } gateAcc_.NewIn(gate, inIdx, defVreg); } else { - GateRef defAcc = ResolveDef(bbIndex, bcIndex - 1, 0, true); + GateRef defAcc = ResolveDef(bb, bcIndex, 0, true); + if (!Bytecodes::IsCallOp(bytecodeInfo.GetOpcode())) { + gateAcc_.NewIn(gate, inIdx, defAcc); + continue; + } + auto oldGt = gateAcc_.GetGateType(defAcc).GetGTRef(); + GateType callTargetType = typeRecorder_.GetCallTargetType(bcIndex); + if (!tsManager_->MethodOffsetIsVaild(oldGt) && !callTargetType.IsAnyType()) { + gateAcc_.SetGateType(defAcc, callTargetType); + } gateAcc_.NewIn(gate, inIdx, defAcc); } } @@ -1457,8 +1478,6 @@ void BytecodeCircuitBuilder::BuildCircuit() frameStateBuilder_.BuildFrameState(); } - gateAcc_.EliminateRedundantPhi(); - if (IsLogEnabled()) { PrintGraph("Bytecode2Gate"); LOG_COMPILER(INFO) << "\033[34m" << "============= " diff --git a/ecmascript/compiler/bytecode_circuit_builder.h b/ecmascript/compiler/bytecode_circuit_builder.h index f4d755b98f1a44016d6336141f0f98a1152b1ea1..8286ef8cf6909b587dbb059f404419c238f62e36 100644 --- a/ecmascript/compiler/bytecode_circuit_builder.h +++ b/ecmascript/compiler/bytecode_circuit_builder.h @@ -264,11 +264,14 @@ public: bool enableTypeLowering, std::string name, const CString &recordName, - PGOProfilerDecoder *decoder) + PGOProfilerDecoder *decoder, + bool isInline, + bool enableOptTrackField) : tsManager_(tsManager), circuit_(circuit), file_(jsPandaFile), method_(methodLiteral), gateAcc_(circuit), argAcc_(circuit, method_), - typeRecorder_(jsPandaFile, method_, tsManager, recordName, decoder), hasTypes_(hasTypes), - enableLog_(enableLog), enableTypeLowering_(enableTypeLowering), + typeRecorder_(jsPandaFile, method_, tsManager, recordName, decoder, methodPCInfo, bytecodes, + enableOptTrackField), + hasTypes_(hasTypes), enableLog_(enableLog), enableTypeLowering_(enableTypeLowering), pcOffsets_(methodPCInfo.pcOffsets), frameStateBuilder_(this, circuit, methodLiteral), methodName_(name), recordName_(recordName), @@ -276,7 +279,9 @@ public: dfsList_(circuit->chunk()), loopExitToVregGate_(circuit->chunk()), loopExitToAccGate_(circuit->chunk()), - preFrameState_(circuit_->GetRoot()) + preFrameState_(circuit_->GetRoot()), + preFrameArgs_(circuit_->GetRoot()), + isInline_(isInline) { } ~BytecodeCircuitBuilder() = default; @@ -284,7 +289,8 @@ public: NO_MOVE_SEMANTIC(BytecodeCircuitBuilder); void PUBLIC_API BytecodeToCircuit(); void CollectRegionInfo(uint32_t bcIndex); - GateRef ResolveDef(const size_t bbId, int32_t bcId, const uint16_t reg, const bool acc); + GateRef ResolveDef(const size_t bbId, int32_t bcId, const uint16_t reg, const bool acc, bool needIter = true); + GateRef ResolveDef(const BytecodeRegion &bb, int32_t bcId, const uint16_t reg, const bool acc); [[nodiscard]] Circuit* GetCircuit() const { @@ -312,6 +318,19 @@ public: return jsGatesToByteCode_.at(gate); } + bool IsBcIndexByGate(GateRef gate) const + { + if (jsGatesToByteCode_.find(gate) == jsGatesToByteCode_.end()) { + return false; + } + return true; + } + + bool NeedCheckSafePointAndStackOver() const + { + return !isInline_ && !method_->IsNoGC(); + } + void UpdateBcIndexGate(GateRef gate, uint32_t bcIndex) { ASSERT(gateAcc_.GetOpCode(gate) == OpCode::JS_BYTECODE); @@ -404,6 +423,33 @@ public: return GetPcOffset(pc); } + size_t GetPcOffsetByGate(GateRef gate) const + { + return GetPcOffset(jsGatesToByteCode_.at(gate)); + } + + PGORWOpType GetPGOType(GateRef gate) const + { + return typeRecorder_.GetRwOpType(GetPcOffsetByGate(gate)); + } + + std::vector LoadElementsKinds(GateRef gate) const + { + return typeRecorder_.LoadElementsKinds(GetPcOffsetByGate(gate)); + } + + ElementsKind GetArrayElementsKind(GateRef gate) const + { + auto type = typeRecorder_.GetType(GetBcIndexByGate(gate)); + auto pgoType = typeRecorder_.GetOrUpdatePGOType(tsManager_, gateAcc_.TryGetPcOffset(gate), type); + return typeRecorder_.GetElementsKind(pgoType); + } + + bool ShouldPGOTypeInfer(GateRef gate) const + { + return jsGatesToByteCode_.find(gate) != jsGatesToByteCode_.end(); + } + size_t GetNumberVRegs() const { return static_cast(method_->GetNumberVRegs()); @@ -470,6 +516,8 @@ public: return (!HasTryCatch()) && (loopHeads_.size() != 0); } + size_t LoopExitCount(size_t from, size_t to); + GateRef GetFrameArgs() const { return argAcc_.GetFrameArgs(); @@ -485,6 +533,16 @@ public: preFrameState_ = gate; } + GateRef GetPreFrameArgs() const + { + return preFrameArgs_; + } + + void SetPreFrameArgs(GateRef gate) + { + preFrameArgs_ = gate; + } + const ChunkVector& GetDfsList() const { return dfsList_; @@ -550,7 +608,6 @@ private: void PrintDefsitesInfo(const std::unordered_map> &defsitesInfo); void BuildRegionInfo(); void BuildFrameArgs(); - size_t LoopExitCount(size_t from, size_t to); void CollectLoopBack(); void ComputeLoopDepth(size_t loopHead); void CountLoopBackEdge(size_t fromId, size_t toId); @@ -589,6 +646,8 @@ private: ChunkMap, GateRef> loopExitToVregGate_; ChunkMap loopExitToAccGate_; GateRef preFrameState_ {Circuit::NullGate()}; + GateRef preFrameArgs_ {Circuit::NullGate()}; + bool isInline_ {false}; }; } // namespace panda::ecmascript::kungfu #endif // ECMASCRIPT_CLASS_LINKER_BYTECODE_CIRCUIT_IR_BUILDER_H diff --git a/ecmascript/compiler/bytecode_info_collector.cpp b/ecmascript/compiler/bytecode_info_collector.cpp index 8d492650f57de8f2b3e3a4ea63f9155336bd094f..61c91e4e024d602a6502c36eca52835c62775188 100644 --- a/ecmascript/compiler/bytecode_info_collector.cpp +++ b/ecmascript/compiler/bytecode_info_collector.cpp @@ -15,10 +15,11 @@ #include "ecmascript/compiler/bytecode_info_collector.h" -#include "ecmascript/base/path_helper.h" #include "ecmascript/compiler/type_recorder.h" #include "ecmascript/interpreter/interpreter-inl.h" #include "ecmascript/jspandafile/type_literal_extractor.h" +#include "ecmascript/module/module_path_helper.h" +#include "ecmascript/pgo_profiler/pgo_profiler_decoder.h" #include "ecmascript/ts_types/ts_type_parser.h" #include "libpandafile/code_data_accessor.h" @@ -29,11 +30,12 @@ static T *InitializeMemory(T *mem, Args... args) return new (mem) T(std::forward(args)...); } -BytecodeInfoCollector::BytecodeInfoCollector(EcmaVM *vm, JSPandaFile *jsPandaFile, +BytecodeInfoCollector::BytecodeInfoCollector(EcmaVM *vm, JSPandaFile *jsPandaFile, PGOProfilerDecoder &pfDecoder, size_t maxAotMethodSize, bool enableCollectLiteralInfo) : vm_(vm), jsPandaFile_(jsPandaFile), - bytecodeInfo_(maxAotMethodSize), + bytecodeInfo_(maxAotMethodSize, jsPandaFile), + pfDecoder_(pfDecoder), enableCollectLiteralInfo_(enableCollectLiteralInfo) { vm_->GetJSThread()->GetCurrentEcmaContext()->GetTSManager()->SetBytecodeInfoCollector(this); @@ -65,7 +67,7 @@ void BytecodeInfoCollector::ProcessClasses() MethodLiteral *methods = jsPandaFile_->GetMethodLiterals(); const panda_file::File *pf = jsPandaFile_->GetPandaFile(); size_t methodIdx = 0; - std::map> processedInsns; + std::map> processedMethod; Span classIndexes = jsPandaFile_->GetClasses(); auto &recordNames = bytecodeInfo_.GetRecordNames(); @@ -80,7 +82,7 @@ void BytecodeInfoCollector::ProcessClasses() panda_file::ClassDataAccessor cda(*pf, classId); CString desc = utf::Mutf8AsCString(cda.GetDescriptor()); const CString recordName = JSPandaFile::ParseEntryPoint(desc); - cda.EnumerateMethods([this, methods, &methodIdx, pf, &processedInsns, + cda.EnumerateMethods([this, methods, &methodIdx, pf, &processedMethod, &recordNames, &methodPcInfos, &recordName, &methodIndexes, &classConstructIndexes] (panda_file::MethodDataAccessor &mda) { auto methodId = mda.GetMethodId(); @@ -112,20 +114,21 @@ void BytecodeInfoCollector::ProcessClasses() panda_file::CodeDataAccessor codeDataAccessor(*pf, codeId.value()); uint32_t codeSize = codeDataAccessor.GetCodeSize(); const uint8_t *insns = codeDataAccessor.GetInstructions(); - auto it = processedInsns.find(insns); - if (it == processedInsns.end()) { + auto it = processedMethod.find(methodOffset); + if (it == processedMethod.end()) { std::vector classNameVec; CollectMethodPcsFromBC(codeSize, insns, methodLiteral, classNameVec, recordName, methodOffset, classConstructIndexes); - processedInsns[insns] = std::make_pair(methodPcInfos.size() - 1, methodOffset); + processedMethod[methodOffset] = std::make_pair(methodPcInfos.size() - 1, methodOffset); // collect className and literal offset for type infer if (EnableCollectLiteralInfo()) { CollectClassLiteralInfo(methodLiteral, classNameVec); } } - SetMethodPcInfoIndex(methodOffset, processedInsns[insns]); + SetMethodPcInfoIndex(methodOffset, processedMethod[methodOffset]); jsPandaFile_->SetMethodLiteralToMap(methodLiteral); + pfDecoder_.MatchAndMarkMethod(recordName, name.c_str(), methodId); }); } // class Construct need to use new target, can not fastcall @@ -173,6 +176,21 @@ void BytecodeInfoCollector::CollectFunctionTypeId(panda_file::File::EntityId fie } } +void BytecodeInfoCollector::CollectInnerFuncType(const MethodLiteral *method, uint32_t innerMethodId, int32_t bcIndex) +{ + auto &methodList = bytecodeInfo_.GetMethodList(); + auto methodId = method->GetMethodId().GetOffset(); + auto methodIter = methodList.find(methodId); + if (methodIter == methodList.end()) { + return; + } + TypeAnnotationExtractor annoExtractor(jsPandaFile_, innerMethodId); + uint32_t innerFuncType = annoExtractor.GetMethodTypeOffset(); + if (innerFuncType != 0) { + methodIter->second.AddBcToTypeId(bcIndex, innerFuncType); + } +} + void BytecodeInfoCollector::IterateLiteral(const MethodLiteral *method, std::vector &classOffsetVector) { @@ -224,6 +242,7 @@ void BytecodeInfoCollector::CollectMethodPcsFromBC(const uint32_t insSz, const u auto &pcOffsets = methodPcInfos.back().pcOffsets; const uint8_t *curPc = bcIns.GetAddress(); bool canFastCall = true; + bool noGC = true; while (bcIns.GetAddress() != bcInsLast.GetAddress()) { bool fastCallFlag = true; @@ -232,15 +251,20 @@ void BytecodeInfoCollector::CollectMethodPcsFromBC(const uint32_t insSz, const u canFastCall = false; } CollectModuleInfoFromBC(bcIns, method, recordName); - CollectConstantPoolIndexInfoFromBC(bcIns, method); + CollectConstantPoolIndexInfoFromBC(bcIns, method, bcIndex); + pgoBCInfo_.Record(bcIns, bcIndex, recordName, method); + if (noGC && !bytecodes_.GetBytecodeMetaData(curPc).IsNoGC()) { + noGC = false; + } curPc = bcIns.GetAddress(); auto nextInst = bcIns.GetNext(); bcIns = nextInst; pcOffsets.emplace_back(curPc); bcIndex++; } - bytecodeInfo_.SetMethodOffsetToCanFastCall(methodOffset, canFastCall); + bytecodeInfo_.SetMethodOffsetToFastCallInfo(methodOffset, canFastCall, noGC); method->SetIsFastCall(canFastCall); + method->SetNoGCBit(noGC); } void BytecodeInfoCollector::SetMethodPcInfoIndex(uint32_t methodOffset, @@ -376,6 +400,7 @@ void BytecodeInfoCollector::CollectMethodInfoFromBC(const BytecodeInstruction &b methodId = jsPandaFile_->ResolveMethodIndex(method->GetMethodId(), static_cast(bcIns.GetId().AsRawValue())).GetOffset(); CollectInnerMethods(method, methodId); + CollectInnerFuncType(method, methodId, bcIndex); break; } case BytecodeInstruction::Opcode::DEFINEMETHOD_IMM8_ID16_IMM8: @@ -600,7 +625,8 @@ void BytecodeInfoCollector::CollectRecordReferenceREL() { auto &recordNames = bytecodeInfo_.GetRecordNames(); for (auto &record : recordNames) { - if (jsPandaFile_->HasTSTypes(record) && jsPandaFile_->IsModule(vm_->GetJSThread(), record)) { + JSRecordInfo info = jsPandaFile_->FindRecordInfo(record); + if (jsPandaFile_->HasTSTypes(info)|| jsPandaFile_->IsModule(info)) { CollectRecordImportInfo(record); CollectRecordExportInfo(record); } @@ -630,7 +656,7 @@ void BytecodeInfoCollector::CollectRecordImportInfo(const CString &recordName) for (size_t index = 0; index < length; index++) { JSTaggedValue resolvedBinding = moduleArray->Get(index); // if resolvedBinding.IsHole(), means that importname is * or it belongs to empty Aot module. - if (resolvedBinding.IsHole()) { + if (!resolvedBinding.IsResolvedIndexBinding()) { continue; } ResolvedIndexBinding *binding = ResolvedIndexBinding::Cast(resolvedBinding.GetTaggedObject()); @@ -662,11 +688,11 @@ void BytecodeInfoCollector::CollectRecordExportInfo(const CString &recordName) starExportEntry.Update(starEntriesArray->Get(index)); JSTaggedValue moduleRequest = starExportEntry->GetModuleRequest(); CString moduleRequestName = ConvertToString(EcmaString::Cast(moduleRequest.GetTaggedObject())); - if (base::PathHelper::IsNativeModuleRequest(moduleRequestName)) { + if (ModulePathHelper::IsNativeModuleRequest(moduleRequestName)) { return; } CString baseFileName = jsPandaFile_->GetJSPandaFileDesc(); - CString entryPoint = base::PathHelper::ConcatFileNameWithMerge(thread, jsPandaFile_, + CString entryPoint = ModulePathHelper::ConcatFileNameWithMerge(thread, jsPandaFile_, baseFileName, recordName, moduleRequestName); if (jsPandaFile_->HasTypeSummaryOffset(entryPoint)) { bytecodeInfo_.AddStarExportToRecord(recordName, entryPoint); @@ -684,7 +710,7 @@ void BytecodeInfoCollector::RearrangeInnerMethods() } void BytecodeInfoCollector::CollectConstantPoolIndexInfoFromBC(const BytecodeInstruction &bcIns, - const MethodLiteral *method) + const MethodLiteral *method, uint32_t bcIndex) { BytecodeInstruction::Opcode opcode = static_cast(bcIns.GetOpcode()); uint32_t methodOffset = method->GetMethodId().GetOffset(); @@ -718,7 +744,7 @@ void BytecodeInfoCollector::CollectConstantPoolIndexInfoFromBC(const BytecodeIns case BytecodeInstruction::Opcode::STGLOBALVAR_IMM16_ID16: case BytecodeInstruction::Opcode::LDBIGINT_ID16: { auto index = bcIns.GetId().AsRawValue(); - AddConstantPoolIndexToBCInfo(ConstantPoolInfo::ItemType::STRING, index, methodOffset); + AddConstantPoolIndexToBCInfo(ConstantPoolInfo::ItemType::STRING, index, methodOffset, bcIndex); break; } case BytecodeInstruction::Opcode::DEFINEFUNC_IMM8_ID16_IMM8: @@ -726,33 +752,35 @@ void BytecodeInfoCollector::CollectConstantPoolIndexInfoFromBC(const BytecodeIns case BytecodeInstruction::Opcode::DEFINEMETHOD_IMM8_ID16_IMM8: case BytecodeInstruction::Opcode::DEFINEMETHOD_IMM16_ID16_IMM8: { auto index = bcIns.GetId().AsRawValue(); - AddConstantPoolIndexToBCInfo(ConstantPoolInfo::ItemType::METHOD, index, methodOffset); + AddConstantPoolIndexToBCInfo(ConstantPoolInfo::ItemType::METHOD, index, methodOffset, bcIndex); break; } case BytecodeInstruction::Opcode::CREATEOBJECTWITHBUFFER_IMM8_ID16: case BytecodeInstruction::Opcode::CREATEOBJECTWITHBUFFER_IMM16_ID16: { auto index = bcIns.GetId().AsRawValue(); - AddConstantPoolIndexToBCInfo(ConstantPoolInfo::ItemType::OBJECT_LITERAL, index, methodOffset); + AddConstantPoolIndexToBCInfo(ConstantPoolInfo::ItemType::OBJECT_LITERAL, index, methodOffset, bcIndex); break; } case BytecodeInstruction::Opcode::CREATEARRAYWITHBUFFER_IMM8_ID16: case BytecodeInstruction::Opcode::CREATEARRAYWITHBUFFER_IMM16_ID16: { auto index = bcIns.GetId().AsRawValue(); - AddConstantPoolIndexToBCInfo(ConstantPoolInfo::ItemType::ARRAY_LITERAL, index, methodOffset); + AddConstantPoolIndexToBCInfo(ConstantPoolInfo::ItemType::ARRAY_LITERAL, index, methodOffset, bcIndex); break; } case BytecodeInstruction::Opcode::DEFINECLASSWITHBUFFER_IMM8_ID16_ID16_IMM16_V8: { auto methodIndex = (bcIns.GetId ()).AsRawValue(); - AddConstantPoolIndexToBCInfo(ConstantPoolInfo::ItemType::METHOD, methodIndex, methodOffset); + AddConstantPoolIndexToBCInfo(ConstantPoolInfo::ItemType::METHOD, methodIndex, methodOffset, bcIndex); auto literalIndex = (bcIns.GetId ()).AsRawValue(); - AddConstantPoolIndexToBCInfo(ConstantPoolInfo::ItemType::CLASS_LITERAL, literalIndex, methodOffset); + AddConstantPoolIndexToBCInfo(ConstantPoolInfo::ItemType::CLASS_LITERAL, literalIndex, + methodOffset, bcIndex); break; } case BytecodeInstruction::Opcode::DEFINECLASSWITHBUFFER_IMM16_ID16_ID16_IMM16_V8: { auto methodIndex = (bcIns.GetId ()).AsRawValue(); - AddConstantPoolIndexToBCInfo(ConstantPoolInfo::ItemType::METHOD, methodIndex, methodOffset); + AddConstantPoolIndexToBCInfo(ConstantPoolInfo::ItemType::METHOD, methodIndex, methodOffset, bcIndex); auto literalIndex = (bcIns.GetId ()).AsRawValue(); - AddConstantPoolIndexToBCInfo(ConstantPoolInfo::ItemType::CLASS_LITERAL, literalIndex, methodOffset); + AddConstantPoolIndexToBCInfo(ConstantPoolInfo::ItemType::CLASS_LITERAL, literalIndex, + methodOffset, bcIndex); break; } default: @@ -801,12 +829,24 @@ uint32_t LexEnvManager::GetTargetLexEnv(uint32_t methodId, uint32_t level) const return offset; } -void ConstantPoolInfo::AddIndexToCPItem(ItemType type, uint32_t index, uint32_t methodOffset) +uint64_t ConstantPoolInfo::GetItemKey(uint32_t index, uint32_t methodOffset) +{ + panda_file::IndexAccessor indexAccessor(*jsPandaFile_->GetPandaFile(), + panda_file::File::EntityId(methodOffset)); + uint64_t result = 0; + result = static_cast(indexAccessor.GetHeaderIndex()); + result = result << CONSTPOOL_MASK; + result |= index; + return result; +} + +void ConstantPoolInfo::AddIndexToCPItem(ItemType type, uint32_t index, uint32_t methodOffset, uint32_t bcIndex) { + uint64_t key = GetItemKey(index, methodOffset); Item &item = GetCPItem(type); - if (item.find(index) != item.end()) { + if (item.find(key) != item.end()) { return; } - item.insert({index, ItemData {index, methodOffset, nullptr}}); + item.insert({key, ItemData {index, methodOffset, nullptr, bcIndex}}); } } // namespace panda::ecmascript::kungfu diff --git a/ecmascript/compiler/bytecode_info_collector.h b/ecmascript/compiler/bytecode_info_collector.h index e7fc69daa03b11d29f5aac3f3bb65c515f06030a..38efd93b8fd2cc4fa3c25d03a924c67700453782 100644 --- a/ecmascript/compiler/bytecode_info_collector.h +++ b/ecmascript/compiler/bytecode_info_collector.h @@ -18,6 +18,7 @@ #include "ecmascript/jspandafile/js_pandafile.h" #include "ecmascript/pgo_profiler/pgo_profiler_decoder.h" +#include "ecmascript/compiler/pgo_bc_info.h" #include "libpandafile/bytecode_instruction-inl.h" namespace panda::ecmascript::kungfu { @@ -53,6 +54,8 @@ namespace panda::ecmascript::kungfu { * of global and function f will be created when methods are executed. */ +using PGOProfilerDecoder = pgo::PGOProfilerDecoder; + enum class LexicalEnvStatus : uint8_t { VIRTUAL_LEXENV, REALITY_LEXENV @@ -273,6 +276,16 @@ public: innerMethods_.insert(innerMethods_.begin(), constructorMethods_.begin(), constructorMethods_.end()); } + inline void AddBcToTypeId(int32_t bcIndex, uint32_t innerFuncTypeId) + { + bcToFuncTypeId_.emplace(bcIndex, innerFuncTypeId); + } + + inline const std::unordered_map &GetBCAndTypes() const + { + return bcToFuncTypeId_; + } + inline void MarkMethodNamespace() { isNamespace_ = true; @@ -368,6 +381,7 @@ private: uint32_t methodPcInfoIndex_ { 0 }; std::vector innerMethods_ {}; std::vector constructorMethods_ {}; + std::unordered_map bcToFuncTypeId_ {}; uint32_t outerMethodId_ { LexEnv::DEFAULT_ROOT }; uint32_t outerMethodOffset_ { MethodInfo::DEFAULT_OUTMETHOD_OFFSET }; uint32_t numOfLexVars_ { 0 }; @@ -396,12 +410,13 @@ public: uint32_t index {0}; uint32_t outerMethodOffset {0}; CString *recordName {nullptr}; + uint32_t bcIndex {0}; }; // key:constantpool index, value:ItemData - using Item = std::unordered_map; + using Item = std::unordered_map; - ConstantPoolInfo() : items_(ItemType::ITEM_TYPE_NUM, Item{}) {} + ConstantPoolInfo(JSPandaFile* jsPandaFile) : items_(ItemType::ITEM_TYPE_NUM, Item{}), jsPandaFile_(jsPandaFile) {} Item& GetCPItem(ItemType type) { @@ -409,15 +424,24 @@ public: return items_[type]; } - void AddIndexToCPItem(ItemType type, uint32_t index, uint32_t methodOffset); + void AddIndexToCPItem(ItemType type, uint32_t index, uint32_t methodOffset, uint32_t bcIndex); private: + static constexpr uint32_t CONSTPOOL_MASK = 32; + uint64_t GetItemKey(uint32_t index, uint32_t methodOffset); + std::vector items_; + JSPandaFile* jsPandaFile_ {nullptr}; +}; + +struct FastCallInfo { + bool canFastCall_ {false}; + bool isNoGC_ {false}; }; class BCInfo { public: - explicit BCInfo(size_t maxAotMethodSize) - : maxMethodSize_(maxAotMethodSize) + explicit BCInfo(size_t maxAotMethodSize, JSPandaFile* jsPandaFile) + : cpInfo_(jsPandaFile), maxMethodSize_(maxAotMethodSize) { } @@ -488,9 +512,9 @@ public: return skippedMethods_.size(); } - void AddIndexToCPInfo(ConstantPoolInfo::ItemType type, uint32_t index, uint32_t methodOffset) + void AddIndexToCPInfo(ConstantPoolInfo::ItemType type, uint32_t index, uint32_t methodOffset, uint32_t bcIndex) { - cpInfo_.AddIndexToCPItem(type, index, methodOffset); + cpInfo_.AddIndexToCPItem(type, index, methodOffset, bcIndex); } template @@ -599,29 +623,34 @@ public: return recordToImportRecordsInfo_; } - bool IterateMethodOffsetToCanFastCall(uint32_t methodOffset, bool *isValid) + FastCallInfo IterateMethodOffsetToFastCallInfo(uint32_t methodOffset, bool *isValid) { - auto iter = methodOffsetToCanFastCall_.find(methodOffset); - if (iter != methodOffsetToCanFastCall_.end()) { + auto iter = methodOffsetToFastCallInfos_.find(methodOffset); + if (iter != methodOffsetToFastCallInfos_.end()) { *isValid = true; return iter->second; } *isValid = false; - return false; + return FastCallInfo(); } - void SetMethodOffsetToCanFastCall(uint32_t methodOffset, bool canFastCall) + void SetMethodOffsetToFastCallInfo(uint32_t methodOffset, bool canFastCall, bool noGC) { - if (methodOffsetToCanFastCall_.find(methodOffset) == methodOffsetToCanFastCall_.end()) { - methodOffsetToCanFastCall_.emplace(methodOffset, canFastCall); + if (methodOffsetToFastCallInfos_.find(methodOffset) == methodOffsetToFastCallInfos_.end()) { + methodOffsetToFastCallInfos_.emplace(methodOffset, FastCallInfo { canFastCall, noGC }); } } void ModifyMethodOffsetToCanFastCall(uint32_t methodOffset, bool canFastCall) { - methodOffsetToCanFastCall_.erase(methodOffset); - if (methodOffsetToCanFastCall_.find(methodOffset) == methodOffsetToCanFastCall_.end()) { - methodOffsetToCanFastCall_.emplace(methodOffset, canFastCall); + auto iter = methodOffsetToFastCallInfos_.find(methodOffset); + bool isNoGC = false; + if (iter != methodOffsetToFastCallInfos_.end()) { + isNoGC = iter->second.isNoGC_; + } + methodOffsetToFastCallInfos_.erase(methodOffset); + if (methodOffsetToFastCallInfos_.find(methodOffset) == methodOffsetToFastCallInfos_.end()) { + methodOffsetToFastCallInfos_.emplace(methodOffset, FastCallInfo { canFastCall, isNoGC }); } } private: @@ -637,7 +666,7 @@ private: std::unordered_map functionTypeIdToMethodOffset_ {}; std::unordered_map recordNameToExportInfo_ {}; std::unordered_map recordToImportRecordsInfo_ {}; - std::unordered_map methodOffsetToCanFastCall_ {}; + std::unordered_map methodOffsetToFastCallInfos_ {}; }; class LexEnvManager { @@ -673,7 +702,7 @@ private: class BytecodeInfoCollector { public: - BytecodeInfoCollector(EcmaVM *vm, JSPandaFile *jsPandaFile, + BytecodeInfoCollector(EcmaVM *vm, JSPandaFile *jsPandaFile, PGOProfilerDecoder &pfDecoder, size_t maxAotMethodSize, bool enableCollectLiteralInfo); ~BytecodeInfoCollector(); NO_COPY_SEMANTIC(BytecodeInfoCollector); @@ -684,6 +713,11 @@ public: return enableCollectLiteralInfo_; } + Bytecodes* GetByteCodes() + { + return &bytecodes_; + } + BCInfo& GetBytecodeInfo() { return bytecodeInfo_; @@ -694,6 +728,11 @@ public: return &bytecodeInfo_; } + PGOBCInfo* GetPGOBCInfo() + { + return &pgoBCInfo_; + } + bool IsSkippedMethod(uint32_t methodOffset) const { return bytecodeInfo_.IsSkippedMethod(methodOffset); @@ -729,9 +768,9 @@ private: } void AddConstantPoolIndexToBCInfo(ConstantPoolInfo::ItemType type, - uint32_t index, uint32_t methodOffset) + uint32_t index, uint32_t methodOffset, uint32_t bcIndex) { - bytecodeInfo_.AddIndexToCPInfo(type, index, methodOffset); + bytecodeInfo_.AddIndexToCPInfo(type, index, methodOffset, bcIndex); } inline std::string GetClassName(const EntityId entityId) @@ -754,6 +793,7 @@ private: void CollectInnerMethods(const MethodLiteral *method, uint32_t innerMethodOffset, bool isConstructor = false); void CollectInnerMethods(uint32_t methodId, uint32_t innerMethodOffset, bool isConstructor = false); void CollectInnerMethodsFromLiteral(const MethodLiteral *method, uint64_t index); + void CollectInnerFuncType(const MethodLiteral *method, uint32_t innerMethodOffset, int32_t bcIndex); void NewLexEnvWithSize(const MethodLiteral *method, uint64_t numOfLexVars); void CollectInnerMethodsFromNewLiteral(const MethodLiteral *method, panda_file::File::EntityId literalId); void CollectMethodInfoFromBC(const BytecodeInstruction &bcIns, const MethodLiteral *method, @@ -762,7 +802,8 @@ private: bool *canFastCall); void CollectModuleInfoFromBC(const BytecodeInstruction &bcIns, const MethodLiteral *method, const CString &recordName); - void CollectConstantPoolIndexInfoFromBC(const BytecodeInstruction &bcIns, const MethodLiteral *method); + void CollectConstantPoolIndexInfoFromBC(const BytecodeInstruction &bcIns, const MethodLiteral *method, + uint32_t bcIndex); void IterateLiteral(const MethodLiteral *method, std::vector &classOffsetVector); void StoreClassTypeOffset(const uint32_t typeOffset, std::vector &classOffsetVector); void CollectClassLiteralInfo(const MethodLiteral *method, const std::vector &classNameVec); @@ -778,10 +819,13 @@ private: EcmaVM *vm_; JSPandaFile *jsPandaFile_ {nullptr}; BCInfo bytecodeInfo_; + PGOProfilerDecoder &pfDecoder_; + PGOBCInfo pgoBCInfo_ {}; size_t methodInfoIndex_ {0}; bool enableCollectLiteralInfo_ {false}; std::set classDefBCIndexes_ {}; LexEnvManager* envManager_ {nullptr}; + Bytecodes bytecodes_; }; } // namespace panda::ecmascript::kungfu #endif // ECMASCRIPT_COMPILER_BYTECODE_INFO_COLLECTOR_H diff --git a/ecmascript/compiler/bytecodes.cpp b/ecmascript/compiler/bytecodes.cpp index 77692ec1ec5d5c8a1cb508d6b82a9bbb8480e528..8e7af669007a6a4b2eea9bc97ef0c972401c7927 100644 --- a/ecmascript/compiler/bytecodes.cpp +++ b/ecmascript/compiler/bytecodes.cpp @@ -60,17 +60,84 @@ BytecodeMetaData BytecodeMetaData::InitBytecodeMetaData(const uint8_t *pc) } switch (inst.GetOpcode()) { + case EcmaOpcode::MOV_V4_V4: + case EcmaOpcode::MOV_V8_V8: + case EcmaOpcode::MOV_V16_V16: + case EcmaOpcode::STA_V8: + case EcmaOpcode::LDA_V8: + case EcmaOpcode::LDHOLE: + case EcmaOpcode::LDAI_IMM32: + case EcmaOpcode::FLDAI_IMM64: + case EcmaOpcode::LDFUNCTION: + case EcmaOpcode::TYPEOF_IMM8: + case EcmaOpcode::TYPEOF_IMM16: + case EcmaOpcode::LDNAN: + case EcmaOpcode::LDINFINITY: + case EcmaOpcode::LDUNDEFINED: + case EcmaOpcode::LDNULL: + case EcmaOpcode::LDTRUE: + case EcmaOpcode::LDFALSE: + case EcmaOpcode::LDSYMBOL: + case EcmaOpcode::LDGLOBAL: + case EcmaOpcode::LDBIGINT_ID16: + case EcmaOpcode::LDLEXVAR_IMM4_IMM4: + case EcmaOpcode::LDLEXVAR_IMM8_IMM8: + case EcmaOpcode::WIDE_LDLEXVAR_PREF_IMM16_IMM16: + case EcmaOpcode::WIDE_LDPATCHVAR_PREF_IMM16: + case EcmaOpcode::LDA_STR_ID16: + case EcmaOpcode::RETURN: + case EcmaOpcode::RETURNUNDEFINED: + flags |= BytecodeFlags::NO_GC; + break; + default: + break; + } + + switch (inst.GetOpcode()) { + case EcmaOpcode::MOV_V4_V4: + case EcmaOpcode::MOV_V8_V8: + case EcmaOpcode::MOV_V16_V16: + case EcmaOpcode::STA_V8: + case EcmaOpcode::LDA_V8: + case EcmaOpcode::LDNAN: + case EcmaOpcode::LDINFINITY: + case EcmaOpcode::LDUNDEFINED: + case EcmaOpcode::LDNULL: + case EcmaOpcode::LDTRUE: + case EcmaOpcode::LDFALSE: + case EcmaOpcode::LDHOLE: + case EcmaOpcode::LDAI_IMM32: + case EcmaOpcode::FLDAI_IMM64: + case EcmaOpcode::LDFUNCTION: + case EcmaOpcode::LDA_STR_ID16: case EcmaOpcode::TYPEOF_IMM8: case EcmaOpcode::TYPEOF_IMM16: case EcmaOpcode::ISTRUE: case EcmaOpcode::ISFALSE: + case EcmaOpcode::JEQZ_IMM8: + case EcmaOpcode::JEQZ_IMM16: + case EcmaOpcode::JEQZ_IMM32: + case EcmaOpcode::JNEZ_IMM8: + case EcmaOpcode::JNEZ_IMM16: + case EcmaOpcode::JNEZ_IMM32: + case EcmaOpcode::JMP_IMM8: + case EcmaOpcode::JMP_IMM16: + case EcmaOpcode::JMP_IMM32: case EcmaOpcode::STMODULEVAR_IMM8: case EcmaOpcode::WIDE_STMODULEVAR_PREF_IMM16: + case EcmaOpcode::LDEXTERNALMODULEVAR_IMM8: + case EcmaOpcode::WIDE_LDEXTERNALMODULEVAR_PREF_IMM16: + case EcmaOpcode::NEWLEXENV_IMM8: + case EcmaOpcode::WIDE_NEWLEXENV_PREF_IMM16: case EcmaOpcode::POPLEXENV: case EcmaOpcode::NEWLEXENVWITHNAME_IMM8_ID16: case EcmaOpcode::WIDE_NEWLEXENVWITHNAME_PREF_IMM16_ID16: + case EcmaOpcode::ASYNCFUNCTIONENTER: + case EcmaOpcode::SETGENERATORSTATE_IMM8: case EcmaOpcode::GETRESUMEMODE: case EcmaOpcode::RESUMEGENERATOR: + case EcmaOpcode::RETURN: + case EcmaOpcode::RETURNUNDEFINED: case EcmaOpcode::LDLEXVAR_IMM4_IMM4: case EcmaOpcode::LDLEXVAR_IMM8_IMM8: case EcmaOpcode::WIDE_LDLEXVAR_PREF_IMM16_IMM16: @@ -84,7 +151,14 @@ BytecodeMetaData BytecodeMetaData::InitBytecodeMetaData(const uint8_t *pc) case EcmaOpcode::CREATEEMPTYOBJECT: case EcmaOpcode::CREATEARRAYWITHBUFFER_IMM8_ID16: case EcmaOpcode::CREATEARRAYWITHBUFFER_IMM16_ID16: - case EcmaOpcode::SETGENERATORSTATE_IMM8: + case EcmaOpcode::CREATEITERRESULTOBJ_V8_V8: + case EcmaOpcode::DEFINEFUNC_IMM8_ID16_IMM8: + case EcmaOpcode::DEFINEFUNC_IMM16_ID16_IMM8: + case EcmaOpcode::DEFINEMETHOD_IMM8_ID16_IMM8: + case EcmaOpcode::DEFINEMETHOD_IMM16_ID16_IMM8: + case EcmaOpcode::GETUNMAPPEDARGS: + case EcmaOpcode::DEBUGGER: + case EcmaOpcode::NOP: flags |= BytecodeFlags::NO_THROW; break; default: @@ -198,7 +272,6 @@ BytecodeMetaData BytecodeMetaData::InitBytecodeMetaData(const uint8_t *pc) kind = BytecodeKind::SUSPEND; break; case EcmaOpcode::RESUMEGENERATOR: - case EcmaOpcode::CREATEOBJECTWITHEXCLUDEDKEYS_IMM8_V8_V8: kind = BytecodeKind::RESUME; break; case EcmaOpcode::DEBUGGER: diff --git a/ecmascript/compiler/bytecodes.h b/ecmascript/compiler/bytecodes.h index 618e756600fe9900b18a4179bf75227b650864dd..244564f9e2f75eed56dfb0ff53e88a20b47dba97 100644 --- a/ecmascript/compiler/bytecodes.h +++ b/ecmascript/compiler/bytecodes.h @@ -49,6 +49,7 @@ enum BytecodeFlags : uint32_t { READ_FUNC = 1 << 9, READ_NEWTARGET = 1 << 10, READ_ARGC = 1 << 11, + NO_GC = 1 << 12, }; enum BytecodeKind : uint32_t { @@ -70,7 +71,7 @@ class BytecodeMetaData { public: static constexpr uint32_t MAX_OPCODE_SIZE = 16; static constexpr uint32_t MAX_SIZE_BITS = 4; - static constexpr uint32_t BYTECODE_FLAGS_SIZE = 12; + static constexpr uint32_t BYTECODE_FLAGS_SIZE = 13; static constexpr uint32_t BYTECODE_KIND_SIZE = 4; using OpcodeField = panda::BitField; @@ -113,6 +114,11 @@ public: return HasFlag(BytecodeFlags::WRITE_ENV); } + bool IsNoGC() const + { + return HasFlag(BytecodeFlags::NO_GC); + } + bool IsMov() const { return GetKind() == BytecodeKind::MOV; @@ -308,6 +314,49 @@ public: return bytecodes_[primary]; } + static bool IsCallOp(EcmaOpcode opcode) + { + switch (opcode) { + case EcmaOpcode::CALLARG0_IMM8: + case EcmaOpcode::CALLARG1_IMM8_V8: + case EcmaOpcode::CALLARGS2_IMM8_V8_V8: + case EcmaOpcode::CALLARGS3_IMM8_V8_V8_V8: + case EcmaOpcode::CALLRANGE_IMM8_IMM8_V8: + case EcmaOpcode::WIDE_CALLRANGE_PREF_IMM16_V8: + case EcmaOpcode::CALLTHIS0_IMM8_V8: + case EcmaOpcode::CALLTHIS1_IMM8_V8_V8: + case EcmaOpcode::CALLTHIS2_IMM8_V8_V8_V8: + case EcmaOpcode::CALLTHIS3_IMM8_V8_V8_V8_V8: + case EcmaOpcode::CALLTHISRANGE_IMM8_IMM8_V8: + case EcmaOpcode::WIDE_CALLTHISRANGE_PREF_IMM16_V8: + return true; + default: + return false; + } + } + + static bool IsCreateObjectWithBufferOp(EcmaOpcode opcode) + { + switch (opcode) { + case EcmaOpcode::CREATEOBJECTWITHBUFFER_IMM8_ID16: + case EcmaOpcode::CREATEOBJECTWITHBUFFER_IMM16_ID16: + return true; + default: + return false; + } + } + + static bool IsCreateArrayWithBufferOp(EcmaOpcode opcode) + { + switch (opcode) { + case EcmaOpcode::CREATEARRAYWITHBUFFER_IMM8_ID16: + case EcmaOpcode::CREATEARRAYWITHBUFFER_IMM16_ID16: + return true; + default: + return false; + } + } + private: static uint8_t ReadByte(const uint8_t *pc) { @@ -636,6 +685,11 @@ public: return HasFuncIn() || HasNewTargetIn() || ThisObjectIn() || HasArgcIn(); } + bool HasFrameState() const + { + return HasFrameArgs() || !NoThrow(); + } + bool IsCall() const { return metaData_.IsCall(); diff --git a/ecmascript/compiler/call_signature.cpp b/ecmascript/compiler/call_signature.cpp index 83e41d03206e552f3eb6c6bf9f9b309615ec93c9..4bde7a4e5ae8bbe1dc92dd18b152e9154ab056e1 100644 --- a/ecmascript/compiler/call_signature.cpp +++ b/ecmascript/compiler/call_signature.cpp @@ -14,6 +14,7 @@ */ #include "ecmascript/compiler/call_signature.h" +#include "ecmascript/compiler/variable_type.h" #if defined(__clang__) #pragma clang diagnostic push @@ -644,13 +645,17 @@ DEF_CALL_SIGNATURE(ConstructorCheck) DEF_CALL_SIGNATURE(CreateEmptyArray) { - // 1 : 1 input parameters - CallSignature signature("CreateEmptyArray", 0, 1, + // 5 : 5 input parameters + CallSignature signature("CreateEmptyArray", 0, 5, ArgumentsOrder::DEFAULT_ORDER, VariableType::JS_ANY()); *callSign = signature; - // 1 : 1 input parameters - std::array params = { + // 5 : 5 input parameters + std::array params = { VariableType::NATIVE_POINTER(), // glue + VariableType::JS_ANY(), // jsFunc + VariableType::JS_ANY(), // pc + VariableType::INT32(), // profileTypeInfo + VariableType::INT32(), // slotId }; callSign->SetParameters(params.data()); callSign->SetCallConv(CallSignature::CallConv::CCallConv); @@ -658,15 +663,18 @@ DEF_CALL_SIGNATURE(CreateEmptyArray) DEF_CALL_SIGNATURE(CreateArrayWithBuffer) { - // 3 : 3 input parameters - CallSignature signature("CreateArrayWithBuffer", 0, 3, + // 6 : 6 input parameters + CallSignature signature("CreateArrayWithBuffer", 0, 6, ArgumentsOrder::DEFAULT_ORDER, VariableType::JS_ANY()); *callSign = signature; - // 3 : 3 input parameters - std::array params = { + // 6 : 6 input parameters + std::array params = { VariableType::NATIVE_POINTER(), // glue VariableType::INT32(), // index VariableType::JS_ANY(), // jsFunc + VariableType::JS_ANY(), // pc + VariableType::INT32(), // profileTypeInfo + VariableType::INT32(), // slotId }; callSign->SetParameters(params.data()); callSign->SetCallConv(CallSignature::CallConv::CCallConv); @@ -1120,6 +1128,27 @@ DEF_CALL_SIGNATURE(ResumeUncaughtFrameAndReturn) callSign->SetCallConv(CallSignature::CallConv::GHCCallConv); } +DEF_CALL_SIGNATURE(ResumeRspAndRollback) +{ + // 8 : 8 input parameters + CallSignature resumeRspAndRollback("ResumeRspAndRollback", 0, 8, + ArgumentsOrder::DEFAULT_ORDER, VariableType::VOID()); + *callSign = resumeRspAndRollback; + std::array params = { // 8 : 8 input parameters + VariableType::NATIVE_POINTER(), + VariableType::NATIVE_POINTER(), + VariableType::NATIVE_POINTER(), + VariableType::JS_POINTER(), + VariableType::JS_POINTER(), + VariableType::JS_ANY(), + VariableType::INT32(), + VariableType::NATIVE_POINTER(), + }; + callSign->SetParameters(params.data()); + callSign->SetTargetKind(CallSignature::TargetKind::RUNTIME_STUB_NO_GC); + callSign->SetCallConv(CallSignature::CallConv::GHCCallConv); +} + DEF_CALL_SIGNATURE(StringsAreEquals) { // 2 : 2 input parameters @@ -1150,6 +1179,21 @@ DEF_CALL_SIGNATURE(BigIntEquals) callSign->SetTargetKind(CallSignature::TargetKind::RUNTIME_STUB_NO_GC); } +DEF_CALL_SIGNATURE(BigIntSameValueZero) +{ + // 1 : 1 input parameters + CallSignature bigIntSameValueZero("BigIntSameValueZero", 0, 2, + ArgumentsOrder::DEFAULT_ORDER, VariableType::BOOL()); + *callSign = bigIntSameValueZero; + std::array params = { // 2 : 2 input parameters + VariableType::JS_POINTER(), + VariableType::JS_POINTER(), + }; + callSign->SetParameters(params.data()); + callSign->SetGCLeafFunction(true); + callSign->SetTargetKind(CallSignature::TargetKind::RUNTIME_STUB_NO_GC); +} + #define PUSH_CALL_ARGS_AND_DISPATCH_SIGNATURE_COMMON(name) \ /* 1 : 1 input parameters */ \ CallSignature signature(#name, 0, 1, \ @@ -1361,82 +1405,73 @@ DEF_CALL_SIGNATURE(CallOptimized) callSign->SetTargetKind(CallSignature::TargetKind::RUNTIME_STUB_NO_GC); } -DEF_CALL_SIGNATURE(DebugPrint) +DEF_CALL_SIGNATURE(Dump) { - // 1 : 1 input parameters - CallSignature debugPrint("DebugPrint", 0, 1, + constexpr size_t N_INPUT_PARAMETERS = 1; + CallSignature dump("Dump", 0, N_INPUT_PARAMETERS, ArgumentsOrder::DEFAULT_ORDER, VariableType::VOID()); - *callSign = debugPrint; - // 1 : 1 input parameters - std::array params = { - VariableType::INT32(), + *callSign = dump; + std::array params = { + VariableType::JS_POINTER() // Tagged value of the object to be dumped }; - callSign->SetVariadicArgs(true); callSign->SetParameters(params.data()); callSign->SetGCLeafFunction(true); callSign->SetTargetKind(CallSignature::TargetKind::RUNTIME_STUB_NO_GC); } -DEF_CALL_SIGNATURE(DebugPrintInstruction) +DEF_CALL_SIGNATURE(DebugDump) { - // 2 : 2 input parameters - CallSignature debugPrintInstruction("DebugPrintInstruction", 0, 2, + constexpr size_t N_INPUT_PARAMETERS = 1; + CallSignature debugDump("DebugDump", 0, N_INPUT_PARAMETERS, ArgumentsOrder::DEFAULT_ORDER, VariableType::VOID()); - *callSign = debugPrintInstruction; - // 2 : 2 input parameters - std::array params = { - VariableType::NATIVE_POINTER(), - VariableType::NATIVE_POINTER(), + *callSign = debugDump; + std::array params = { + VariableType::JS_POINTER() // Tagged value of the object to be dumped }; - callSign->SetVariadicArgs(true); callSign->SetParameters(params.data()); callSign->SetGCLeafFunction(true); callSign->SetTargetKind(CallSignature::TargetKind::RUNTIME_STUB_NO_GC); } -DEF_CALL_SIGNATURE(Comment) +DEF_CALL_SIGNATURE(DumpWithHint) { - // 1 : 1 input parameters - CallSignature comment("Comment", 0, 1, + constexpr size_t N_INPUT_PARAMETERS = 2; + CallSignature dumpWithHint("DumpWithHint", 0, N_INPUT_PARAMETERS, ArgumentsOrder::DEFAULT_ORDER, VariableType::VOID()); - *callSign = comment; - // 1 : 1 input parameters - std::array params = { - VariableType::NATIVE_POINTER(), + *callSign = dumpWithHint; + std::array params = { + VariableType::NATIVE_POINTER(), // String created via CircuitBuilder::StringPtr() + VariableType::JS_POINTER() // Tagged value of the object to be dumped }; callSign->SetParameters(params.data()); callSign->SetGCLeafFunction(true); callSign->SetTargetKind(CallSignature::TargetKind::RUNTIME_STUB_NO_GC); } -DEF_CALL_SIGNATURE(ProfileCall) +DEF_CALL_SIGNATURE(DebugDumpWithHint) { - // 2 : 2 input parameters - CallSignature callProfilerInstruction("ProfileCall", 0, 2, ArgumentsOrder::DEFAULT_ORDER, VariableType::VOID()); - *callSign = callProfilerInstruction; - // 2 : 2 input parameters - std::array params = { - VariableType::NATIVE_POINTER(), - VariableType::JS_ANY(), + constexpr size_t N_INPUT_PARAMETERS = 2; + CallSignature debugDumpWithHint("DebugDumpWithHint", 0, N_INPUT_PARAMETERS, + ArgumentsOrder::DEFAULT_ORDER, VariableType::VOID()); + *callSign = debugDumpWithHint; + std::array params = { + VariableType::NATIVE_POINTER(), // String created via CircuitBuilder::StringPtr() + VariableType::JS_POINTER() // Tagged value of the object to be dumped }; - callSign->SetVariadicArgs(true); callSign->SetParameters(params.data()); callSign->SetGCLeafFunction(true); callSign->SetTargetKind(CallSignature::TargetKind::RUNTIME_STUB_NO_GC); } -DEF_CALL_SIGNATURE(ProfileDefineClass) +DEF_CALL_SIGNATURE(DebugPrint) { - // 4: 4 input parameters - CallSignature defineProfInstruction( - "ProfileDefineClass", 0, 4, ArgumentsOrder::DEFAULT_ORDER, VariableType::VOID()); - *callSign = defineProfInstruction; - // 4: 4 input parameters - std::array params = { // 4 : 4 input parameters - VariableType::NATIVE_POINTER(), - VariableType::JS_ANY(), + // 1 : 1 input parameters + CallSignature debugPrint("DebugPrint", 0, 1, + ArgumentsOrder::DEFAULT_ORDER, VariableType::VOID()); + *callSign = debugPrint; + // 1 : 1 input parameters + std::array params = { VariableType::INT32(), - VariableType::JS_ANY(), }; callSign->SetVariadicArgs(true); callSign->SetParameters(params.data()); @@ -1444,17 +1479,15 @@ DEF_CALL_SIGNATURE(ProfileDefineClass) callSign->SetTargetKind(CallSignature::TargetKind::RUNTIME_STUB_NO_GC); } -DEF_CALL_SIGNATURE(ProfileOpType) +DEF_CALL_SIGNATURE(DebugPrintCustom) { - // 4: 4 input parameters - CallSignature typeProfInstruction("ProfileOpType", 0, 4, ArgumentsOrder::DEFAULT_ORDER, VariableType::VOID()); - *callSign = typeProfInstruction; - // 4: 4 input parameters - std::array params = { // 4 : 4 input parameters - VariableType::NATIVE_POINTER(), - VariableType::JS_ANY(), - VariableType::INT32(), - VariableType::INT32(), + // 1 : 1 input parameters + CallSignature debugPrintCustom("DebugPrintCustom", 0, 1, + ArgumentsOrder::DEFAULT_ORDER, VariableType::VOID()); + *callSign = debugPrintCustom; + // 1 : 1 input parameters + std::array params = { + VariableType::NATIVE_POINTER() // Format string created via CircuitBuilder::StringPtr() }; callSign->SetVariadicArgs(true); callSign->SetParameters(params.data()); @@ -1462,18 +1495,16 @@ DEF_CALL_SIGNATURE(ProfileOpType) callSign->SetTargetKind(CallSignature::TargetKind::RUNTIME_STUB_NO_GC); } -DEF_CALL_SIGNATURE(ProfileObjLayout) +DEF_CALL_SIGNATURE(DebugPrintInstruction) { - // 4: 4 input parameters - CallSignature layoutProfInstruction("ProfileObjLayout", 0, 5, ArgumentsOrder::DEFAULT_ORDER, VariableType::VOID()); - *callSign = layoutProfInstruction; - // 4: 4 input parameters - std::array params = { // 5 : 5 input parameters + // 2 : 2 input parameters + CallSignature debugPrintInstruction("DebugPrintInstruction", 0, 2, + ArgumentsOrder::DEFAULT_ORDER, VariableType::VOID()); + *callSign = debugPrintInstruction; + // 2 : 2 input parameters + std::array params = { + VariableType::NATIVE_POINTER(), VariableType::NATIVE_POINTER(), - VariableType::JS_ANY(), - VariableType::INT32(), - VariableType::JS_ANY(), - VariableType::INT32(), }; callSign->SetVariadicArgs(true); callSign->SetParameters(params.data()); @@ -1481,6 +1512,21 @@ DEF_CALL_SIGNATURE(ProfileObjLayout) callSign->SetTargetKind(CallSignature::TargetKind::RUNTIME_STUB_NO_GC); } +DEF_CALL_SIGNATURE(Comment) +{ + // 1 : 1 input parameters + CallSignature comment("Comment", 0, 1, + ArgumentsOrder::DEFAULT_ORDER, VariableType::VOID()); + *callSign = comment; + // 1 : 1 input parameters + std::array params = { + VariableType::NATIVE_POINTER(), + }; + callSign->SetParameters(params.data()); + callSign->SetGCLeafFunction(true); + callSign->SetTargetKind(CallSignature::TargetKind::RUNTIME_STUB_NO_GC); +} + DEF_CALL_SIGNATURE(FatalPrint) { // 1 : 1 input parameters @@ -1497,6 +1543,22 @@ DEF_CALL_SIGNATURE(FatalPrint) callSign->SetTargetKind(CallSignature::TargetKind::RUNTIME_STUB_NO_GC); } +DEF_CALL_SIGNATURE(FatalPrintCustom) +{ + // 1 : 1 input parameters + CallSignature fatalPrintCustom("FatalPrintCustom", 0, 1, + ArgumentsOrder::DEFAULT_ORDER, VariableType::VOID()); + *callSign = fatalPrintCustom; + // 1 : 1 input parameters + std::array params = { + VariableType::NATIVE_POINTER() // Format string created via CircuitBuilder::StringPtr() + }; + callSign->SetVariadicArgs(true); + callSign->SetParameters(params.data()); + callSign->SetGCLeafFunction(true); + callSign->SetTargetKind(CallSignature::TargetKind::RUNTIME_STUB_NO_GC); +} + DEF_CALL_SIGNATURE(GetActualArgvNoGC) { CallSignature index("GetActualArgvNoGC", 0, 1, ArgumentsOrder::DEFAULT_ORDER, VariableType::NATIVE_POINTER()); @@ -1642,9 +1704,24 @@ DEF_CALL_SIGNATURE(FindElementWithCache) } DEF_CALL_SIGNATURE(DoubleToInt) +{ + // 2 : 2 input parameters + CallSignature index("DoubleToInt", 0, 2, ArgumentsOrder::DEFAULT_ORDER, VariableType::INT32()); + *callSign = index; + // 2 : 2 input parameters + std::array params = { + VariableType::FLOAT64(), + VariableType::NATIVE_POINTER(), + }; + callSign->SetParameters(params.data()); + callSign->SetGCLeafFunction(true); + callSign->SetTargetKind(CallSignature::TargetKind::RUNTIME_STUB_NO_GC); +} + +DEF_CALL_SIGNATURE(DoubleToLength) { // 1 : 1 input parameters - CallSignature index("DoubleToInt", 0, 1, ArgumentsOrder::DEFAULT_ORDER, VariableType::INT32()); + CallSignature index("DoubleToLength", 0, 1, ArgumentsOrder::DEFAULT_ORDER, VariableType::JS_ANY()); *callSign = index; // 1 : 1 input parameters std::array params = { @@ -1915,8 +1992,8 @@ DEF_CALL_SIGNATURE(EndCallTimer) { CallSignature index("EndCallTimer", 0, 2, ArgumentsOrder::DEFAULT_ORDER, VariableType::VOID()); *callSign = index; - // 3 : 3 input parameters - std::array params = { + // 2 : 2 input parameters + std::array params = { VariableType::NATIVE_POINTER(), VariableType::JS_ANY() }; @@ -1924,4 +2001,51 @@ DEF_CALL_SIGNATURE(EndCallTimer) callSign->SetGCLeafFunction(true); callSign->SetTargetKind(CallSignature::TargetKind::RUNTIME_STUB_NO_GC); } + +DEF_CALL_SIGNATURE(GetSingleCharCodeByIndex) +{ + // 3 : 3 input parameters + CallSignature signature("GetSingleCharCodeByIndex", 0, 3, + ArgumentsOrder::DEFAULT_ORDER, VariableType::INT32()); + *callSign = signature; + // 3 : 3 input parameters + std::array params = { + VariableType::NATIVE_POINTER(), // glue + VariableType::JS_ANY(), // ecmaString + VariableType::INT32(), // index + }; + callSign->SetParameters(params.data()); + callSign->SetCallConv(CallSignature::CallConv::CCallConv); +} + +DEF_CALL_SIGNATURE(CreateStringBySingleCharCode) +{ + // 2 : 2 input parameters + CallSignature signature("CreateStringByCharCode", 0, 2, + ArgumentsOrder::DEFAULT_ORDER, VariableType::JS_ANY()); + *callSign = signature; + // 2 : 2 input parameters + std::array params = { + VariableType::NATIVE_POINTER(), // glue + VariableType::INT32(), // charcode + }; + callSign->SetParameters(params.data()); + callSign->SetCallConv(CallSignature::CallConv::CCallConv); +} + +DEF_CALL_SIGNATURE(FastStringEqual) +{ + // 3 : 3 input parameters + CallSignature signature("FastStringEqual", 0, 3, + ArgumentsOrder::DEFAULT_ORDER, VariableType::BOOL()); + *callSign = signature; + // 3 : 3 input parameters + std::array params = { + VariableType::NATIVE_POINTER(), // glue + VariableType::JS_ANY(), // ecmaString1 + VariableType::JS_ANY(), // ecmaString2 + }; + callSign->SetParameters(params.data()); + callSign->SetCallConv(CallSignature::CallConv::CCallConv); +} } // namespace panda::ecmascript::kungfu diff --git a/ecmascript/compiler/call_signature.h b/ecmascript/compiler/call_signature.h index 0a55bbe0e9d37dc8e93d076544a4bd19f346c543..fb14695c6d08b5895b292804b3f94a43ad1e891b 100644 --- a/ecmascript/compiler/call_signature.h +++ b/ecmascript/compiler/call_signature.h @@ -21,7 +21,6 @@ #include #include "ecmascript/compiler/variable_type.h" -#include "ecmascript/compiler/test_stubs_signature.h" #include "libpandabase/macros.h" #include "libpandabase/utils/bit_field.h" @@ -410,19 +409,24 @@ private: V(ResumeRspAndReturn) \ V(ResumeCaughtFrameAndDispatch) \ V(ResumeUncaughtFrameAndReturn) \ + V(ResumeRspAndRollback) \ V(StringsAreEquals) \ V(BigIntEquals) \ + V(BigIntSameValueZero) \ + V(Dump) \ + V(DebugDump) \ + V(DumpWithHint) \ + V(DebugDumpWithHint) \ V(DebugPrint) \ + V(DebugPrintCustom) \ V(DebugPrintInstruction) \ V(Comment) \ - V(ProfileCall) \ - V(ProfileDefineClass) \ - V(ProfileOpType) \ - V(ProfileObjLayout) \ V(FatalPrint) \ + V(FatalPrintCustom) \ V(GetActualArgvNoGC) \ V(InsertOldToNewRSet) \ V(DoubleToInt) \ + V(DoubleToLength) \ V(FloatMod) \ V(FloatSqrt) \ V(FloatCos) \ @@ -457,7 +461,9 @@ private: V(CallReturnWithArgv) \ V(StartCallTimer) \ V(EndCallTimer) \ - TEST_STUB_SIGNATRUE_LIST(V) + V(GetSingleCharCodeByIndex) \ + V(CreateStringBySingleCharCode) \ + V(FastStringEqual) #define DECL_CALL_SIGNATURE(name) \ class name##CallSignature final { \ diff --git a/ecmascript/compiler/circuit.cpp b/ecmascript/compiler/circuit.cpp index 9baf7aa906e4d813f52da1dcf4c7c633c620eb6d..9edca7ef2de664002bda0bcdc24f7d5265aa332d 100644 --- a/ecmascript/compiler/circuit.cpp +++ b/ecmascript/compiler/circuit.cpp @@ -21,10 +21,12 @@ #include "ecmascript/platform/map.h" namespace panda::ecmascript::kungfu { -Circuit::Circuit(NativeAreaAllocator* allocator, DebugInfo* debugInfo, const char* funcName, bool isArch64) +Circuit::Circuit(NativeAreaAllocator* allocator, DebugInfo* debugInfo, const char* funcName, + bool isArch64, panda::ecmascript::FrameType type) : circuitSize_(0), gateCount_(0), time_(1), + frameType_(type), isArch64_(isArch64), chunk_(allocator), root_(Circuit::NullGate()), @@ -160,13 +162,70 @@ void Circuit::PrintAllGatesWithBytecode() const std::vector gateList; GetAllGates(gateList); for (const auto &gate : gateList) { - if (GetOpCode(gate) == OpCode::JS_BYTECODE) { - const Gate *gatePtr = LoadGatePtrConst(gate); - auto opcode = gatePtr->GetJSBytecodeMetaData()->GetByteCodeOpcode(); - std::string bytecodeStr = GetEcmaOpcodeStr(opcode); - LoadGatePtrConst(gate)->PrintByteCode(bytecodeStr); - } else { - LoadGatePtrConst(gate)->Print(); + auto opcode = GetOpCode(gate); + switch (opcode) { + case OpCode::JS_BYTECODE: { + const Gate *gatePtr = LoadGatePtrConst(gate); + auto bytecode = gatePtr->GetJSBytecodeMetaData()->GetByteCodeOpcode(); + std::string bytecodeStr = GetEcmaOpcodeStr(bytecode); + gatePtr->PrintGateWithAdditionOp(bytecodeStr); + break; + } + case OpCode::TYPED_BINARY_OP: { + const Gate *gatePtr = LoadGatePtrConst(gate); + auto typedOp = gatePtr->GetTypedBinaryMetaData()->GetTypedBinaryOp(); + std::string typedOpStr = GateMetaData::Str(typedOp); + gatePtr->PrintGateWithAdditionOp(typedOpStr); + break; + } + case OpCode::TYPED_UNARY_OP: { + const Gate *gatePtr = LoadGatePtrConst(gate); + auto typedOp = TypedUnaryAccessor(gatePtr->GetOneParameterMetaData()->GetValue()).GetTypedUnOp(); + std::string typedOpStr = GateMetaData::Str(typedOp); + gatePtr->PrintGateWithAdditionOp(typedOpStr); + break; + } + case OpCode::TYPED_CONDITION_JUMP: { + const Gate *gatePtr = LoadGatePtrConst(gate); + auto typedOp = TypedJumpAccessor(gatePtr->GetOneParameterMetaData()->GetValue()).GetTypedJumpOp(); + std::string typedOpStr = GateMetaData::Str(typedOp); + gatePtr->PrintGateWithAdditionOp(typedOpStr); + break; + } + case OpCode::LOAD_ELEMENT: { + const Gate *gatePtr = LoadGatePtrConst(gate); + auto typedOp = static_cast(gatePtr->GetOneParameterMetaData()->GetValue()); + std::string typedOpStr = GateMetaData::Str(typedOp); + gatePtr->PrintGateWithAdditionOp(typedOpStr); + break; + } + case OpCode::STORE_ELEMENT: { + const Gate *gatePtr = LoadGatePtrConst(gate); + auto typedOp = static_cast(gatePtr->GetOneParameterMetaData()->GetValue()); + std::string typedOpStr = GateMetaData::Str(typedOp); + gatePtr->PrintGateWithAdditionOp(typedOpStr); + break; + } + case OpCode::TYPED_CALLTARGETCHECK_OP: { + const Gate *gatePtr = LoadGatePtrConst(gate); + auto typedOp = gatePtr->GetTypedCallTargetCheckMetaData()->GetTypedCallTargetCheckOp(); + std::string typedOpStr = GateMetaData::Str(typedOp); + gatePtr->PrintGateWithAdditionOp(typedOpStr); + break; + } + case OpCode::CONVERT: + case OpCode::CHECK_AND_CONVERT: { + const Gate *gatePtr = LoadGatePtrConst(gate); + ValuePairTypeAccessor accessor(gatePtr->GetOneParameterMetaData()->GetValue()); + auto srcType = accessor.GetSrcType(); + auto dstType = accessor.GetDstType(); + std::string typedOpStr = GateMetaData::Str(srcType) + "_TO_" + GateMetaData::Str(dstType); + gatePtr->PrintGateWithAdditionOp(typedOpStr); + break; + } + default: + LoadGatePtrConst(gate)->Print(); + break; } } } @@ -448,7 +507,7 @@ GateRef Circuit::GetConstantGate(MachineType machineType, uint64_t value, { auto search = constantCache_.find({machineType, value, type}); if (search != constantCache_.end()) { - return constantCache_.at({machineType, value, type}); + return search->second; } auto gate = NewGate(metaBuilder_.Constant(value), machineType, type); constantCache_[{machineType, value, type}] = gate; @@ -463,7 +522,7 @@ void Circuit::ClearConstantCache(MachineType machineType, uint64_t value, GateTy } } -GateRef Circuit::GetConstantStringGate(MachineType machineType, const std::string &str, +GateRef Circuit::GetConstantStringGate(MachineType machineType, std::string_view str, GateType type) { auto gate = NewGate(metaBuilder_.ConstString(str), machineType, type); diff --git a/ecmascript/compiler/circuit.h b/ecmascript/compiler/circuit.h index 739fc6e3ba50b4d4cb533cd9f49051d4c99bd601..636b2292f0455558f2748d4e15803def1d121811 100644 --- a/ecmascript/compiler/circuit.h +++ b/ecmascript/compiler/circuit.h @@ -23,7 +23,10 @@ #include #include "ecmascript/compiler/gate.h" -#include "ecmascript/compiler/gate_meta_data.h" +#include "ecmascript/compiler/share_gate_meta_data.h" +#include "ecmascript/compiler/lcr_gate_meta_data.h" +#include "ecmascript/compiler/mcr_gate_meta_data.h" +#include "ecmascript/compiler/hcr_gate_meta_data.h" #include "ecmascript/compiler/gate_meta_data_builder.h" #include "ecmascript/frames.h" @@ -41,7 +44,7 @@ enum class VisitState : uint8_t { class Circuit { // note: calling NewGate could make all saved Gate* invalid public: explicit Circuit(NativeAreaAllocator* allocator, DebugInfo* dInfo = nullptr, const char* funcName = nullptr, - bool isArch64 = true); + bool isArch64 = true, FrameType type = FrameType::OPTIMIZED_FRAME); ~Circuit(); NO_COPY_SEMANTIC(Circuit); NO_MOVE_SEMANTIC(Circuit); @@ -61,7 +64,7 @@ public: void SetFrameType(panda::ecmascript::FrameType type); GateRef GetConstantGate(MachineType machineType, uint64_t value, GateType type); void ClearConstantCache(MachineType machineType, uint64_t value, GateType type); - GateRef GetConstantStringGate(MachineType machineType, const std::string &str, GateType type); + GateRef GetConstantStringGate(MachineType machineType, std::string_view str, GateType type); GateRef NewArg(MachineType machineType, size_t index, GateType type, GateRef argRoot); GateRef GetInitialEnvGate(GateRef jsFunc); size_t GetGateCount() const; @@ -132,6 +135,14 @@ public: GATE_META_DATA_LIST_WITH_BOOL(DECLARE_GATE_META) #undef DECLARE_GATE_META +#define DECLARE_GATE_META_WITH_BOOL_VALUE_IN(NAME, OP, R, S, D, V) \ + const GateMetaData* NAME(size_t value, bool flag) \ + { \ + return metaBuilder_.NAME(value, flag); \ + } + GATE_META_DATA_LIST_WITH_BOOL_VALUE_IN(DECLARE_GATE_META_WITH_BOOL_VALUE_IN) +#undef DECLARE_GATE_META_WITH_BOOL_VALUE_IN + #define DECLARE_GATE_META(NAME, OP, R, S, D, V) \ const GateMetaData* NAME(uint64_t value, uint64_t pcOffset) \ { \ @@ -140,6 +151,14 @@ public: GATE_META_DATA_LIST_WITH_PC_OFFSET(DECLARE_GATE_META) #undef DECLARE_GATE_META +#define DECLARE_GATE_META_FOR_CALL(NAME, OP, R, S, D, V) \ + const GateMetaData* NAME(uint64_t value, uint64_t pcOffset, bool noGC) \ + { \ + return metaBuilder_.NAME(value, pcOffset, noGC); \ + } + GATE_META_DATA_LIST_FOR_CALL(DECLARE_GATE_META_FOR_CALL) +#undef DECLARE_GATE_META_FOR_CALL + #define DECLARE_GATE_META(NAME, OP, R, S, D, V) \ const GateMetaData* NAME(uint64_t pcOffset) const \ { \ @@ -167,6 +186,11 @@ public: return metaBuilder_.TypedBinaryOp(value, binOp, type); } + const GateMetaData* TypedCallTargetCheckOp(uint32_t numIns, uint64_t value, TypedCallTargetCheckOp checkOp) + { + return metaBuilder_.TypedCallTargetCheckOp(numIns, value, checkOp); + } + GateRef DeadGate() { if (dead_ == NullGate()) { @@ -183,7 +207,7 @@ public: bool IsOptimizedJSFunctionFrame() const { - return frameType_ == panda::ecmascript::FrameType::OPTIMIZED_JS_FUNCTION_FRAME + return frameType_ == FrameType::OPTIMIZED_JS_FUNCTION_FRAME || frameType_ == FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME; } @@ -240,7 +264,7 @@ private: std::map, GateRef> constantCache_ {}; std::map, GateRef> constantDataCache_ {}; std::map initialEnvCache_ {}; - panda::ecmascript::FrameType frameType_ {panda::ecmascript::FrameType::OPTIMIZED_FRAME}; + panda::ecmascript::FrameType frameType_ {FrameType::OPTIMIZED_FRAME}; bool isArch64_ { false }; Chunk chunk_; diff --git a/ecmascript/compiler/circuit_builder-inl.h b/ecmascript/compiler/circuit_builder-inl.h index 285d808a17c4d2601f41976d414d24cb71771795..293493271377ecdd30e9b8193411da096908e777 100644 --- a/ecmascript/compiler/circuit_builder-inl.h +++ b/ecmascript/compiler/circuit_builder-inl.h @@ -36,7 +36,12 @@ GateRef CircuitBuilder::Undefined() return UndefineConstant(); } -GateRef CircuitBuilder::Equal(GateRef x, GateRef y) +GateRef CircuitBuilder::Hole() +{ + return HoleConstant(); +} + +GateRef CircuitBuilder::Equal(GateRef x, GateRef y, const char* comment) { auto xType = acc_.GetMachineType(x); switch (xType) { @@ -47,17 +52,17 @@ GateRef CircuitBuilder::Equal(GateRef x, GateRef y) case I16: case I32: case I64: - return BinaryCmp(circuit_->Icmp(static_cast(ICmpCondition::EQ)), x, y); + return BinaryCmp(circuit_->Icmp(static_cast(ICmpCondition::EQ)), x, y, comment); case F32: case F64: - return BinaryCmp(circuit_->Fcmp(static_cast(FCmpCondition::OEQ)), x, y); + return BinaryCmp(circuit_->Fcmp(static_cast(FCmpCondition::OEQ)), x, y, comment); default: LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); } } -GateRef CircuitBuilder::NotEqual(GateRef x, GateRef y) +GateRef CircuitBuilder::NotEqual(GateRef x, GateRef y, const char* comment) { auto xType = acc_.GetMachineType(x); switch (xType) { @@ -68,10 +73,10 @@ GateRef CircuitBuilder::NotEqual(GateRef x, GateRef y) case I16: case I32: case I64: - return BinaryCmp(circuit_->Icmp(static_cast(ICmpCondition::NE)), x, y); + return BinaryCmp(circuit_->Icmp(static_cast(ICmpCondition::NE)), x, y, comment); case F32: case F64: - return BinaryCmp(circuit_->Fcmp(static_cast(FCmpCondition::ONE)), x, y); + return BinaryCmp(circuit_->Fcmp(static_cast(FCmpCondition::ONE)), x, y, comment); default: LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); @@ -175,7 +180,7 @@ GateRef CircuitBuilder::DoubleToInt(GateRef x, Label *exit) Bind(&overflow); { result = CallNGCRuntime(acc_.GetGlueFromArgList(), RTSTUB_ID(DoubleToInt), - Circuit::NullGate(), { x }, Circuit::NullGate()); + Circuit::NullGate(), { x, IntPtr(base::INT32_BITS) }, Circuit::NullGate()); Jump(exit); } Bind(exit); @@ -227,6 +232,20 @@ GateRef CircuitBuilder::BinaryOp(GateRef x, GateRef y) return Circuit::NullGate(); } +template +GateRef CircuitBuilder::BinaryOpWithOverflow(GateRef x, GateRef y) +{ + if (Op == OpCode::ADD) { + return BinaryArithmetic(circuit_->AddWithOverflow(), Type, x, y); + } else if (Op == OpCode::SUB) { + return BinaryArithmetic(circuit_->SubWithOverflow(), Type, x, y); + } else if (Op == OpCode::MUL) { + return BinaryArithmetic(circuit_->MulWithOverflow(), Type, x, y); + } + UNREACHABLE(); + return Circuit::NullGate(); +} + GateRef CircuitBuilder::IntPtrLSR(GateRef x, GateRef y) { auto ptrSize = env_->Is32Bit() ? MachineType::I32 : MachineType::I64; @@ -360,7 +379,8 @@ inline GateRef CircuitBuilder::IsJSHClass(GateRef obj) GateRef CircuitBuilder::TaggedIsHeapObject(GateRef x) { x = ChangeTaggedPointerToInt64(x); - return Equal(Int64And(x, Int64(JSTaggedValue::TAG_HEAPOBJECT_MASK)), Int64(0)); + auto t = Int64And(x, Int64(JSTaggedValue::TAG_HEAPOBJECT_MASK), GateType::Empty(), "checkHeapObject"); + return Equal(t, Int64(0), "checkHeapObject"); } GateRef CircuitBuilder::TaggedIsAsyncGeneratorObject(GateRef x) @@ -390,6 +410,13 @@ GateRef CircuitBuilder::TaggedIsGeneratorObject(GateRef x) return LogicAnd(isHeapObj, isAsyncGeneratorObj); } +GateRef CircuitBuilder::TaggedIsJSArray(GateRef x) +{ + GateRef objType = GetObjectType(LoadHClass(x)); + GateRef isJSArray = Equal(objType, Int32(static_cast(JSType::JS_ARRAY))); + return isJSArray; +} + GateRef CircuitBuilder::TaggedIsPropertyBox(GateRef x) { return LogicAnd(TaggedIsHeapObject(x), @@ -488,6 +515,16 @@ GateRef CircuitBuilder::BooleanToTaggedBooleanPtr(GateRef x) return Int64ToTaggedPtr(Int64Or(val, Int64(JSTaggedValue::TAG_BOOLEAN_MASK))); } +GateRef CircuitBuilder::BooleanToInt32(GateRef x) +{ + return ZExtInt1ToInt32(x); +} + +GateRef CircuitBuilder::BooleanToFloat64(GateRef x) +{ + return ChangeInt32ToFloat64(ZExtInt1ToInt32(x)); +} + GateRef CircuitBuilder::Float32ToTaggedDoublePtr(GateRef x) { GateRef val = ExtFloat32ToDouble(x); @@ -566,6 +603,11 @@ GateRef CircuitBuilder::LoadObjectFromWeakRef(GateRef x) return PtrAdd(x, IntPtr(-JSTaggedValue::TAG_WEAK)); } +GateRef CircuitBuilder::CreateWeakRef(GateRef x) +{ + return PtrAdd(x, IntPtr(JSTaggedValue::TAG_WEAK)); +} + // object operation GateRef CircuitBuilder::LoadHClass(GateRef object) { @@ -596,6 +638,15 @@ inline GateRef CircuitBuilder::IsJSFunctionWithBit(GateRef obj) return NotEqual(Int32And(bitfield, Int32(1LU << JSHClass::IsJSFunctionBit::START_BIT)), Int32(0)); } +inline GateRef CircuitBuilder::IsOptimizedAndNotFastCall(GateRef obj) +{ + GateRef hClass = LoadHClass(obj); + GateRef bitfieldOffset = Int32(JSHClass::BIT_FIELD_OFFSET); + GateRef bitfield = Load(VariableType::INT32(), hClass, bitfieldOffset); + GateRef optimizedFastCallBitsInBitfield = Int32And(bitfield, Int32(JSHClass::OPTIMIZED_FASTCALL_BITS)); + return Equal(optimizedFastCallBitsInBitfield, Int32(JSHClass::OPTIMIZED_BIT)); +} + inline GateRef CircuitBuilder::IsOptimized(GateRef obj) { GateRef hClass = LoadHClass(obj); @@ -651,13 +702,22 @@ GateRef CircuitBuilder::IsDictionaryModeByHClass(GateRef hClass) Int32(0)); } -GateRef CircuitBuilder::IsIsStableElementsByHClass(GateRef hClass) +GateRef CircuitBuilder::GetElementsKindByHClass(GateRef hClass) +{ + GateRef bitfieldOffset = IntPtr(JSHClass::BIT_FIELD_OFFSET); + GateRef bitfield = Load(VariableType::INT32(), hClass, bitfieldOffset); + return Int32And(Int32LSR(bitfield, + Int32(JSHClass::ElementsKindBits::START_BIT)), + Int32((1LLU << JSHClass::ElementsKindBits::SIZE) - 1)); +} + +GateRef CircuitBuilder::HasConstructorByHClass(GateRef hClass) { GateRef bitfieldOffset = Int32(JSHClass::BIT_FIELD_OFFSET); GateRef bitfield = Load(VariableType::INT32(), hClass, bitfieldOffset); return NotEqual(Int32And(Int32LSR(bitfield, - Int32(JSHClass::IsStableElementsBit::START_BIT)), - Int32((1LU << JSHClass::IsStableElementsBit::SIZE) - 1)), + Int32(JSHClass::HasConstructorBits::START_BIT)), + Int32((1LU << JSHClass::HasConstructorBits::SIZE) - 1)), Int32(0)); } @@ -702,15 +762,22 @@ GateRef CircuitBuilder::IsClassConstructor(GateRef object) GateRef hClass = LoadHClass(object); GateRef bitfieldOffset = Int32(JSHClass::BIT_FIELD_OFFSET); GateRef bitfield = Load(VariableType::INT32(), hClass, bitfieldOffset); - return NotEqual(Int32And(Int32LSR(bitfield, - Int32(JSHClass::ClassConstructorBit::START_BIT)), - Int32((1LU << JSHClass::ClassConstructorBit::SIZE) - 1)), - Int32(0)); + return IsClassConstructorWithBitField(bitfield); } GateRef CircuitBuilder::IsClassConstructorWithBitField(GateRef bitfield) { - return NotEqual(Int32And(bitfield, Int32(1LU << JSHClass::ClassConstructorBit::START_BIT)), Int32(0)); + auto classBitMask = 1LU << JSHClass::IsClassConstructorOrPrototypeBit::START_BIT; + auto ctorBitMask = 1LU << JSHClass::ConstructorBit::START_BIT; + auto mask = Int32(classBitMask | ctorBitMask); + auto classCtor = Int32And(bitfield, mask); + return Int32Equal(classCtor, mask); +} + +GateRef CircuitBuilder::HasConstructor(GateRef object) +{ + GateRef hClass = LoadHClass(object); + return HasConstructorByHClass(hClass); } GateRef CircuitBuilder::IsConstructor(GateRef object) @@ -731,10 +798,16 @@ GateRef CircuitBuilder::IsClassPrototype(GateRef object) GateRef bitfieldOffset = IntPtr(JSHClass::BIT_FIELD_OFFSET); GateRef bitfield = Load(VariableType::INT32(), hClass, bitfieldOffset); // decode - return NotEqual( - Int32And(Int32LSR(bitfield, Int32(JSHClass::ClassPrototypeBit::START_BIT)), - Int32((1LU << JSHClass::ClassPrototypeBit::SIZE) - 1)), - Int32(0)); + return IsClassPrototypeWithBitField(bitfield); +} + +GateRef CircuitBuilder::IsClassPrototypeWithBitField(GateRef bitfield) +{ + auto classBitMask = 1LU << JSHClass::IsClassConstructorOrPrototypeBit::START_BIT; + auto ptBitMask = 1LU << JSHClass::IsPrototypeBit::START_BIT; + auto mask = Int32(classBitMask | ptBitMask); + auto classPt = Int32And(bitfield, mask); + return Int32Equal(classPt, mask); } GateRef CircuitBuilder::IsExtensible(GateRef object) @@ -837,6 +910,37 @@ GateRef CircuitBuilder::GetObjectSizeFromHClass(GateRef hClass) return PtrMul(ZExtInt32ToPtr(objectSizeInWords), IntPtr(JSTaggedValue::TaggedTypeSize())); } +GateRef CircuitBuilder::IsTreeString(GateRef obj) +{ + GateRef objectType = GetObjectType(LoadHClass(obj)); + return Int32Equal(objectType, Int32(static_cast(JSType::TREE_STRING))); +} + +GateRef CircuitBuilder::IsSlicedString(GateRef obj) +{ + GateRef objectType = GetObjectType(LoadHClass(obj)); + return Int32Equal(objectType, Int32(static_cast(JSType::SLICED_STRING))); +} + +GateRef CircuitBuilder::TreeStringIsFlat(GateRef string) +{ + GateRef second = GetSecondFromTreeString(string); + GateRef len = GetLengthFromString(second); + return Int32Equal(len, Int32(0)); +} + +GateRef CircuitBuilder::GetFirstFromTreeString(GateRef string) +{ + GateRef offset = IntPtr(TreeEcmaString::FIRST_OFFSET); + return Load(VariableType::JS_POINTER(), string, offset); +} + +GateRef CircuitBuilder::GetSecondFromTreeString(GateRef string) +{ + GateRef offset = IntPtr(TreeEcmaString::SECOND_OFFSET); + return Load(VariableType::JS_POINTER(), string, offset); +} + template GateRef CircuitBuilder::TypedBinaryOp(GateRef x, GateRef y, GateType xType, GateType yType, GateType gateType, PGOSampleType sampleType) @@ -852,6 +956,50 @@ GateRef CircuitBuilder::TypedBinaryOp(GateRef x, GateRef y, GateType xType, Gate return numberBinaryOp; } +template +GateRef CircuitBuilder::JSNoGCCallThisTargetTypeCheck(GateType type, GateRef func, GateRef methodId, GateRef gate) +{ + auto currentLabel = env_->GetCurrentLabel(); + auto currentControl = currentLabel->GetControl(); + auto currentDepend = currentLabel->GetDepend(); + auto frameState = acc_.GetFrameState(gate); + GateRef ret = GetCircuit()->NewGate(circuit_->TypedCallTargetCheckOp(CircuitBuilder::GATE_TWO_VALUESIN, + static_cast(type.Value()), Op), MachineType::I1, + {currentControl, currentDepend, func, methodId, frameState}, GateType::NJSValue()); + currentLabel->SetControl(ret); + currentLabel->SetDepend(ret); + return ret; +} + +template +GateRef CircuitBuilder::JSCallTargetTypeCheck(GateType type, GateRef func, GateRef methodIndex, GateRef gate) +{ + auto currentLabel = env_->GetCurrentLabel(); + auto currentControl = currentLabel->GetControl(); + auto currentDepend = currentLabel->GetDepend(); + auto frameState = acc_.GetFrameState(gate); + GateRef ret = GetCircuit()->NewGate(circuit_->TypedCallTargetCheckOp(CircuitBuilder::GATE_TWO_VALUESIN, + static_cast(type.Value()), Op), MachineType::I1, + {currentControl, currentDepend, func, methodIndex, frameState}, GateType::NJSValue()); + currentLabel->SetControl(ret); + currentLabel->SetDepend(ret); + return ret; +} + +template +GateRef CircuitBuilder::JSCallThisTargetTypeCheck(GateType type, GateRef func, GateRef gate) +{ + auto currentLabel = env_->GetCurrentLabel(); + auto currentControl = currentLabel->GetControl(); + auto currentDepend = currentLabel->GetDepend(); + auto frameState = acc_.GetFrameState(gate); + GateRef ret = GetCircuit()->NewGate(circuit_->TypedCallTargetCheckOp(1, static_cast(type.Value()), Op), + MachineType::I1, {currentControl, currentDepend, func, frameState}, GateType::NJSValue()); + currentLabel->SetControl(ret); + currentLabel->SetDepend(ret); + return ret; +} + template GateRef CircuitBuilder::TypedUnaryOp(GateRef x, GateType xType, GateType gateType) { @@ -867,13 +1015,13 @@ GateRef CircuitBuilder::TypedUnaryOp(GateRef x, GateType xType, GateType gateTyp } template -GateRef CircuitBuilder::TypedConditionJump(GateRef x, GateType xType) +GateRef CircuitBuilder::TypedConditionJump(GateRef x, GateType xType, uint32_t weight) { auto currentLabel = env_->GetCurrentLabel(); auto currentControl = currentLabel->GetControl(); auto currentDepend = currentLabel->GetDepend(); auto machineType = MachineType::NOVALUE; - auto jumpOp = TypedConditionJump(machineType, Op, xType, {currentControl, currentDepend, x}); + auto jumpOp = TypedConditionJump(machineType, Op, weight, xType, {currentControl, currentDepend, x}); currentLabel->SetControl(jumpOp); currentLabel->SetDepend(jumpOp); return jumpOp; @@ -968,6 +1116,18 @@ GateRef CircuitBuilder::LogicOr(GateRef x, GateRef y) return ret; } +GateRef CircuitBuilder::LoadFromTaggedArray(GateRef array, size_t index) +{ + auto dataOffset = TaggedArray::DATA_OFFSET + index * JSTaggedValue::TaggedTypeSize(); + return LoadConstOffset(VariableType::JS_ANY(), array, dataOffset); +} + +GateRef CircuitBuilder::StoreToTaggedArray(GateRef array, size_t index, GateRef value) +{ + auto dataOffset = TaggedArray::DATA_OFFSET + index * JSTaggedValue::TaggedTypeSize(); + return StoreConstOffset(VariableType::JS_ANY(), array, dataOffset, value); +} + int CircuitBuilder::NextVariableId() { return env_->NextVariableId(); @@ -1073,16 +1233,22 @@ inline GateRef CircuitBuilder::IsBase(GateRef ctor) return Int32LessThanOrEqual(kind, Int32(static_cast(FunctionKind::CLASS_CONSTRUCTOR))); } -inline GateRef CircuitBuilder::TypedCallBuiltin(GateRef hirGate, GateRef x, BuiltinsStubCSigns::ID id) +inline GateRef CircuitBuilder::TypedCallBuiltin(GateRef hirGate, const std::vector &args, + BuiltinsStubCSigns::ID id) { auto currentLabel = env_->GetCurrentLabel(); auto currentControl = currentLabel->GetControl(); auto currentDepend = currentLabel->GetDepend(); - GateRef idGate = Int8(static_cast(id)); - auto numberMathOp = TypedCallOperator(hirGate, MachineType::I64, {currentControl, currentDepend, x, idGate}); - currentLabel->SetControl(numberMathOp); - currentLabel->SetDepend(numberMathOp); - return numberMathOp; + + std::vector inList { currentControl, currentDepend }; + inList.insert(inList.end(), args.begin(), args.end()); + inList.push_back(Int8(static_cast(id))); + AppendFrameArgs(inList, hirGate); + + auto builtinOp = TypedCallOperator(hirGate, MachineType::I64, inList); + currentLabel->SetControl(builtinOp); + currentLabel->SetDepend(builtinOp); + return builtinOp; } inline GateRef CircuitBuilder::GetMethodId(GateRef func) diff --git a/ecmascript/compiler/circuit_builder.cpp b/ecmascript/compiler/circuit_builder.cpp index af1928106cb71b5b5fb86e35b86d6c90180a28f6..885888f039464c79d7ba7541a926a89818536fe0 100644 --- a/ecmascript/compiler/circuit_builder.cpp +++ b/ecmascript/compiler/circuit_builder.cpp @@ -64,9 +64,11 @@ GateRef CircuitBuilder::UndefineConstant() return circuit_->GetConstantGate(MachineType::I64, JSTaggedValue::VALUE_UNDEFINED, type); } -GateRef CircuitBuilder::Branch(GateRef state, GateRef condition) +GateRef CircuitBuilder::Branch(GateRef state, GateRef condition, uint32_t trueWeight, uint32_t falseWeight, + const char* comment) { - return circuit_->NewGate(circuit_->IfBranch(), { state, condition }); + auto value = BranchAccessor::ToValue(trueWeight, falseWeight); + return circuit_->NewGate(circuit_->IfBranch(value), { state, condition }, comment); } GateRef CircuitBuilder::SwitchBranch(GateRef state, GateRef index, int caseCounts) @@ -102,6 +104,23 @@ GateRef CircuitBuilder::LoopEnd(GateRef state) return circuit_->NewGate(circuit_->LoopBack(), { state }); } +GateRef CircuitBuilder::LoopExit(GateRef state) +{ + return circuit_->NewGate(circuit_->LoopExit(), { state }); +} + +GateRef CircuitBuilder::LoopExitDepend(GateRef state, GateRef depend) +{ + return circuit_->NewGate(circuit_->LoopExitDepend(), { state, depend }); +} + +GateRef CircuitBuilder::LoopExitValue(GateRef state, GateRef value) +{ + auto machineType = acc_.GetMachineType(value); + auto gateType = acc_.GetGateType(value); + return circuit_->NewGate(circuit_->LoopExitValue(), machineType, { state, value }, gateType); +} + GateRef CircuitBuilder::IfTrue(GateRef ifBranch) { return circuit_->NewGate(circuit_->IfTrue(), { ifBranch }); @@ -127,20 +146,40 @@ GateRef CircuitBuilder::DependRelay(GateRef state, GateRef depend) return circuit_->NewGate(circuit_->DependRelay(), { state, depend }); } +GateRef CircuitBuilder::ReadSp() +{ + return circuit_->NewGate(circuit_->ReadSp(), MachineType::I64, GateType::NJSValue()); +} + GateRef CircuitBuilder::Arguments(size_t index) { auto argListOfCircuit = circuit_->GetArgRoot(); return GetCircuit()->NewArg(MachineType::I64, index, GateType::NJSValue(), argListOfCircuit); } -GateRef CircuitBuilder::ObjectTypeCheck(GateType type, GateRef gate, GateRef index) +GateRef CircuitBuilder::ObjectTypeCheck(GateType type, bool isHeapObject, GateRef gate, GateRef hclassIndex) +{ + auto currentLabel = env_->GetCurrentLabel(); + auto currentControl = currentLabel->GetControl(); + auto currentDepend = currentLabel->GetDepend(); + auto frameState = acc_.FindNearestFrameState(currentDepend); + ObjectTypeAccessor accessor(type, isHeapObject); + GateRef ret = GetCircuit()->NewGate(circuit_->ObjectTypeCheck(accessor.ToValue()), MachineType::I1, + {currentControl, currentDepend, gate, hclassIndex, frameState}, GateType::NJSValue()); + currentLabel->SetControl(ret); + currentLabel->SetDepend(ret); + return ret; +} + +GateRef CircuitBuilder::ObjectTypeCompare(GateType type, bool isHeapObject, GateRef gate, GateRef hclassIndex) { auto currentLabel = env_->GetCurrentLabel(); auto currentControl = currentLabel->GetControl(); auto currentDepend = currentLabel->GetDepend(); auto frameState = acc_.FindNearestFrameState(currentDepend); - GateRef ret = GetCircuit()->NewGate(circuit_->ObjectTypeCheck(static_cast(type.Value())), - MachineType::I1, {currentControl, currentDepend, gate, index, frameState}, GateType::NJSValue()); + ObjectTypeAccessor accessor(type, isHeapObject); + GateRef ret = GetCircuit()->NewGate(circuit_->ObjectTypeCompare(accessor.ToValue()), MachineType::I1, + {currentControl, currentDepend, gate, hclassIndex, frameState}, GateType::NJSValue()); currentLabel->SetControl(ret); currentLabel->SetDepend(ret); return ret; @@ -152,31 +191,73 @@ GateRef CircuitBuilder::HeapObjectCheck(GateRef gate, GateRef frameState) auto currentControl = currentLabel->GetControl(); auto currentDepend = currentLabel->GetDepend(); GateRef ret = GetCircuit()->NewGate(circuit_->HeapObjectCheck(), + MachineType::I1, + {currentControl, currentDepend, gate, frameState}, + GateType::NJSValue()); + currentLabel->SetControl(ret); + currentLabel->SetDepend(ret); + return ret; +} + +GateRef CircuitBuilder::StableArrayCheck(GateRef gate, ElementsKind kind, ArrayMetaDataAccessor::Mode mode) +{ + auto currentLabel = env_->GetCurrentLabel(); + auto currentControl = currentLabel->GetControl(); + auto currentDepend = currentLabel->GetDepend(); + auto frameState = acc_.FindNearestFrameState(currentDepend); + ArrayMetaDataAccessor accessor(kind, mode); + GateRef ret = GetCircuit()->NewGate(circuit_->StableArrayCheck(accessor.ToValue()), MachineType::I1, {currentControl, currentDepend, gate, frameState}, GateType::NJSValue()); currentLabel->SetControl(ret); currentLabel->SetDepend(ret); return ret; } -GateRef CircuitBuilder::StableArrayCheck(GateRef gate) +GateRef CircuitBuilder::COWArrayCheck(GateRef gate) { auto currentLabel = env_->GetCurrentLabel(); auto currentControl = currentLabel->GetControl(); auto currentDepend = currentLabel->GetDepend(); auto frameState = acc_.FindNearestFrameState(currentDepend); - GateRef ret = GetCircuit()->NewGate(circuit_->StableArrayCheck(), + GateRef ret = GetCircuit()->NewGate(circuit_->COWArrayCheck(), MachineType::I1, {currentControl, currentDepend, gate, frameState}, GateType::NJSValue()); currentLabel->SetControl(ret); currentLabel->SetDepend(ret); return ret; } -GateRef CircuitBuilder::HClassStableArrayCheck(GateRef gate, GateRef frameState) +GateRef CircuitBuilder::EcmaStringCheck(GateRef gate) { auto currentLabel = env_->GetCurrentLabel(); auto currentControl = currentLabel->GetControl(); auto currentDepend = currentLabel->GetDepend(); - GateRef ret = GetCircuit()->NewGate(circuit_->HClassStableArrayCheck(), + auto frameState = acc_.FindNearestFrameState(currentDepend); + GateRef ret = GetCircuit()->NewGate(circuit_->EcmaStringCheck(), + MachineType::I1, {currentControl, currentDepend, gate, frameState}, GateType::NJSValue()); + currentLabel->SetControl(ret); + currentLabel->SetDepend(ret); + return ret; +} + +GateRef CircuitBuilder::FlattenTreeStringCheck(GateRef gate) +{ + auto currentLabel = env_->GetCurrentLabel(); + auto currentControl = currentLabel->GetControl(); + auto currentDepend = currentLabel->GetDepend(); + auto frameState = acc_.FindNearestFrameState(currentDepend); + GateRef ret = GetCircuit()->NewGate(circuit_->FlattenTreeStringCheck(), + MachineType::I1, {currentControl, currentDepend, gate, frameState}, GateType::NJSValue()); + currentLabel->SetControl(ret); + currentLabel->SetDepend(ret); + return ret; +} + +GateRef CircuitBuilder::HClassStableArrayCheck(GateRef gate, GateRef frameState, ArrayMetaDataAccessor accessor) +{ + auto currentLabel = env_->GetCurrentLabel(); + auto currentControl = currentLabel->GetControl(); + auto currentDepend = currentLabel->GetDepend(); + GateRef ret = GetCircuit()->NewGate(circuit_->HClassStableArrayCheck(accessor.ToValue()), MachineType::I1, {currentControl, currentDepend, gate, frameState}, GateType::NJSValue()); currentLabel->SetControl(ret); currentLabel->SetDepend(ret); @@ -208,6 +289,43 @@ GateRef CircuitBuilder::TypedArrayCheck(GateType type, GateRef gate) return ret; } +GateRef CircuitBuilder::LoadTypedArrayLength(GateType type, GateRef gate) +{ + auto currentLabel = env_->GetCurrentLabel(); + auto currentControl = currentLabel->GetControl(); + auto currentDepend = currentLabel->GetDepend(); + GateRef ret = GetCircuit()->NewGate(circuit_->LoadTypedArrayLength(static_cast(type.Value())), + MachineType::I64, {currentControl, currentDepend, gate}, GateType::IntType()); + currentLabel->SetControl(ret); + currentLabel->SetDepend(ret); + return ret; +} + +GateRef CircuitBuilder::StringEqual(GateRef x, GateRef y) +{ + auto currentLabel = env_->GetCurrentLabel(); + auto currentControl = currentLabel->GetControl(); + auto currentDepend = currentLabel->GetDepend(); + auto ret = GetCircuit()->NewGate(circuit_->StringEqual(), MachineType::I1, + { currentControl, currentDepend, x, y }, GateType::BooleanType()); + currentLabel->SetControl(ret); + currentLabel->SetDepend(ret); + return ret; +} + +GateRef CircuitBuilder::RangeGuard(GateRef gate, uint32_t left, uint32_t right) +{ + auto currentLabel = env_->GetCurrentLabel(); + auto currentControl = currentLabel->GetControl(); + auto currentDepend = currentLabel->GetDepend(); + UInt32PairAccessor accessor(left, right); + GateRef ret = GetCircuit()->NewGate(circuit_->RangeGuard(accessor.ToValue()), + MachineType::I64, {currentControl, currentDepend, gate}, GateType::IntType()); + currentLabel->SetControl(ret); + currentLabel->SetDepend(ret); + return ret; +} + GateRef CircuitBuilder::IndexCheck(GateType type, GateRef gate, GateRef index) { auto currentLabel = env_->GetCurrentLabel(); @@ -221,6 +339,31 @@ GateRef CircuitBuilder::IndexCheck(GateType type, GateRef gate, GateRef index) return ret; } +GateRef CircuitBuilder::TypeOfCheck(GateRef gate, GateType type) +{ + auto currentLabel = env_->GetCurrentLabel(); + auto currentControl = currentLabel->GetControl(); + auto currentDepend = currentLabel->GetDepend(); + auto frameState = acc_.FindNearestFrameState(currentDepend); + GateRef ret = GetCircuit()->NewGate(circuit_->TypeOfCheck(static_cast(type.Value())), + MachineType::I64, {currentControl, currentDepend, gate, frameState}, GateType::IntType()); + currentLabel->SetControl(ret); + currentLabel->SetDepend(ret); + return ret; +} + +GateRef CircuitBuilder::TypedTypeOf(GateType type) +{ + auto currentLabel = env_->GetCurrentLabel(); + auto currentControl = currentLabel->GetControl(); + auto currentDepend = currentLabel->GetDepend(); + GateRef ret = GetCircuit()->NewGate(circuit_->TypeOf(static_cast(type.Value())), + MachineType::I64, {currentControl, currentDepend}, GateType::AnyType()); + currentLabel->SetControl(ret); + currentLabel->SetDepend(ret); + return ret; +} + GateRef CircuitBuilder::IsJsCOWArray(GateRef obj) { // Elements of JSArray are shared and properties are not yet. @@ -234,6 +377,12 @@ GateRef CircuitBuilder::IsCOWArray(GateRef objectType) return Int32Equal(objectType, Int32(static_cast(JSType::COW_TAGGED_ARRAY))); } +GateRef CircuitBuilder::IsTaggedArray(GateRef object) +{ + GateRef objectType = GetObjectType(LoadHClass(object)); + return Int32Equal(objectType, Int32(static_cast(JSType::TAGGED_ARRAY))); +} + GateRef CircuitBuilder::GetElementsArray(GateRef object) { GateRef elementsOffset = IntPtr(JSObject::ELEMENTS_OFFSET); @@ -279,16 +428,20 @@ GateType CircuitBuilder::GetGateTypeOfValueType(ValueType type) } } -GateRef CircuitBuilder::CheckAndConvert(GateRef gate, ValueType src, ValueType dst) +GateRef CircuitBuilder::CheckAndConvert(GateRef gate, ValueType src, ValueType dst, ConvertSupport support) { auto currentLabel = env_->GetCurrentLabel(); + auto currentControl = currentLabel->GetControl(); auto currentDepend = currentLabel->GetDepend(); - auto frameState = acc_.FindNearestFrameState(currentDepend); + auto stateSplit = acc_.FindNearestStateSplit(currentDepend); + auto frameState = acc_.GetFrameState(stateSplit); MachineType machineType = GetMachineTypeOfValueType(dst); GateType gateType = GetGateTypeOfValueType(dst); - uint64_t value = ValuePairTypeAccessor::ToValue(src, dst); + uint64_t value = ValuePairTypeAccessor::ToValue(src, dst, support); GateRef ret = GetCircuit()->NewGate(circuit_->CheckAndConvert(value), - machineType, {gate, frameState}, gateType); + machineType, {currentControl, currentDepend, gate, frameState}, gateType); + currentLabel->SetControl(ret); + currentLabel->SetDepend(ret); return ret; } @@ -301,11 +454,31 @@ GateRef CircuitBuilder::Convert(GateRef gate, ValueType src, ValueType dst) return ret; } +GateRef CircuitBuilder::ConvertBoolToInt32(GateRef gate, ConvertSupport support) +{ + return CheckAndConvert(gate, ValueType::BOOL, ValueType::INT32, support); +} + +GateRef CircuitBuilder::ConvertBoolToFloat64(GateRef gate, ConvertSupport support) +{ + return CheckAndConvert(gate, ValueType::BOOL, ValueType::FLOAT64, support); +} + +GateRef CircuitBuilder::ConvertCharToEcmaString(GateRef gate) +{ + return Convert(gate, ValueType::CHAR, ValueType::ECMA_STRING); +} + GateRef CircuitBuilder::ConvertInt32ToFloat64(GateRef gate) { return Convert(gate, ValueType::INT32, ValueType::FLOAT64); } +GateRef CircuitBuilder::ConvertUInt32ToFloat64(GateRef gate) +{ + return Convert(gate, ValueType::UINT32, ValueType::FLOAT64); +} + GateRef CircuitBuilder::ConvertFloat64ToInt32(GateRef gate) { return Convert(gate, ValueType::FLOAT64, ValueType::INT32); @@ -326,11 +499,21 @@ GateRef CircuitBuilder::ConvertInt32ToTaggedInt(GateRef gate) return Convert(gate, ValueType::INT32, ValueType::TAGGED_INT); } +GateRef CircuitBuilder::ConvertUInt32ToTaggedNumber(GateRef gate) +{ + return Convert(gate, ValueType::UINT32, ValueType::TAGGED_NUMBER); +} + GateRef CircuitBuilder::ConvertInt32ToBool(GateRef gate) { return Convert(gate, ValueType::INT32, ValueType::BOOL); } +GateRef CircuitBuilder::ConvertUInt32ToBool(GateRef gate) +{ + return Convert(gate, ValueType::UINT32, ValueType::BOOL); +} + GateRef CircuitBuilder::ConvertFloat64ToBool(GateRef gate) { return Convert(gate, ValueType::FLOAT64, ValueType::BOOL); @@ -351,6 +534,11 @@ GateRef CircuitBuilder::ConvertFloat64ToTaggedDouble(GateRef gate) return Convert(gate, ValueType::FLOAT64, ValueType::TAGGED_DOUBLE); } +GateRef CircuitBuilder::CheckUInt32AndConvertToInt32(GateRef gate) +{ + return CheckAndConvert(gate, ValueType::UINT32, ValueType::INT32); +} + GateRef CircuitBuilder::CheckTaggedIntAndConvertToInt32(GateRef gate) { return CheckAndConvert(gate, ValueType::TAGGED_INT, ValueType::INT32); @@ -381,97 +569,86 @@ GateRef CircuitBuilder::CheckTaggedNumberAndConvertToFloat64(GateRef gate) return CheckAndConvert(gate, ValueType::TAGGED_NUMBER, ValueType::FLOAT64); } -GateRef CircuitBuilder::TryPrimitiveTypeCheck(GateType type, GateRef gate) +GateRef CircuitBuilder::CheckNullAndConvertToInt32(GateRef gate) { - if (acc_.GetOpCode(gate) == OpCode::CONSTANT) { - return Circuit::NullGate(); - } - auto currentLabel = env_->GetCurrentLabel(); - auto currentControl = currentLabel->GetControl(); - auto currentDepend = currentLabel->GetDepend(); - auto frameState = acc_.FindNearestFrameState(currentDepend); - GateRef ret = GetCircuit()->NewGate(circuit_->PrimitiveTypeCheck(static_cast(type.Value())), - MachineType::I1, {currentControl, currentDepend, gate, frameState}, GateType::NJSValue()); - currentLabel->SetControl(ret); - currentLabel->SetDepend(ret); - return ret; + return CheckAndConvert(gate, ValueType::TAGGED_NULL, ValueType::INT32); } -GateRef CircuitBuilder::CallTargetCheck(GateRef function, GateRef id, GateRef param, const char* comment) +GateRef CircuitBuilder::CheckTaggedBooleanAndConvertToInt32(GateRef gate) { - auto currentLabel = env_->GetCurrentLabel(); - auto currentControl = currentLabel->GetControl(); - auto currentDepend = currentLabel->GetDepend(); - auto frameState = acc_.FindNearestFrameState(currentDepend); - GateRef ret = GetCircuit()->NewGate(circuit_->TypedCallCheck(), - MachineType::I1, - { currentControl, currentDepend, function, id, param, frameState}, - GateType::NJSValue(), - comment); - currentLabel->SetControl(ret); - currentLabel->SetDepend(ret); - return ret; + return CheckAndConvert(gate, ValueType::TAGGED_BOOLEAN, ValueType::INT32); } -GateRef CircuitBuilder::JSCallTargetFromDefineFuncCheck(GateType type, GateRef func) +GateRef CircuitBuilder::CheckNullAndConvertToFloat64(GateRef gate) { - auto currentLabel = env_->GetCurrentLabel(); - auto currentControl = currentLabel->GetControl(); - auto currentDepend = currentLabel->GetDepend(); - auto frameState = acc_.FindNearestFrameState(currentDepend); - GateRef ret = GetCircuit()->NewGate(circuit_->JSCallTargetFromDefineFuncCheck(static_cast(type.Value())), - MachineType::I1, {currentControl, currentDepend, func, frameState}, GateType::NJSValue()); - currentLabel->SetControl(ret); - currentLabel->SetDepend(ret); - return ret; + return CheckAndConvert(gate, ValueType::TAGGED_NULL, ValueType::FLOAT64); } -GateRef CircuitBuilder::JSCallTargetTypeCheck(GateType type, GateRef func, GateRef methodIndex) +GateRef CircuitBuilder::CheckTaggedBooleanAndConvertToFloat64(GateRef gate) { - auto currentLabel = env_->GetCurrentLabel(); - auto currentControl = currentLabel->GetControl(); - auto currentDepend = currentLabel->GetDepend(); - auto frameState = acc_.FindNearestFrameState(currentDepend); - GateRef ret = GetCircuit()->NewGate(circuit_->JSCallTargetTypeCheck(static_cast(type.Value())), - MachineType::I1, {currentControl, currentDepend, func, methodIndex, frameState}, GateType::NJSValue()); - currentLabel->SetControl(ret); - currentLabel->SetDepend(ret); - return ret; + return CheckAndConvert(gate, ValueType::TAGGED_BOOLEAN, ValueType::FLOAT64); +} + +GateRef CircuitBuilder::CheckUndefinedAndConvertToFloat64(GateRef gate) +{ + return CheckAndConvert(gate, ValueType::UNDEFINED, ValueType::FLOAT64); } -GateRef CircuitBuilder::JSFastCallTargetTypeCheck(GateType type, GateRef func, GateRef methodIndex) +GateRef CircuitBuilder::CheckUndefinedAndConvertToBool(GateRef gate) { + return CheckAndConvert(gate, ValueType::UNDEFINED, ValueType::BOOL); +} + +GateRef CircuitBuilder::CheckNullAndConvertToBool(GateRef gate) +{ + return CheckAndConvert(gate, ValueType::TAGGED_NULL, ValueType::BOOL); +} + +GateRef CircuitBuilder::CheckUndefinedAndConvertToInt32(GateRef gate) +{ + return CheckAndConvert(gate, ValueType::UNDEFINED, ValueType::INT32); +} + +GateRef CircuitBuilder::TryPrimitiveTypeCheck(GateType type, GateRef gate) +{ + if (acc_.GetOpCode(gate) == OpCode::CONSTANT) { + return Circuit::NullGate(); + } auto currentLabel = env_->GetCurrentLabel(); auto currentControl = currentLabel->GetControl(); auto currentDepend = currentLabel->GetDepend(); auto frameState = acc_.FindNearestFrameState(currentDepend); - GateRef ret = GetCircuit()->NewGate(circuit_->JSFastCallTargetTypeCheck(static_cast(type.Value())), - MachineType::I1, {currentControl, currentDepend, func, methodIndex, frameState}, GateType::NJSValue()); + GateRef ret = GetCircuit()->NewGate(circuit_->PrimitiveTypeCheck(static_cast(type.Value())), + MachineType::I1, {currentControl, currentDepend, gate, frameState}, GateType::NJSValue()); currentLabel->SetControl(ret); currentLabel->SetDepend(ret); return ret; } -GateRef CircuitBuilder::JSCallThisTargetTypeCheck(GateType type, GateRef func) +GateRef CircuitBuilder::CallTargetCheck(GateRef gate, GateRef function, GateRef id, GateRef param, const char* comment) { auto currentLabel = env_->GetCurrentLabel(); auto currentControl = currentLabel->GetControl(); auto currentDepend = currentLabel->GetDepend(); - auto frameState = acc_.FindNearestFrameState(currentDepend); - GateRef ret = GetCircuit()->NewGate(circuit_->JSCallThisTargetTypeCheck(static_cast(type.Value())), - MachineType::I1, {currentControl, currentDepend, func, frameState}, GateType::NJSValue()); + auto frameState = acc_.GetFrameState(gate); + GateRef ret = GetCircuit()->NewGate(circuit_->TypedCallCheck(), + MachineType::I1, + { currentControl, currentDepend, function, id, param, frameState}, + GateType::NJSValue(), + comment); currentLabel->SetControl(ret); currentLabel->SetDepend(ret); return ret; } -GateRef CircuitBuilder::JSFastCallThisTargetTypeCheck(GateType type, GateRef func) +GateRef CircuitBuilder::JSCallTargetFromDefineFuncCheck(GateType type, GateRef func, GateRef gate) { auto currentLabel = env_->GetCurrentLabel(); auto currentControl = currentLabel->GetControl(); auto currentDepend = currentLabel->GetDepend(); - auto frameState = acc_.FindNearestFrameState(currentDepend); - GateRef ret = GetCircuit()->NewGate(circuit_->JSFastCallThisTargetTypeCheck(static_cast(type.Value())), + auto frameState = acc_.GetFrameState(gate); + GateRef ret = GetCircuit()->NewGate(circuit_->TypedCallTargetCheckOp(1, static_cast(type.Value()), + TypedCallTargetCheckOp::JSCALL_IMMEDIATE_AFTER_FUNC_DEF), MachineType::I1, {currentControl, currentDepend, func, frameState}, GateType::NJSValue()); currentLabel->SetControl(ret); currentLabel->SetDepend(ret); @@ -529,13 +706,14 @@ GateRef CircuitBuilder::GetSuperConstructor(GateRef ctor) return ret; } -GateRef CircuitBuilder::TypedCallOperator(GateRef hirGate, MachineType type, const std::initializer_list& args) +GateRef CircuitBuilder::TypedCallOperator(GateRef hirGate, MachineType type, const std::vector &inList) { ASSERT(acc_.GetOpCode(hirGate) == OpCode::JS_BYTECODE); - auto numValueIn = args.size() - 2; // 2: state & depend + auto numValueIn = inList.size() - 3; // 3: state & depend & frame state uint64_t pcOffset = acc_.TryGetPcOffset(hirGate); ASSERT(pcOffset != 0); - return GetCircuit()->NewGate(circuit_->TypedCallBuiltin(numValueIn, pcOffset), type, args, GateType::AnyType()); + return GetCircuit()->NewGate(circuit_->TypedCallBuiltin(numValueIn, pcOffset), type, inList.size(), inList.data(), + GateType::AnyType()); } GateRef CircuitBuilder::AddWithOverflow(GateRef left, GateRef right) @@ -591,40 +769,27 @@ GateRef CircuitBuilder::Float64CheckRightIsZero(GateRef right) return ret; } -GateRef CircuitBuilder::ValueCheckNegOverflow(GateRef value) +GateRef CircuitBuilder::LexVarIsHoleCheck(GateRef value) { auto currentLabel = env_->GetCurrentLabel(); auto currentControl = currentLabel->GetControl(); auto currentDepend = currentLabel->GetDepend(); auto frameState = acc_.FindNearestFrameState(currentDepend); - GateRef ret = GetCircuit()->NewGate(circuit_->ValueCheckNegOverflow(), + GateRef ret = GetCircuit()->NewGate(circuit_->LexVarIsHoleCheck(), MachineType::I1, {currentControl, currentDepend, value, frameState}, GateType::NJSValue()); currentLabel->SetControl(ret); currentLabel->SetDepend(ret); return ret; } -GateRef CircuitBuilder::NegativeIndexCheck(GateRef index) -{ - auto currentLabel = env_->GetCurrentLabel(); - auto currentControl = currentLabel->GetControl(); - auto currentDepend = currentLabel->GetDepend(); - auto frameState = acc_.FindNearestFrameState(currentDepend); - GateRef ret = GetCircuit()->NewGate(circuit_->NegativeIndexCheck(), - MachineType::I1, {currentControl, currentDepend, index, frameState}, GateType::NJSValue()); - currentLabel->SetControl(ret); - currentLabel->SetDepend(ret); - return ret; -} - -GateRef CircuitBuilder::LargeIndexCheck(GateRef index, GateRef length) +GateRef CircuitBuilder::ValueCheckNegOverflow(GateRef value) { auto currentLabel = env_->GetCurrentLabel(); auto currentControl = currentLabel->GetControl(); auto currentDepend = currentLabel->GetDepend(); auto frameState = acc_.FindNearestFrameState(currentDepend); - GateRef ret = GetCircuit()->NewGate(circuit_->LargeIndexCheck(), - MachineType::I1, {currentControl, currentDepend, index, length, frameState}, GateType::IntType()); + GateRef ret = GetCircuit()->NewGate(circuit_->ValueCheckNegOverflow(), + MachineType::I1, {currentControl, currentDepend, value, frameState}, GateType::NJSValue()); currentLabel->SetControl(ret); currentLabel->SetDepend(ret); return ret; @@ -678,10 +843,10 @@ GateRef CircuitBuilder::TypeConvert(MachineType type, GateType typeFrom, GateTyp type, inList.size(), inList.data(), GateType::AnyType()); } -GateRef CircuitBuilder::TypedConditionJump(MachineType type, TypedJumpOp jumpOp, GateType typeVal, - const std::vector& inList) +GateRef CircuitBuilder::TypedConditionJump(MachineType type, TypedJumpOp jumpOp, uint32_t weight, + GateType typeVal, const std::vector& inList) { - uint64_t value = TypedJumpAccessor::ToValue(typeVal, jumpOp); + uint64_t value = TypedJumpAccessor::ToValue(typeVal, jumpOp, weight); return GetCircuit()->NewGate(circuit_->TypedConditionJump(value), type, inList.size(), inList.data(), GateType::Empty()); } @@ -711,7 +876,7 @@ GateRef CircuitBuilder::IntPtr(int64_t val) return GetCircuit()->GetConstantGate(MachineType::ARCH, val, GateType::NJSValue()); } -GateRef CircuitBuilder::StringPtr(const std::string &str) +GateRef CircuitBuilder::StringPtr(std::string_view str) { return GetCircuit()->GetConstantStringGate(MachineType::ARCH, str, GateType::NJSValue()); } @@ -756,24 +921,29 @@ GateRef CircuitBuilder::ExceptionConstant() return GetCircuit()->GetConstantGate(MachineType::I64, JSTaggedValue::VALUE_EXCEPTION, type); } +GateRef CircuitBuilder::NanValue() +{ + return Double(std::numeric_limits::quiet_NaN()); +} + MachineType CircuitBuilder::GetMachineTypeFromVariableType(VariableType type) { return type.GetMachineType(); } GateRef CircuitBuilder::BinaryArithmetic(const GateMetaData* meta, MachineType machineType, - GateRef left, GateRef right, GateType gateType) + GateRef left, GateRef right, GateType gateType, const char* comment) { auto circuit = GetCircuit(); if (gateType == GateType::Empty()) { gateType = acc_.GetGateType(left); } - return circuit->NewGate(meta, machineType, { left, right }, gateType); + return circuit->NewGate(meta, machineType, { left, right }, gateType, comment); } -GateRef CircuitBuilder::BinaryCmp(const GateMetaData* meta, GateRef left, GateRef right) +GateRef CircuitBuilder::BinaryCmp(const GateMetaData* meta, GateRef left, GateRef right, const char* comment) { - return GetCircuit()->NewGate(meta, MachineType::I1, { left, right }, GateType::NJSValue()); + return GetCircuit()->NewGate(meta, MachineType::I1, { left, right }, GateType::NJSValue(), comment); } GateRef CircuitBuilder::CallBCHandler(GateRef glue, GateRef target, const std::vector &args, @@ -993,6 +1163,9 @@ GateRef CircuitBuilder::Call(const CallSignature* cs, GateRef glue, GateRef targ inputs.insert(inputs.end(), args.begin(), args.end()); auto numValuesIn = args.size() + 2; // 2: target & glue if (GetCircuit()->IsOptimizedJSFunctionFrame() && hirGate != Circuit::NullGate()) { + AppendFrameArgs(inputs, hirGate); + numValuesIn += 1; + GateRef pcOffset = Int64(acc_.TryGetPcOffset(hirGate)); inputs.emplace_back(pcOffset); numValuesIn += 1; @@ -1016,9 +1189,11 @@ GateRef CircuitBuilder::Call(const CallSignature* cs, GateRef glue, GateRef targ } else if (cs->IsRuntimeNGCStub()) { meta = circuit_->NoGcRuntimeCall(numValuesIn); } else if (cs->IsOptimizedStub()) { - meta = circuit_->CallOptimized(numValuesIn); + bool isNoGC = acc_.GetNoGCFlag(hirGate); + meta = circuit_->CallOptimized(numValuesIn, isNoGC); } else if (cs->IsOptimizedFastCallStub()) { - meta = circuit_->FastCallOptimized(numValuesIn); + bool isNoGC = acc_.GetNoGCFlag(hirGate); + meta = circuit_->FastCallOptimized(numValuesIn, isNoGC); } else { LOG_ECMA(FATAL) << "unknown call operator"; UNREACHABLE(); @@ -1031,6 +1206,19 @@ GateRef CircuitBuilder::Call(const CallSignature* cs, GateRef glue, GateRef targ return result; } +GateRef CircuitBuilder::StoreMemory(MemoryType Op, VariableType type, GateRef receiver, GateRef index, GateRef value) +{ + auto opIdx = static_cast(Op); + auto currentLabel = env_->GetCurrentLabel(); + auto currentControl = currentLabel->GetControl(); + auto currentDepend = currentLabel->GetDepend(); + auto ret = GetCircuit()->NewGate(GetCircuit()->StoreMemory(opIdx), type.GetMachineType(), + {currentControl, currentDepend, receiver, index, value}, type.GetGateType()); + currentLabel->SetControl(ret); + currentLabel->SetDepend(ret); + return ret; +} + GateRef CircuitBuilder::NoLabelCallRuntime(GateRef glue, GateRef depend, size_t index, std::vector &args, GateRef hirGate) { @@ -1040,6 +1228,8 @@ GateRef CircuitBuilder::NoLabelCallRuntime(GateRef glue, GateRef depend, size_t std::vector inputs { depend, target, glue }; inputs.insert(inputs.end(), args.begin(), args.end()); auto numValuesIn = args.size() + 2; // 2: target & glue + inputs.emplace_back(IntPtr(0)); // framestate slot + numValuesIn += 1; GateRef pcOffset = Int64(acc_.TryGetPcOffset(hirGate)); inputs.emplace_back(pcOffset); numValuesIn += 1; @@ -1136,6 +1326,18 @@ GateRef CircuitBuilder::LoadArrayLength(GateRef array) return ret; } +GateRef CircuitBuilder::LoadStringLength(GateRef string) +{ + auto currentLabel = env_->GetCurrentLabel(); + auto currentControl = currentLabel->GetControl(); + auto currentDepend = currentLabel->GetDepend(); + auto ret = GetCircuit()->NewGate(circuit_->LoadStringLength(), MachineType::I64, + { currentControl, currentDepend, string }, GateType::IntType()); + currentLabel->SetControl(ret); + currentLabel->SetDepend(ret); + return ret; +} + GateRef CircuitBuilder::LoadConstOffset(VariableType type, GateRef receiver, size_t offset) { auto currentLabel = env_->GetCurrentLabel(); @@ -1180,6 +1382,16 @@ GateRef CircuitBuilder::ConvertHoleAsUndefined(GateRef receiver) return ret; } +void CircuitBuilder::AppendFrameArgs(std::vector &args, GateRef hirGate) +{ + GateRef frameArgs = acc_.GetFrameArgs(hirGate); + if (frameArgs == Circuit::NullGate()) { + args.emplace_back(IntPtr(0)); + } else { + args.emplace_back(frameArgs); + } +} + GateRef CircuitBuilder::Construct(GateRef hirGate, std::vector args) { ASSERT(acc_.GetOpCode(hirGate) == OpCode::JS_BYTECODE); @@ -1191,6 +1403,7 @@ GateRef CircuitBuilder::Construct(GateRef hirGate, std::vector args) ASSERT(pcOffset != 0); args.insert(args.begin(), currentDepend); args.insert(args.begin(), currentControl); + AppendFrameArgs(args, hirGate); auto callGate = GetCircuit()->NewGate(circuit_->Construct(bitfield, pcOffset), MachineType::I64, args.size(), args.data(), GateType::AnyType()); currentLabel->SetControl(callGate); @@ -1198,7 +1411,7 @@ GateRef CircuitBuilder::Construct(GateRef hirGate, std::vector args) return callGate; } -GateRef CircuitBuilder::TypedCall(GateRef hirGate, std::vector args) +GateRef CircuitBuilder::TypedCall(GateRef hirGate, std::vector args, bool isNoGC) { ASSERT(acc_.GetOpCode(hirGate) == OpCode::JS_BYTECODE); auto currentLabel = env_->GetCurrentLabel(); @@ -1209,14 +1422,15 @@ GateRef CircuitBuilder::TypedCall(GateRef hirGate, std::vector args) ASSERT(pcOffset != 0); args.insert(args.begin(), currentDepend); args.insert(args.begin(), currentControl); - auto callGate = GetCircuit()->NewGate(circuit_->TypedCall(bitfield, pcOffset), MachineType::I64, + AppendFrameArgs(args, hirGate); + auto callGate = GetCircuit()->NewGate(circuit_->TypedCall(bitfield, pcOffset, isNoGC), MachineType::I64, args.size(), args.data(), GateType::AnyType()); currentLabel->SetControl(callGate); currentLabel->SetDepend(callGate); return callGate; } -GateRef CircuitBuilder::TypedFastCall(GateRef hirGate, std::vector args) +GateRef CircuitBuilder::TypedFastCall(GateRef hirGate, std::vector args, bool isNoGC) { ASSERT(acc_.GetOpCode(hirGate) == OpCode::JS_BYTECODE); auto currentLabel = env_->GetCurrentLabel(); @@ -1227,7 +1441,8 @@ GateRef CircuitBuilder::TypedFastCall(GateRef hirGate, std::vector args ASSERT(pcOffset != 0); args.insert(args.begin(), currentDepend); args.insert(args.begin(), currentControl); - auto callGate = GetCircuit()->NewGate(circuit_->TypedFastCall(bitfield, pcOffset), MachineType::I64, + AppendFrameArgs(args, hirGate); + auto callGate = GetCircuit()->NewGate(circuit_->TypedFastCall(bitfield, pcOffset, isNoGC), MachineType::I64, args.size(), args.data(), GateType::AnyType()); currentLabel->SetControl(callGate); currentLabel->SetDepend(callGate); @@ -1244,9 +1459,12 @@ GateRef CircuitBuilder::CallGetter(GateRef hirGate, GateRef receiver, GateRef pr auto currentLabel = env_->GetCurrentLabel(); auto currentControl = currentLabel->GetControl(); auto currentDepend = currentLabel->GetDepend(); + std::vector args = { currentControl, currentDepend, receiver, propertyLookupResult }; + AppendFrameArgs(args, hirGate); auto callGate = GetCircuit()->NewGate(circuit_->CallGetter(pcOffset), MachineType::I64, - { currentControl, currentDepend, receiver, propertyLookupResult }, + args.size(), + args.data(), GateType::AnyType(), comment); currentLabel->SetControl(callGate); @@ -1264,9 +1482,12 @@ GateRef CircuitBuilder::CallSetter(GateRef hirGate, GateRef receiver, GateRef pr auto currentLabel = env_->GetCurrentLabel(); auto currentControl = currentLabel->GetControl(); auto currentDepend = currentLabel->GetDepend(); + std::vector args = { currentControl, currentDepend, receiver, propertyLookupResult, value }; + AppendFrameArgs(args, hirGate); auto callGate = GetCircuit()->NewGate(circuit_->CallSetter(pcOffset), MachineType::I64, - { currentControl, currentDepend, receiver, propertyLookupResult, value }, + args.size(), + args.data(), GateType::AnyType(), comment); currentLabel->SetControl(callGate); @@ -1296,6 +1517,17 @@ GateRef CircuitBuilder::GetGlobalEnv() return newGate; } +GateRef CircuitBuilder::GetGlobalEnvObj(GateRef env, size_t index) +{ + auto currentLabel = env_->GetCurrentLabel(); + auto currentDepend = currentLabel->GetDepend(); + auto newGate = GetCircuit()->NewGate(circuit_->GetGlobalEnvObj(index), MachineType::I64, + { currentDepend, env }, + GateType::AnyType()); + currentLabel->SetDepend(newGate); + return newGate; +} + GateRef CircuitBuilder::GetGlobalEnvObjHClass(GateRef env, size_t index) { auto currentLabel = env_->GetCurrentLabel(); @@ -1343,6 +1575,26 @@ GateRef CircuitBuilder::TaggedIsString(GateRef obj) return ret; } +GateRef CircuitBuilder::TaggedIsSymbol(GateRef obj) +{ + Label entry(env_); + SubCfgEntry(&entry); + Label exit(env_); + DEFVAlUE(result, env_, VariableType::BOOL(), False()); + Label isHeapObject(env_); + Branch(TaggedIsHeapObject(obj), &isHeapObject, &exit); + Bind(&isHeapObject); + { + GateRef objType = GetObjectType(LoadHClass(obj)); + result = Equal(objType, Int32(static_cast(JSType::SYMBOL))); + Jump(&exit); + } + Bind(&exit); + auto ret = *result; + SubCfgExit(); + return ret; +} + GateRef CircuitBuilder::TaggedIsStringOrSymbol(GateRef obj) { Label entry(env_); @@ -1393,8 +1645,9 @@ GateRef CircuitBuilder::GetMethodFromFunction(GateRef function) GateRef CircuitBuilder::GetModuleFromFunction(GateRef function) { - GateRef offset = IntPtr(JSFunction::ECMA_MODULE_OFFSET); - return Load(VariableType::JS_POINTER(), function, offset); + GateRef method = GetMethodFromFunction(function); + GateRef offset = IntPtr(Method::ECMA_MODULE_OFFSET); + return Load(VariableType::JS_POINTER(), method, offset); } GateRef CircuitBuilder::GetHomeObjectFromFunction(GateRef function) @@ -1406,7 +1659,7 @@ GateRef CircuitBuilder::GetHomeObjectFromFunction(GateRef function) GateRef CircuitBuilder::GetLengthFromString(GateRef value) { GateRef len = Load(VariableType::INT32(), value, IntPtr(EcmaString::MIX_LENGTH_OFFSET)); - return Int32LSR(len, Int32(2)); // 2 : 2 means len must be right shift 2 bits + return Int32LSR(len, Int32(EcmaString::STRING_LENGTH_SHIFT_COUNT)); } GateRef CircuitBuilder::GetConstPoolFromFunction(GateRef jsFunc) @@ -1448,7 +1701,7 @@ GateRef CircuitBuilder::GetObjectFromConstPool(GateRef glue, GateRef hirGate, Ga { constPool, Int32ToTaggedInt(index), module }, hirGate); } else { result = CallRuntime(glue, RTSTUB_ID(GetMethodFromCache), Gate::InvalidGateRef, - { constPool, Int32ToTaggedInt(index) }, hirGate); + { constPool, Int32ToTaggedInt(index), module }, hirGate); } Jump(&exit); } @@ -1460,7 +1713,7 @@ GateRef CircuitBuilder::GetObjectFromConstPool(GateRef glue, GateRef hirGate, Ga Bind(&isInt); { result = CallRuntime(glue, RTSTUB_ID(GetMethodFromCache), Gate::InvalidGateRef, - { constPool, Int32ToTaggedInt(index) }, hirGate); + { constPool, Int32ToTaggedInt(index), module }, hirGate); Jump(&exit); } } else if (type == ConstPoolType::ARRAY_LITERAL) { @@ -1491,13 +1744,38 @@ GateRef CircuitBuilder::GetObjectFromConstPool(GateRef glue, GateRef hirGate, Ga return ret; } -GateRef CircuitBuilder::CreateArray(GateRef obj, bool isEmptyArray) +GateRef CircuitBuilder::ComputeTaggedArraySize(GateRef length) +{ + return PtrAdd(IntPtr(TaggedArray::DATA_OFFSET), + PtrMul(IntPtr(JSTaggedValue::TaggedTypeSize()), length)); +} + +GateRef CircuitBuilder::CreateArray(ElementsKind kind, uint32_t arraySize) +{ + auto currentLabel = env_->GetCurrentLabel(); + auto currentControl = currentLabel->GetControl(); + auto currentDepend = currentLabel->GetDepend(); + ArrayMetaDataAccessor accessor(kind, ArrayMetaDataAccessor::Mode::CREATE, arraySize); + GateRef newGate = GetCircuit()->NewGate(circuit_->CreateArray(accessor.ToValue()), MachineType::I64, + { currentControl, currentDepend }, GateType::TaggedValue()); + currentLabel->SetControl(newGate); + currentLabel->SetDepend(newGate); + return newGate; +} + +GateRef CircuitBuilder::CreateArrayWithBuffer(ElementsKind kind, ArrayMetaDataAccessor::Mode mode, + GateRef constPoolIndex, GateRef elementIndex) { auto currentLabel = env_->GetCurrentLabel(); auto currentControl = currentLabel->GetControl(); auto currentDepend = currentLabel->GetDepend(); - GateRef newGate = GetCircuit()->NewGate(circuit_->CreateArray(isEmptyArray), MachineType::I64, - { currentControl, currentDepend, obj }, GateType::TaggedValue()); + auto frameState = acc_.FindNearestFrameState(currentDepend); + ArrayMetaDataAccessor accessor(kind, mode); + GateRef newGate = GetCircuit()->NewGate(circuit_->CreateArrayWithBuffer(accessor.ToValue()), + MachineType::I64, + { currentControl, currentDepend, constPoolIndex, + elementIndex, frameState }, + GateType::NJSValue()); currentLabel->SetControl(newGate); currentLabel->SetDepend(newGate); return newGate; @@ -1614,12 +1892,6 @@ GateRef CircuitBuilder::GetFunctionLexicalEnv(GateRef function) return Load(VariableType::JS_POINTER(), function, IntPtr(JSFunction::LEXICAL_ENV_OFFSET)); } -void CircuitBuilder::SetModuleToFunction(GateRef glue, GateRef function, GateRef value) -{ - GateRef offset = IntPtr(JSFunction::ECMA_MODULE_OFFSET); - Store(VariableType::JS_POINTER(), glue, function, offset, value); -} - void CircuitBuilder::SetPropertyInlinedProps(GateRef glue, GateRef obj, GateRef hClass, GateRef value, GateRef attrOffset, VariableType type) { @@ -1725,11 +1997,12 @@ void CircuitBuilder::Jump(Label *label) env_->SetCurrentLabel(nullptr); } -void CircuitBuilder::Branch(GateRef condition, Label *trueLabel, Label *falseLabel) +void CircuitBuilder::Branch(GateRef condition, Label *trueLabel, Label *falseLabel, + uint32_t trueWeight, uint32_t falseWeight) { auto currentLabel = env_->GetCurrentLabel(); auto currentControl = currentLabel->GetControl(); - GateRef ifBranch = Branch(currentControl, condition); + GateRef ifBranch = Branch(currentControl, condition, trueWeight, falseWeight); currentLabel->SetControl(ifBranch); GateRef ifTrue = IfTrue(ifBranch); trueLabel->AppendPredecessor(GetCurrentLabel()); @@ -1786,6 +2059,25 @@ void CircuitBuilder::LoopEnd(Label *loopHead) env_->SetCurrentLabel(nullptr); } +// add loop exit info at begin of label (only support not merge label) +void CircuitBuilder::LoopExit(const std::vector &vars, size_t diff) +{ + auto currentLabel = env_->GetCurrentLabel(); + auto loopExit = currentLabel->GetControl(); + auto loopExitDepend = currentLabel->GetDepend(); + std::vector loopExitValues; + for (size_t i = 0; i < diff; ++i) { + loopExit = LoopExit(loopExit); + loopExitDepend = LoopExitDepend(loopExit, loopExitDepend); + for (const auto &var : vars) { + auto loopExitValue = LoopExitValue(loopExit, var->ReadVariable()); + var->WriteVariable(loopExitValue); + } + } + currentLabel->SetControl(loopExit); + currentLabel->SetDepend(loopExitDepend); +} + Label::Label(Environment *env) { impl_ = env->NewLabel(env); @@ -2026,4 +2318,108 @@ void CircuitBuilder::ClearConstantCache(GateRef gate) auto gateType = acc_.GetGateType(gate); GetCircuit()->ClearConstantCache(machineType, value, gateType); } + +GateRef CircuitBuilder::InsertTypedBinaryop(GateRef left, GateRef right, GateType leftType, GateType rightType, + GateType gateType, PGOSampleType sampleType, TypedBinOp op) +{ + auto currentLabel = env_->GetCurrentLabel(); + auto currentControl = currentLabel->GetControl(); + auto currentDepend = currentLabel->GetDepend(); + uint64_t operandTypes = GatePairTypeAccessor::ToValue(leftType, rightType); + auto ret = GetCircuit()->NewGate(circuit_->TypedBinaryOp(operandTypes, op, sampleType), + MachineType::I64, + {currentControl, currentDepend, left, right}, + gateType); + acc_.ReplaceInAfterInsert(currentControl, currentDepend, ret); + currentLabel->SetControl(ret); + currentLabel->SetDepend(ret); + return ret; +} + +GateRef CircuitBuilder::InsertRangeCheckPredicate(GateRef left, TypedBinOp cond, GateRef right) +{ + auto currentLabel = env_->GetCurrentLabel(); + auto currentControl = currentLabel->GetControl(); + auto currentDepend = currentLabel->GetDepend(); + auto frameState = acc_.FindNearestFrameState(currentDepend); + TypedBinaryAccessor accessor(GateType::IntType(), cond); + auto ret = GetCircuit()->NewGate(circuit_->RangeCheckPredicate(accessor.ToValue()), + MachineType::I32, + {currentControl, currentDepend, left, right, frameState}, + GateType::IntType()); + acc_.ReplaceInAfterInsert(currentControl, currentDepend, ret); + currentLabel->SetControl(ret); + currentLabel->SetDepend(ret); + return ret; +} + +GateRef CircuitBuilder::InsertStableArrayCheck(GateRef array) +{ + auto currentLabel = env_->GetCurrentLabel(); + auto currentControl = currentLabel->GetControl(); + auto currentDepend = currentLabel->GetDepend(); + GateRef frameState = acc_.FindNearestFrameState(currentDepend); + ElementsKind kind = acc_.TryGetElementsKind(array); + ArrayMetaDataAccessor::Mode mode = ArrayMetaDataAccessor::Mode::LOAD_LENGTH; + ArrayMetaDataAccessor accessor(kind, mode); + auto ret = GetCircuit()->NewGate(circuit_->StableArrayCheck(accessor.ToValue()), + MachineType::I1, + {currentControl, currentDepend, array, frameState}, + GateType::NJSValue()); + acc_.ReplaceInAfterInsert(currentControl, currentDepend, ret); + currentLabel->SetControl(ret); + currentLabel->SetDepend(ret); + return ret; +} + +GateRef CircuitBuilder::InsertTypedArrayCheck(GateType type, GateRef array) +{ + auto currentLabel = env_->GetCurrentLabel(); + auto currentControl = currentLabel->GetControl(); + auto currentDepend = currentLabel->GetDepend(); + GateRef frameState = acc_.FindNearestFrameState(currentDepend); + auto ret = GetCircuit()->NewGate(circuit_->TypedArrayCheck(static_cast(type.Value())), + MachineType::I1, + {currentControl, currentDepend, array, frameState}, + GateType::NJSValue()); + acc_.ReplaceInAfterInsert(currentControl, currentDepend, ret); + currentLabel->SetControl(ret); + currentLabel->SetDepend(ret); + return ret; +} + +GateRef CircuitBuilder::InsertLoadArrayLength(GateRef array, bool isTypedArray) +{ + auto currentLabel = env_->GetCurrentLabel(); + auto currentControl = currentLabel->GetControl(); + auto currentDepend = currentLabel->GetDepend(); + GateType arrayType = acc_.GetGateType(array); + if (isTypedArray) { + InsertTypedArrayCheck(arrayType, array); + currentControl = currentLabel->GetControl(); + currentDepend = currentLabel->GetDepend(); + auto ret = GetCircuit()->NewGate(circuit_->LoadTypedArrayLength(static_cast(arrayType.Value())), + MachineType::I64, + { currentControl, currentDepend, array }, + GateType::IntType()); + acc_.ReplaceInAfterInsert(currentControl, currentDepend, ret); + currentLabel->SetControl(ret); + currentLabel->SetDepend(ret); + return ret; + } else { + InsertStableArrayCheck(array); + currentControl = currentLabel->GetControl(); + currentDepend = currentLabel->GetDepend(); + auto ret = GetCircuit()->NewGate(circuit_->LoadArrayLength(), + MachineType::I64, + { currentControl, currentDepend, array }, + GateType::IntType()); + acc_.ReplaceInAfterInsert(currentControl, currentDepend, ret); + currentLabel->SetControl(ret); + currentLabel->SetDepend(ret); + return ret; + } + UNREACHABLE(); + return Circuit::NullGate(); +} } // namespace panda::ecmascript::kungfu diff --git a/ecmascript/compiler/circuit_builder.h b/ecmascript/compiler/circuit_builder.h index 7ee2bc6af222ebd35e2455618afdafffdaa3ff43..d429f9877ff357072440fc71ed6486eaf199cf20 100644 --- a/ecmascript/compiler/circuit_builder.h +++ b/ecmascript/compiler/circuit_builder.h @@ -40,6 +40,10 @@ class Environment; class Label; class Variable; class StubBuilder; +class BuiltinsStringStubBuilder; +class TSHCRLowering; +class NTypeHCRLowering; +class SlowPathLowering; #define BINARY_ARITHMETIC_METHOD_LIST_WITH_BITWIDTH(V) \ V(Int16Add, Add, MachineType::I16) \ @@ -106,6 +110,7 @@ class StubBuilder; V(TruncInt64ToInt1, Trunc, MachineType::I1) \ V(TruncInt64ToInt16, Trunc, MachineType::I16) \ V(TruncInt32ToInt1, Trunc, MachineType::I1) \ + V(TruncInt32ToInt8, Trunc, MachineType::I8) \ V(TruncInt32ToInt16, Trunc, MachineType::I16) \ V(TruncFloatToInt64, TruncFloatToInt64, MachineType::I64) \ V(TruncFloatToInt32, TruncFloatToInt32, MachineType::I32) \ @@ -243,24 +248,35 @@ public: ~CircuitBuilder() = default; NO_MOVE_SEMANTIC(CircuitBuilder); NO_COPY_SEMANTIC(CircuitBuilder); + // low level interface GateRef HeapObjectCheck(GateRef gate, GateRef frameState); - GateRef StableArrayCheck(GateRef gate); - GateRef HClassStableArrayCheck(GateRef gate, GateRef frameState); + GateRef StableArrayCheck(GateRef gate, ElementsKind kind, ArrayMetaDataAccessor::Mode mode); + GateRef COWArrayCheck(GateRef gate); + GateRef EcmaStringCheck(GateRef gate); + GateRef FlattenTreeStringCheck(GateRef gate); + GateRef HClassStableArrayCheck(GateRef gate, GateRef frameState, ArrayMetaDataAccessor accessor); GateRef ArrayGuardianCheck(GateRef frameState); GateRef TypedArrayCheck(GateType type, GateRef gate); + GateRef LoadTypedArrayLength(GateType type, GateRef gate); + GateRef RangeGuard(GateRef gate, uint32_t left, uint32_t right); GateRef IndexCheck(GateType type, GateRef gate, GateRef index); - GateRef ObjectTypeCheck(GateType type, GateRef gate, GateRef hclassOffset); + GateRef ObjectTypeCheck(GateType type, bool isHeapObject, GateRef gate, GateRef hclassIndex); + GateRef ObjectTypeCompare(GateType type, bool isHeapObject, GateRef gate, GateRef hclassIndex); GateRef TryPrimitiveTypeCheck(GateType type, GateRef gate); - GateRef CallTargetCheck(GateRef function, GateRef id, GateRef param, const char* comment = nullptr); - GateRef JSCallTargetFromDefineFuncCheck(GateType type, GateRef func); - GateRef JSCallTargetTypeCheck(GateType type, GateRef func, GateRef methodIndex); - GateRef JSFastCallTargetTypeCheck(GateType type, GateRef func, GateRef methodIndex); - GateRef JSCallThisTargetTypeCheck(GateType type, GateRef func); - GateRef JSFastCallThisTargetTypeCheck(GateType type, GateRef func); + GateRef CallTargetCheck(GateRef gate, GateRef function, GateRef id, GateRef param, const char* comment = nullptr); + GateRef JSCallTargetFromDefineFuncCheck(GateType type, GateRef func, GateRef gate); + template + GateRef JSCallTargetTypeCheck(GateType type, GateRef func, GateRef methodIndex, GateRef gate); + template + GateRef JSCallThisTargetTypeCheck(GateType type, GateRef func, GateRef gate); + template + inline GateRef JSNoGCCallThisTargetTypeCheck(GateType type, GateRef func, GateRef methodId, GateRef gate); GateRef DeoptCheck(GateRef condition, GateRef frameState, DeoptType type); - GateRef TypedCallOperator(GateRef hirGate, MachineType type, const std::initializer_list& args); - inline GateRef TypedCallBuiltin(GateRef hirGate, GateRef x, BuiltinsStubCSigns::ID id); + GateRef TypeOfCheck(GateRef gate, GateType type); + GateRef TypedTypeOf(GateType type); + GateRef TypedCallOperator(GateRef hirGate, MachineType type, const std::vector& inList); + inline GateRef TypedCallBuiltin(GateRef hirGate, const std::vector &args, BuiltinsStubCSigns::ID id); GateRef TypeConvert(MachineType type, GateType typeFrom, GateType typeTo, const std::vector& inList); GateRef AddWithOverflow(GateRef left, GateRef right); GateRef SubWithOverflow(GateRef left, GateRef right); @@ -270,15 +286,15 @@ public: GateRef Int32CheckRightIsZero(GateRef right); GateRef Float64CheckRightIsZero(GateRef right); GateRef ValueCheckNegOverflow(GateRef value); - GateRef NegativeIndexCheck(GateRef index); - GateRef LargeIndexCheck(GateRef index, GateRef length); GateRef OverflowCheck(GateRef value); + GateRef LexVarIsHoleCheck(GateRef value); GateRef Int32UnsignedUpperBoundCheck(GateRef value, GateRef upperBound); GateRef Int32DivWithCheck(GateRef left, GateRef right); MachineType GetMachineTypeOfValueType(ValueType type); GateType GetGateTypeOfValueType(ValueType type); GateRef IsJsCOWArray(GateRef obj); GateRef IsCOWArray(GateRef objectType); + GateRef IsTaggedArray(GateRef object); GateRef GetElementsArray(GateRef object); GateRef Convert(GateRef gate, ValueType src, ValueType dst); GateRef ConvertBoolToTaggedBoolean(GateRef gate); @@ -289,8 +305,16 @@ public: GateRef ConvertFloat64ToTaggedDouble(GateRef gate); GateRef ConvertFloat64ToInt32(GateRef gate); GateRef ConvertInt32ToFloat64(GateRef gate); - GateRef CheckAndConvert(GateRef gate, ValueType src, ValueType dst); + GateRef ConvertBoolToInt32(GateRef gate, ConvertSupport support); + GateRef ConvertBoolToFloat64(GateRef gate, ConvertSupport support); + GateRef ConvertUInt32ToBool(GateRef gate); + GateRef ConvertUInt32ToTaggedNumber(GateRef gate); + GateRef ConvertUInt32ToFloat64(GateRef gate); + GateRef ConvertCharToEcmaString(GateRef gate); + GateRef CheckAndConvert( + GateRef gate, ValueType src, ValueType dst, ConvertSupport support = ConvertSupport::ENABLE); GateRef ConvertHoleAsUndefined(GateRef receiver); + GateRef CheckUInt32AndConvertToInt32(GateRef gate); GateRef CheckTaggedIntAndConvertToInt32(GateRef gate); GateRef CheckTaggedDoubleAndConvertToInt32(GateRef gate); GateRef CheckTaggedNumberAndConvertToInt32(GateRef gate); @@ -299,7 +323,22 @@ public: GateRef CheckTaggedNumberAndConvertToFloat64(GateRef gate); GateRef CheckTaggedNumberAndConvertToBool(GateRef gate); GateRef CheckTaggedBooleanAndConvertToBool(GateRef gate); - GateRef TypedConditionJump(MachineType type, TypedJumpOp jumpOp, GateType typeVal, + GateRef CheckNullAndConvertToInt32(GateRef gate); + GateRef CheckTaggedBooleanAndConvertToInt32(GateRef gate); + GateRef CheckNullAndConvertToFloat64(GateRef gate); + GateRef CheckTaggedBooleanAndConvertToFloat64(GateRef gate); + GateRef CheckUndefinedAndConvertToFloat64(GateRef gate); + GateRef CheckUndefinedAndConvertToBool(GateRef gate); + GateRef CheckNullAndConvertToBool(GateRef gate); + GateRef CheckUndefinedAndConvertToInt32(GateRef gate); + GateRef InsertStableArrayCheck(GateRef array); + GateRef InsertLoadArrayLength(GateRef array, bool isTypedArray); + GateRef InsertTypedArrayCheck(GateType type, GateRef array); + GateRef InsertTypedBinaryop(GateRef left, GateRef right, GateType leftType, GateType rightType, + GateType gateType, PGOSampleType sampleType, TypedBinOp op); + GateRef InsertRangeCheckPredicate(GateRef left, TypedBinOp cond, GateRef right); + void AppendFrameArgs(std::vector &args, GateRef hirGate); + GateRef TypedConditionJump(MachineType type, TypedJumpOp jumpOp, uint32_t weight, GateType typeVal, const std::vector& inList); GateRef TypedNewAllocateThis(GateRef ctor, GateRef hclassIndex, GateRef frameState); GateRef TypedSuperAllocateThis(GateRef superCtor, GateRef newTarget, GateRef frameState); @@ -315,7 +354,7 @@ public: GateRef Int32(int32_t value); GateRef Int64(int64_t value); GateRef IntPtr(int64_t val); - GateRef StringPtr(const std::string &str); + GateRef StringPtr(std::string_view str); GateRef Boolean(bool value); GateRef Double(double value); GateRef UndefineConstant(); @@ -323,23 +362,29 @@ public: GateRef NullPtrConstant(); GateRef NullConstant(); GateRef ExceptionConstant(); + GateRef NanValue(); GateRef RelocatableData(uint64_t val); GateRef Alloca(size_t size); - GateRef Branch(GateRef state, GateRef condition); + GateRef Branch(GateRef state, GateRef condition, uint32_t leftWeight = 1, uint32_t rightWeight = 1, + const char* comment = nullptr); // 1: default branch weight GateRef SwitchBranch(GateRef state, GateRef index, int caseCounts); GateRef Return(GateRef state, GateRef depend, GateRef value); GateRef ReturnVoid(GateRef state, GateRef depend); GateRef Goto(GateRef state); GateRef LoopBegin(GateRef state); GateRef LoopEnd(GateRef state); + GateRef LoopExit(GateRef state); + GateRef LoopExitDepend(GateRef state, GateRef depend); + GateRef LoopExitValue(GateRef state, GateRef value); GateRef IfTrue(GateRef ifBranch); GateRef IfFalse(GateRef ifBranch); GateRef SwitchCase(GateRef switchBranch, int64_t value); GateRef DefaultCase(GateRef switchBranch); GateRef DependRelay(GateRef state, GateRef depend); - GateRef BinaryArithmetic(const GateMetaData* meta, MachineType machineType, - GateRef left, GateRef right, GateType gateType = GateType::Empty()); - GateRef BinaryCmp(const GateMetaData* meta, GateRef left, GateRef right); + GateRef ReadSp(); + GateRef BinaryArithmetic(const GateMetaData* meta, MachineType machineType, GateRef left, + GateRef right, GateType gateType = GateType::Empty(), const char* comment = nullptr); + GateRef BinaryCmp(const GateMetaData* meta, GateRef left, GateRef right, const char* comment = nullptr); static MachineType GetMachineTypeFromVariableType(VariableType type); GateRef GetCallBuiltinId(GateRef method); Circuit *GetCircuit() const @@ -350,6 +395,7 @@ public: inline GateRef True(); inline GateRef False(); inline GateRef Undefined(); + inline GateRef Hole(); // call operation GateRef CallBCHandler(GateRef glue, GateRef target, const std::vector &args, @@ -389,35 +435,35 @@ public: void Store(VariableType type, GateRef glue, GateRef base, GateRef offset, GateRef value); void StoreWithNoBarrier(VariableType type, GateRef base, GateRef offset, GateRef value); -#define ARITHMETIC_BINARY_OP_WITH_BITWIDTH(NAME, OPCODEID, MACHINETYPEID) \ - inline GateRef NAME(GateRef x, GateRef y, GateType type = GateType::Empty()) \ - { \ - return BinaryArithmetic(circuit_->OPCODEID(), MACHINETYPEID, x, y, type); \ +#define ARITHMETIC_BINARY_OP_WITH_BITWIDTH(NAME, OPCODEID, MACHINETYPEID) \ + inline GateRef NAME(GateRef x, GateRef y, GateType type = GateType::Empty(), const char* comment = nullptr) \ + { \ + return BinaryArithmetic(circuit_->OPCODEID(), MACHINETYPEID, x, y, type, comment); \ } BINARY_ARITHMETIC_METHOD_LIST_WITH_BITWIDTH(ARITHMETIC_BINARY_OP_WITH_BITWIDTH) #undef ARITHMETIC_BINARY_OP_WITH_BITWIDTH -#define ARITHMETIC_UNARY_OP_WITH_BITWIDTH(NAME, OPCODEID, MACHINETYPEID) \ - inline GateRef NAME(GateRef x) \ - { \ - return circuit_->NewGate(circuit_->OPCODEID(), MACHINETYPEID, { x }, GateType::NJSValue()); \ +#define ARITHMETIC_UNARY_OP_WITH_BITWIDTH(NAME, OPCODEID, MACHINETYPEID) \ + inline GateRef NAME(GateRef x, const char* comment = nullptr) \ + { \ + return circuit_->NewGate(circuit_->OPCODEID(), MACHINETYPEID, { x }, GateType::NJSValue(), comment); \ } UNARY_ARITHMETIC_METHOD_LIST_WITH_BITWIDTH(ARITHMETIC_UNARY_OP_WITH_BITWIDTH) #undef ARITHMETIC_UNARY_OP_WITH_BITWIDTH -#define CMP_BINARY_OP_WITHOUT_BITWIDTH(NAME, OPCODEID, CONDITION) \ - inline GateRef NAME(GateRef x, GateRef y) \ - { \ - return BinaryCmp(circuit_->OPCODEID(static_cast(CONDITION)), x, y); \ +#define CMP_BINARY_OP_WITHOUT_BITWIDTH(NAME, OPCODEID, CONDITION) \ + inline GateRef NAME(GateRef x, GateRef y, const char* comment = nullptr) \ + { \ + return BinaryCmp(circuit_->OPCODEID(static_cast(CONDITION)), x, y, comment); \ } BINARY_CMP_METHOD_LIST_WITHOUT_BITWIDTH(CMP_BINARY_OP_WITHOUT_BITWIDTH) #undef CMP_BINARY_OP_WITHOUT_BITWIDTH - inline GateRef Equal(GateRef x, GateRef y); - inline GateRef NotEqual(GateRef x, GateRef y); + inline GateRef Equal(GateRef x, GateRef y, const char* comment = nullptr); + inline GateRef NotEqual(GateRef x, GateRef y, const char* comment = nullptr); // js world // cast operation @@ -448,6 +494,7 @@ public: inline GateRef TaggedIsAsyncGeneratorObject(GateRef x); inline GateRef TaggedIsJSGlobalObject(GateRef x); inline GateRef TaggedIsGeneratorObject(GateRef x); + inline GateRef TaggedIsJSArray(GateRef x); inline GateRef TaggedIsPropertyBox(GateRef x); inline GateRef TaggedIsWeak(GateRef x); inline GateRef TaggedIsPrototypeHandler(GateRef x); @@ -465,6 +512,8 @@ public: inline GateRef ToTaggedIntPtr(GateRef x); inline GateRef DoubleToTaggedDoublePtr(GateRef x); inline GateRef BooleanToTaggedBooleanPtr(GateRef x); + inline GateRef BooleanToInt32(GateRef x); + inline GateRef BooleanToFloat64(GateRef x); inline GateRef Float32ToTaggedDoublePtr(GateRef x); inline GateRef TaggedDoublePtrToFloat32(GateRef x); inline GateRef TaggedIntPtrToFloat32(GateRef x); @@ -486,13 +535,18 @@ public: inline GateRef IntPtrGreaterThan(GateRef x, GateRef y); template inline GateRef BinaryOp(GateRef x, GateRef y); + template + inline GateRef BinaryOpWithOverflow(GateRef x, GateRef y); inline GateRef GetLengthFromTaggedArray(GateRef array); inline GateRef GetValueFromTaggedArray(GateRef array, GateRef index); inline void SetValueToTaggedArray(VariableType valType, GateRef glue, GateRef array, GateRef index, GateRef val); GateRef TaggedIsString(GateRef obj); GateRef TaggedIsStringOrSymbol(GateRef obj); + GateRef TaggedIsSymbol(GateRef obj); inline GateRef GetGlobalConstantString(ConstantIndex index); inline GateRef LoadObjectFromWeakRef(GateRef x); + inline GateRef CreateWeakRef(GateRef x); + GateRef ComputeTaggedArraySize(GateRef length); GateRef IsJSHClass(GateRef obj); GateRef HasPendingException(GateRef glue); @@ -503,7 +557,7 @@ public: template inline GateRef TypedUnaryOp(GateRef x, GateType xType, GateType gateType); template - inline GateRef TypedConditionJump(GateRef x, GateType xType); + inline GateRef TypedConditionJump(GateRef x, GateType xType, uint32_t weight); inline GateRef PrimitiveToNumber(GateRef x, VariableType type); // middle ir: object operations @@ -512,26 +566,31 @@ public: GateRef LoadElement(GateRef receiver, GateRef index); template GateRef StoreElement(GateRef receiver, GateRef index, GateRef value); + GateRef StoreMemory(MemoryType Op, VariableType type, GateRef receiver, GateRef index, GateRef value); GateRef LoadProperty(GateRef receiver, GateRef propertyLookupResult, bool isFunction); GateRef StoreProperty(GateRef receiver, GateRef propertyLookupResult, GateRef value); GateRef LoadArrayLength(GateRef array); + GateRef LoadStringLength(GateRef string); GateRef Construct(GateRef hirGate, std::vector args); - GateRef TypedCall(GateRef hirGate, std::vector args); - GateRef TypedFastCall(GateRef hirGate, std::vector args); + GateRef TypedCall(GateRef hirGate, std::vector args, bool isNoGC); + GateRef TypedFastCall(GateRef hirGate, std::vector args, bool isNoGC); GateRef CallGetter(GateRef hirGate, GateRef receiver, GateRef propertyLookupResult, const char* comment = nullptr); GateRef CallSetter(GateRef hirGate, GateRef receiver, GateRef propertyLookupResult, GateRef value, const char* comment = nullptr); GateRef GetConstPool(GateRef jsFunc); GateRef GetGlobalEnv(); + GateRef GetGlobalEnvObj(GateRef env, size_t index); GateRef GetGlobalEnvObjHClass(GateRef env, size_t index); GateRef GetGlobalConstantValue(ConstantIndex index); GateRef LoadConstOffset(VariableType type, GateRef receiver, size_t offset); GateRef StoreConstOffset(VariableType type, GateRef receiver, size_t offset, GateRef value); GateRef LoadObjectFromConstPool(GateRef jsFunc, GateRef index); + GateRef StringEqual(GateRef x, GateRef y); // Object Operations inline GateRef LoadHClass(GateRef object); inline GateRef IsJSFunction(GateRef obj); inline GateRef IsJSFunctionWithBit(GateRef obj); + inline GateRef IsOptimizedAndNotFastCall(GateRef obj); inline GateRef IsOptimized(GateRef obj); inline GateRef IsOptimizedWithBitField(GateRef bitfield); inline GateRef CanFastCall(GateRef obj); @@ -541,7 +600,8 @@ public: inline GateRef IsJsType(GateRef object, JSType type); inline GateRef GetObjectType(GateRef hClass); inline GateRef IsDictionaryModeByHClass(GateRef hClass); - inline GateRef IsIsStableElementsByHClass(GateRef hClass); + inline GateRef GetElementsKindByHClass(GateRef hClass); + inline GateRef HasConstructorByHClass(GateRef hClass); inline GateRef IsStableElements(GateRef hClass); inline GateRef IsStableArguments(GateRef hClass); inline GateRef IsStableArray(GateRef hClass); @@ -549,8 +609,10 @@ public: inline GateRef IsDictionaryElement(GateRef hClass); inline GateRef IsClassConstructor(GateRef object); inline GateRef IsClassConstructorWithBitField(GateRef bitfield); + inline GateRef HasConstructor(GateRef object); inline GateRef IsConstructor(GateRef object); inline GateRef IsClassPrototype(GateRef object); + inline GateRef IsClassPrototypeWithBitField(GateRef object); inline GateRef IsExtensible(GateRef object); inline GateRef GetExpectedNumOfArgs(GateRef method); inline GateRef TaggedObjectIsEcmaObject(GateRef obj); @@ -563,6 +625,11 @@ public: inline GateRef LogicOr(GateRef x, GateRef y); inline GateRef BothAreString(GateRef x, GateRef y); inline GateRef GetObjectSizeFromHClass(GateRef hClass); + inline GateRef IsTreeString(GateRef obj); + inline GateRef IsSlicedString(GateRef obj); + inline GateRef TreeStringIsFlat(GateRef string); + inline GateRef GetFirstFromTreeString(GateRef string); + inline GateRef GetSecondFromTreeString(GateRef string); GateRef GetGlobalObject(GateRef glue); GateRef GetMethodFromFunction(GateRef function); GateRef GetModuleFromFunction(GateRef function); @@ -575,7 +642,6 @@ public: GateRef TaggedIsBigInt(GateRef obj); void SetLexicalEnvToFunction(GateRef glue, GateRef function, GateRef value); GateRef GetFunctionLexicalEnv(GateRef function); - void SetModuleToFunction(GateRef glue, GateRef function, GateRef value); void SetPropertyInlinedProps(GateRef glue, GateRef obj, GateRef hClass, GateRef value, GateRef attrOffset, VariableType type); void SetHomeObjectToFunction(GateRef glue, GateRef function, GateRef value); @@ -590,7 +656,9 @@ public: GateRef StartAllocate(); GateRef FinishAllocate(); GateRef HeapAlloc(GateRef size, GateType type, RegionSpaceFlag flag); - GateRef CreateArray(GateRef obj, bool isEmptyArray); + GateRef CreateArray(ElementsKind kind, uint32_t arraySize); + GateRef CreateArrayWithBuffer(ElementsKind kind, ArrayMetaDataAccessor::Mode mode, + GateRef constPoolIndex, GateRef elementIndex); void SetEnvironment(Environment *env) { @@ -621,26 +689,33 @@ public: inline void Bind(Label *label); inline void Bind(Label *label, bool justSlowPath); void Jump(Label *label); - void Branch(GateRef condition, Label *trueLabel, Label *falseLabel); + void Branch(GateRef condition, Label *trueLabel, Label *falseLabel, + uint32_t trueWeight = 1, uint32_t falseWeight = 1); // 1: default branch weight void Switch(GateRef index, Label *defaultLabel, int64_t *keysValue, Label *keysLabel, int numberOfKeys); void LoopBegin(Label *loopHead); void LoopEnd(Label *loopHead); + void LoopExit(const std::vector &vars, size_t diff = 1); inline Label *GetCurrentLabel() const; inline GateRef GetState() const; inline GateRef GetDepend() const; inline StateDepend GetStateDepend() const; - inline void SetDepend(GateRef depend); - inline void SetState(GateRef state); GateRef GetGlobalEnvValue(VariableType type, GateRef env, size_t index); GateRef IsBase(GateRef ctor); inline GateRef GetMethodId(GateRef func); + inline GateRef LoadFromTaggedArray(GateRef array, size_t index); + inline GateRef StoreToTaggedArray(GateRef array, size_t index, GateRef value); private: -#define ARITHMETIC_UNARY_OP_WITH_BITWIDTH(NAME, OPCODEID, MACHINETYPEID) \ - inline GateRef NAME(GateRef x) \ - { \ - return circuit_->NewGate(circuit_->OPCODEID(), MACHINETYPEID, { x }, GateType::NJSValue()); \ + static constexpr uint32_t GATE_TWO_VALUESIN = 2; + + inline void SetDepend(GateRef depend); + inline void SetState(GateRef state); + +#define ARITHMETIC_UNARY_OP_WITH_BITWIDTH(NAME, OPCODEID, MACHINETYPEID) \ + inline GateRef NAME(GateRef x, const char* comment = nullptr) \ + { \ + return circuit_->NewGate(circuit_->OPCODEID(), MACHINETYPEID, { x }, GateType::NJSValue(), comment); \ } UNARY_ARITHMETIC_METHOD_LIST_WITH_BITWIDTH_PRIVATE(ARITHMETIC_UNARY_OP_WITH_BITWIDTH) @@ -651,6 +726,10 @@ private: Environment *env_ {nullptr}; CompilationConfig *cmpCfg_ {nullptr}; friend StubBuilder; + friend BuiltinsStringStubBuilder; + friend TSHCRLowering; + friend NTypeHCRLowering; + friend SlowPathLowering; }; class Label { @@ -715,7 +794,7 @@ private: } void MergeControl(GateRef control) { - if (predeControl_ == -1) { + if (predeControl_ == Circuit::NullGate()) { predeControl_ = control; control_ = predeControl_; } else { @@ -746,9 +825,9 @@ private: GateRef ReadVariableRecursive(Variable *var); Environment *env_; GateRef control_; - GateRef predeControl_ {-1}; - GateRef depend_ {-1}; - GateRef loopDepend_ {-1}; + GateRef predeControl_ {Circuit::NullGate()}; + GateRef depend_ {Circuit::NullGate()}; + GateRef loopDepend_ {Circuit::NullGate()}; std::vector otherPredeControls_; bool isSealed_ {false}; std::map valueMap_; diff --git a/ecmascript/compiler/code_generator.h b/ecmascript/compiler/code_generator.h index 114e020bf8299f57368e0ec2a767718645aa4bc5..f955f82f1da11dfea3dc9f6a09b85ab5c1a051ee 100644 --- a/ecmascript/compiler/code_generator.h +++ b/ecmascript/compiler/code_generator.h @@ -34,7 +34,7 @@ public: virtual void GenerateCode(Circuit *circuit, const ControlFlowGraph &graph, const CompilationConfig *cfg, const MethodLiteral *methodLiteral, const JSPandaFile *jsPandaFile, - const std::string &methodName) = 0; + const std::string &methodName, bool enableOptInlining) = 0; }; class CodeGenerator { @@ -57,9 +57,9 @@ public: } void Run(Circuit *circuit, const ControlFlowGraph &graph, const CompilationConfig *cfg, - const MethodLiteral *methodLiteral, const JSPandaFile *jsPandaFile) + const MethodLiteral *methodLiteral, const JSPandaFile *jsPandaFile, bool enableOptInlining) { - impl_->GenerateCode(circuit, graph, cfg, methodLiteral, jsPandaFile, methodName_); + impl_->GenerateCode(circuit, graph, cfg, methodLiteral, jsPandaFile, methodName_, enableOptInlining); } private: diff --git a/ecmascript/compiler/graph_visitor.cpp b/ecmascript/compiler/combined_pass_visitor.cpp similarity index 54% rename from ecmascript/compiler/graph_visitor.cpp rename to ecmascript/compiler/combined_pass_visitor.cpp index 8f77b73baadd36d84f11207ea5a893a81f00114b..722e62c83c78630b3da6874d90b6a23671210eb2 100644 --- a/ecmascript/compiler/graph_visitor.cpp +++ b/ecmascript/compiler/combined_pass_visitor.cpp @@ -13,28 +13,54 @@ * limitations under the License. */ -#include -#include -#include "ecmascript/compiler/graph_visitor.h" +#include "ecmascript/compiler/combined_pass_visitor.h" namespace panda::ecmascript::kungfu { -void GraphVisitor::ReplaceGate(GateRef gate, GateRef replacement) +int32_t CombinedPassVisitor::GetGateOrder(GateRef gate) +{ + auto id = acc_.GetId(gate); + if (id >= orderList_.size()) { + Resize(id + 1, -1); + } + return orderList_[acc_.GetId(gate)]; +} + +void CombinedPassVisitor::SetGateOrder(GateRef gate, int32_t orderId) +{ + auto id = acc_.GetId(gate); + if (id >= orderList_.size()) { + Resize(id + 1, -1); + } + orderList_[acc_.GetId(gate)] = orderId; +} + +void CombinedPassVisitor::Resize(int32_t size, int32_t num) +{ + orderList_.resize(size, num); +} + +void CombinedPassVisitor::AddPass(PassVisitor* pass) +{ + passList_.emplace_back(pass); +} + +void CombinedPassVisitor::ReplaceGate(GateRef gate, GateRef replacement) { GateRef depend = Circuit::NullGate(); if (acc_.GetDependCount(gate) > 0) { - ASSERT(acc_.GetDependCount(gate) == 1); // 1: one dep + ASSERT(acc_.GetDependCount(gate) == 1 || acc_.GetOpCode(replacement) == OpCode::DEAD); // 1: one dep depend = acc_.GetDep(gate); } GateRef state = Circuit::NullGate(); if (acc_.GetStateCount(gate) > 0) { - ASSERT(acc_.GetStateCount(gate) == 1); // 1: one state + ASSERT(acc_.GetStateCount(gate) == 1 || acc_.GetOpCode(replacement) == OpCode::DEAD); // 1: one state state = acc_.GetState(gate); } return ReplaceGate(gate, StateDepend {state, depend}, replacement); } -void GraphVisitor::ReplaceGate(GateRef gate, StateDepend stateDepend, GateRef replacement) +void CombinedPassVisitor::ReplaceGate(GateRef gate, StateDepend stateDepend, GateRef replacement) { ASSERT(gate != replacement); auto state = stateDepend.State(); @@ -44,7 +70,9 @@ void GraphVisitor::ReplaceGate(GateRef gate, StateDepend stateDepend, GateRef re if (acc_.GetMark(*it) == MarkCode::FINISHED) { PushChangedGate(*it); } - if (acc_.IsStateIn(it)) { + if (acc_.GetOpCode(replacement) == OpCode::DEAD) { + it = acc_.ReplaceIn(it, replacement); + } else if (acc_.IsStateIn(it)) { ASSERT(state != Circuit::NullGate()); it = acc_.ReplaceIn(it, state); } else if (acc_.IsDependIn(it)) { @@ -57,11 +85,14 @@ void GraphVisitor::ReplaceGate(GateRef gate, StateDepend stateDepend, GateRef re acc_.DeleteGate(gate); } -void GraphVisitor::VisitGraph() +void CombinedPassVisitor::VisitGraph() { + for (auto pass : passList_) { + pass->Initialize(); + } circuit_->AdvanceTime(); orderCount_ = 0; - orderList_.resize(circuit_->GetMaxGateId() + 1, -1); + Resize(circuit_->GetMaxGateId() + 1, -1); GateRef returnList = acc_.GetReturnRoot(); auto uses = acc_.Uses(returnList); for (auto useIt = uses.begin(); useIt != uses.end(); useIt++) { @@ -79,30 +110,47 @@ void GraphVisitor::VisitGraph() PushGate(gate, 0); } } else { + for (auto pass : passList_) { + pass->Finalize(); + } break; } } } -void GraphVisitor::ReVisitGate(GateRef gate) +void CombinedPassVisitor::ReVisitGate(GateRef gate) { if (acc_.GetMark(gate) == MarkCode::FINISHED) { PushChangedGate(gate); } } -int32_t GraphVisitor::GetGateOrder(GateRef gate) const -{ - return orderList_[acc_.GetId(gate)]; -} -void GraphVisitor::SetGateOrder(GateRef gate, int32_t orderId) +GateRef CombinedPassVisitor::VisitGate(GateRef gate) { - orderList_[acc_.GetId(gate)] = orderId; + auto skip = passList_.end(); + for (auto i = passList_.begin(); i != passList_.end();) { + if (i == skip) { + i++; + continue; + } + GateRef replacement = (*i)->VisitGate(gate); + if (replacement == gate) { + skip = i; + i = passList_.begin(); + continue; + } else if (replacement != Circuit::NullGate()) { + return replacement; + } + i++; + } + if (skip == passList_.end()) { + return Circuit::NullGate(); + } + return gate; } - // Reverse post-order -void GraphVisitor::VisitTopGate(Edge& current) +void CombinedPassVisitor::VisitTopGate(Edge& current) { GateRef gate = current.GetGate(); // gate is delete or dead @@ -111,7 +159,24 @@ void GraphVisitor::VisitTopGate(Edge& current) return; } auto numIns = acc_.GetNumIns(gate); - for (size_t i = current.GetIndex(); i < numIns; i++) { + auto start = current.GetIndex(); + if (start >= numIns) { + start = 0; + } + for (size_t i = start; i < numIns; i++) { + GateRef input = acc_.GetIn(gate, i); + if (input == gate) { + continue; + } + // find not visited gate, push stack + if (acc_.GetMark(input) < MarkCode::VISITED) { + PushGate(input, 0); + // next index + current.SetIndex(i + 1); + return; + } + } + for (size_t i = 0; i < start; i++) { GateRef input = acc_.GetIn(gate, i); if (input == gate) { continue; @@ -146,7 +211,7 @@ void GraphVisitor::VisitTopGate(Edge& current) } } -void GraphVisitor::PrintStack() +void CombinedPassVisitor::PrintStack() { std::string log; for (size_t i = 0; i < workList_.size(); i++) { @@ -157,4 +222,19 @@ void GraphVisitor::PrintStack() LOG_COMPILER(INFO) << std::dec << log; } -} // namespace panda::ecmascript::kungfu \ No newline at end of file +void CombinedPassVisitor::PrintLog(const std::string& phaseName) +{ + if (enableLog_) { + LOG_COMPILER(INFO) << ""; + LOG_COMPILER(INFO) << "\033[34m" + << "====================" + << " After " << phaseName << " " + << "[" << methodName_ << "]" + << "====================" + << "\033[0m"; + circuit_->PrintAllGatesWithBytecode(); + LOG_COMPILER(INFO) << "\033[34m" << "========================= End ==========================" << "\033[0m"; + } +} + +} // namespace panda::ecmascript::kungfu diff --git a/ecmascript/compiler/graph_visitor.h b/ecmascript/compiler/combined_pass_visitor.h similarity index 49% rename from ecmascript/compiler/graph_visitor.h rename to ecmascript/compiler/combined_pass_visitor.h index e7a1a83a6568434359fd22c691d86b72a81e9726..897bcf5536c71b0a5e0ce2e111edb29a1da8be9d 100644 --- a/ecmascript/compiler/graph_visitor.h +++ b/ecmascript/compiler/combined_pass_visitor.h @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_COMPILER_GRAPH_VISITOR_H -#define ECMASCRIPT_COMPILER_GRAPH_VISITOR_H +#ifndef ECMASCRIPT_COMPILER_COMBINED_PASS_VISITOR_H +#define ECMASCRIPT_COMPILER_COMBINED_PASS_VISITOR_H #include "ecmascript/compiler/circuit_builder.h" #include "ecmascript/compiler/gate_accessor.h" @@ -22,23 +22,62 @@ namespace panda::ecmascript::kungfu { -class GraphVisitor { +class RPOVisitor { public: - GraphVisitor(Circuit *circuit, Chunk* chunk) - : circuit_(circuit), acc_(circuit), - chunk_(chunk), workList_(chunk), changedList_(chunk), orderList_(chunk) {} - - virtual ~GraphVisitor() = default; + virtual ~RPOVisitor() = default; + virtual int32_t GetGateOrder(GateRef gate) = 0; + virtual void SetGateOrder(GateRef gate, int32_t orderId) = 0; + virtual void Resize(int32_t size, int32_t num) = 0; + virtual void ReVisitGate(GateRef gate) = 0; + virtual void ReplaceGate(GateRef gate, GateRef replacement) = 0; + virtual void ReplaceGate(GateRef gate, StateDepend stateDepend, GateRef replacement) = 0; +}; - void VisitGraph(); - void ReVisitGate(GateRef gate); - int32_t GetGateOrder(GateRef gate) const; - void SetGateOrder(GateRef gate, int32_t orderId); +class PassVisitor { +public: + PassVisitor(Circuit* circuit, Chunk* chunk, RPOVisitor* visitor) + : circuit_(circuit), acc_(circuit), chunk_(chunk), visitor_(visitor) {} + virtual ~PassVisitor() = default; virtual GateRef VisitGate(GateRef gate) = 0; + virtual void Initialize() {} + virtual void Finalize() {} protected: + void ReplaceGate(GateRef gate, GateRef replacement) + { + visitor_->ReplaceGate(gate, replacement); + } + void ReplaceGate(GateRef gate, StateDepend stateDepend, GateRef replacement) + { + visitor_->ReplaceGate(gate, stateDepend, replacement); + } + Circuit* circuit_ {nullptr}; + GateAccessor acc_; + Chunk* chunk_ {nullptr}; + RPOVisitor* visitor_; +}; + +class CombinedPassVisitor : public RPOVisitor { +public: + CombinedPassVisitor(Circuit* circuit, bool enableLog, const std::string& name, Chunk* chunk) + : enableLog_(enableLog), methodName_(name), circuit_(circuit), acc_(circuit), + chunk_(chunk), workList_(chunk), changedList_(chunk), orderList_(chunk), passList_(chunk) {} + virtual ~CombinedPassVisitor() = default; + void AddPass(PassVisitor* pass); + + int32_t GetGateOrder(GateRef gate); + void SetGateOrder(GateRef gate, int32_t orderId); + void Resize(int32_t size, int32_t num); + + void VisitGraph(); + GateRef VisitGate(GateRef gate); + void ReVisitGate(GateRef gate); void ReplaceGate(GateRef gate, GateRef replacement); void ReplaceGate(GateRef gate, StateDepend stateDepend, GateRef replacement); + void PrintLog(const std::string& phaseName); + +protected: + void VisitTopGate(Edge& current); void PushGate(GateRef gate, size_t index) @@ -65,13 +104,17 @@ protected: } void PrintStack(); - Circuit *circuit_ {nullptr}; +private: + bool enableLog_ {false}; + std::string methodName_; + Circuit* circuit_ {nullptr}; GateAccessor acc_; Chunk* chunk_ {nullptr}; ChunkDeque workList_; ChunkDeque changedList_; ChunkVector orderList_; + ChunkVector passList_; uint32_t orderCount_ {0}; }; } // panda::ecmascript::kungfu -#endif // ECMASCRIPT_COMPILER_GRAPH_VISITOR_H \ No newline at end of file +#endif // ECMASCRIPT_COMPILER_COMBINED_PASS_VISITOR_H diff --git a/ecmascript/compiler/common_stubs.cpp b/ecmascript/compiler/common_stubs.cpp index 64cc215edd9e71bc495937454d470f42765eac3a..5f99b9dc96dcb0dac60e40dbd680d6cc22cbd86e 100644 --- a/ecmascript/compiler/common_stubs.cpp +++ b/ecmascript/compiler/common_stubs.cpp @@ -17,6 +17,7 @@ #include "ecmascript/base/number_helper.h" #include "ecmascript/compiler/access_object_stub_builder.h" +#include "ecmascript/compiler/builtins/builtins_string_stub_builder.h" #include "ecmascript/compiler/interpreter_stub.h" #include "ecmascript/compiler/llvm_ir_builder.h" #include "ecmascript/compiler/new_object_stub_builder.h" @@ -216,7 +217,7 @@ void InstanceofStubBuilder::GenerateCircuit() GateRef jsFunc = TaggedArgument(3); // 3 : 4th para GateRef slotId = Int32Argument(4); // 4 : 5th pars GateRef profileTypeInfo = UpdateProfileTypeInfo(glue, jsFunc); - Return(InstanceOf(glue, object, target, profileTypeInfo, slotId)); + Return(InstanceOf(glue, object, target, profileTypeInfo, slotId, ProfileOperation())); } void IncStubBuilder::GenerateCircuit() @@ -307,7 +308,7 @@ void GetPropertyByIndexStubBuilder::GenerateCircuit() GateRef glue = PtrArgument(0); GateRef receiver = TaggedArgument(1); GateRef index = Int32Argument(2); /* 2 : 3rd parameter is index */ - Return(GetPropertyByIndex(glue, receiver, index)); + Return(GetPropertyByIndex(glue, receiver, index, ProfileOperation())); } void SetPropertyByIndexStubBuilder::GenerateCircuit() @@ -399,7 +400,7 @@ void DeprecatedGetPropertyByValueStubBuilder::GenerateCircuit() GateRef glue = PtrArgument(0); GateRef receiver = TaggedArgument(1); GateRef key = TaggedArgument(2); // 2 : 3rd para - Return(GetPropertyByValue(glue, receiver, key)); + Return(GetPropertyByValue(glue, receiver, key, ProfileOperation())); } void SetPropertyByValueStubBuilder::GenerateCircuit() @@ -442,7 +443,7 @@ void TryLdGlobalByNameStubBuilder::GenerateCircuit() AccessObjectStubBuilder builder(this, jsFunc); StringIdInfo info = { 0, 0, StringIdInfo::Offset::INVALID, StringIdInfo::Length::INVALID }; GateRef profileTypeInfo = UpdateProfileTypeInfo(glue, jsFunc); - Return(builder.TryLoadGlobalByName(glue, id, info, profileTypeInfo, slotId)); + Return(builder.TryLoadGlobalByName(glue, id, info, profileTypeInfo, slotId, ProfileOperation())); } void TryStGlobalByNameStubBuilder::GenerateCircuit() @@ -455,7 +456,7 @@ void TryStGlobalByNameStubBuilder::GenerateCircuit() AccessObjectStubBuilder builder(this, jsFunc); StringIdInfo info = { 0, 0, StringIdInfo::Offset::INVALID, StringIdInfo::Length::INVALID }; GateRef profileTypeInfo = UpdateProfileTypeInfo(glue, jsFunc); - Return(builder.TryStoreGlobalByName(glue, id, info, value, profileTypeInfo, slotId)); + Return(builder.TryStoreGlobalByName(glue, id, info, value, profileTypeInfo, slotId, ProfileOperation())); } void LdGlobalVarStubBuilder::GenerateCircuit() @@ -467,7 +468,7 @@ void LdGlobalVarStubBuilder::GenerateCircuit() AccessObjectStubBuilder builder(this, jsFunc); StringIdInfo info = { 0, 0, StringIdInfo::Offset::INVALID, StringIdInfo::Length::INVALID }; GateRef profileTypeInfo = UpdateProfileTypeInfo(glue, jsFunc); - Return(builder.LoadGlobalVar(glue, id, info, profileTypeInfo, slotId)); + Return(builder.LoadGlobalVar(glue, id, info, profileTypeInfo, slotId, ProfileOperation())); } void StGlobalVarStubBuilder::GenerateCircuit() @@ -505,7 +506,7 @@ void TryLoadICByNameStubBuilder::GenerateCircuit() &hclassNotEqualFirstValue); Bind(&hclassEqualFirstValue); { - Return(LoadICWithHandler(glue, receiver, receiver, secondValue)); + Return(LoadICWithHandler(glue, receiver, receiver, secondValue, ProfileOperation())); } Bind(&hclassNotEqualFirstValue); { @@ -513,7 +514,7 @@ void TryLoadICByNameStubBuilder::GenerateCircuit() Branch(TaggedIsHole(cachedHandler), &receiverNotHeapObject, &cachedHandlerNotHole); Bind(&cachedHandlerNotHole); { - Return(LoadICWithHandler(glue, receiver, receiver, cachedHandler)); + Return(LoadICWithHandler(glue, receiver, receiver, cachedHandler, ProfileOperation())); } } } @@ -555,7 +556,7 @@ void TryLoadICByValueStubBuilder::GenerateCircuit() auto cachedHandler = CheckPolyHClass(secondValue, hclass); Branch(TaggedIsHole(cachedHandler), &receiverNotHeapObject, &cachedHandlerNotHole); Bind(&cachedHandlerNotHole); - Return(LoadICWithHandler(glue, receiver, receiver, cachedHandler)); + Return(LoadICWithHandler(glue, receiver, receiver, cachedHandler, ProfileOperation())); } } } @@ -680,8 +681,10 @@ void CreateArrayWithBufferStubBuilder::GenerateCircuit() GateRef glue = PtrArgument(0); GateRef index = Int32Argument(1); GateRef jsFunc = TaggedArgument(2); // 2 : 3rd para + GateRef slotId = Int32Argument(5); // 5 : 6th para NewObjectStubBuilder newBuilder(this); - Return(newBuilder.CreateArrayWithBuffer(glue, index, jsFunc)); + Return(newBuilder.CreateArrayWithBuffer(glue, index, jsFunc, Undefined(), + Undefined(), slotId, ProfileOperation())); } void NewJSObjectStubBuilder::GenerateCircuit() @@ -782,11 +785,13 @@ void JsProxyCallInternalStubBuilder::GenerateCircuit() Bind(¬Null); { GateRef target = GetTargetFromJSProxy(proxy); - GateRef globalConstOffset = IntPtr(JSThread::GlueData::GetGlobalConstOffset(env->Is32Bit())); - GateRef keyOffset = PtrAdd(globalConstOffset, + GateRef gConstPointer = Load(VariableType::JS_ANY(), glue, + IntPtr(JSThread::GlueData::GetGlobalConstOffset(env->Is32Bit()))); + + GateRef keyOffset = PtrAdd(gConstPointer, PtrMul(IntPtr(static_cast(ConstantIndex::APPLY_STRING_INDEX)), IntPtr(sizeof(JSTaggedValue)))); - GateRef key = Load(VariableType::JS_ANY(), glue, keyOffset); + GateRef key = Load(VariableType::JS_ANY(), keyOffset); GateRef method = CallRuntime(glue, RTSTUB_ID(JSObjectGetMethod), {handler, key}); ReturnExceptionIfAbruptCompletion(glue); @@ -886,6 +891,34 @@ void JsProxyCallInternalStubBuilder::GenerateCircuit() Return(*result); } +void GetSingleCharCodeByIndexStubBuilder::GenerateCircuit() +{ + GateRef str = TaggedArgument(1); + GateRef index = Int32Argument(2); + BuiltinsStringStubBuilder builder(this); + GateRef result = builder.GetSingleCharCodeByIndex(str, index); + Return(result); +} + +void CreateStringBySingleCharCodeStubBuilder::GenerateCircuit() +{ + GateRef glue = PtrArgument(0); + GateRef charCode = Int32Argument(1); + BuiltinsStringStubBuilder builder(this); + GateRef result = builder.CreateStringBySingleCharCode(glue, charCode); + Return(result); +} + +void FastStringEqualStubBuilder::GenerateCircuit() +{ + GateRef glue = PtrArgument(0); + GateRef str1 = TaggedArgument(1); + GateRef str2 = Int32Argument(2); + + GateRef result = FastStringEqual(glue, str1, str2); + Return(result); +} + CallSignature CommonStubCSigns::callSigns_[CommonStubCSigns::NUM_OF_STUBS]; void CommonStubCSigns::Initialize() diff --git a/ecmascript/compiler/common_stubs.h b/ecmascript/compiler/common_stubs.h index e990f492b5d6e6b2bac54ad205799a20cd9a3948..e48899c82c834610c4ba9dd89cc5cb402f934fa8 100644 --- a/ecmascript/compiler/common_stubs.h +++ b/ecmascript/compiler/common_stubs.h @@ -17,7 +17,6 @@ #define ECMASCRIPT_COMPILER_COMMON_STUBS_H #include "ecmascript/compiler/stub_builder.h" -#include "ecmascript/compiler/test_stubs.h" namespace panda::ecmascript::kungfu { #define COMMON_STUB_LIST(V) \ @@ -77,11 +76,13 @@ namespace panda::ecmascript::kungfu { V(CreateArrayWithBuffer) \ V(NewJSObject) \ V(JsBoundCallInternal) \ - V(JsProxyCallInternal) + V(JsProxyCallInternal) \ + V(CreateStringBySingleCharCode) \ + V(GetSingleCharCodeByIndex) \ + V(FastStringEqual) #define COMMON_STUB_ID_LIST(V) \ - COMMON_STUB_LIST(V) \ - TEST_STUB_SIGNATRUE_LIST(V) + COMMON_STUB_LIST(V) #define DECLARE_STUB_CLASS(name) \ class name##StubBuilder : public StubBuilder { \ diff --git a/ecmascript/compiler/compilation_driver.cpp b/ecmascript/compiler/compilation_driver.cpp index 41f34046d65d6a3e307627f60eab617c093c51e0..f0236fefba4c5c01f00065ea5beeccfed6fd7f3d 100644 --- a/ecmascript/compiler/compilation_driver.cpp +++ b/ecmascript/compiler/compilation_driver.cpp @@ -15,6 +15,7 @@ #include "ecmascript/compiler/compilation_driver.h" #include "ecmascript/compiler/file_generators.h" +#include "ecmascript/jspandafile/method_literal.h" #include "ecmascript/ts_types/ts_manager.h" namespace panda::ecmascript::kungfu { @@ -26,6 +27,7 @@ CompilationDriver::CompilationDriver(PGOProfilerDecoder &profilerDecoder, const std::string &fileName, const std::string &triple, LOptions *lOptions, + CompilerLog *log, bool outputAsm, size_t maxMethodsInModule) : vm_(collector->GetVM()), @@ -36,6 +38,7 @@ CompilationDriver::CompilationDriver(PGOProfilerDecoder &profilerDecoder, fileName_(fileName), triple_(triple), lOptions_(lOptions), + log_(log), outputAsm_(outputAsm), maxMethodsInModule_(maxMethodsInModule) { @@ -151,6 +154,16 @@ void CompilationDriver::TopologicalSortForRecords() ASSERT(tpOrder.size() == mainMethods.size()); } +void CompilationDriver::FetchPGOMismatchResult() +{ + ASSERT(log_ != nullptr); + uint32_t totalMethodCount = 0; + uint32_t mismatchMethodCount = 0; + std::set> mismatchMethodSet {}; + pfDecoder_.GetMismatchResult(totalMethodCount, mismatchMethodCount, mismatchMethodSet); + log_->SetPGOMismatchResult(totalMethodCount, mismatchMethodCount, mismatchMethodSet); +} + void CompilationDriver::UpdatePGO() { std::unordered_set newMethodIds; @@ -165,6 +178,7 @@ void CompilationDriver::UpdatePGO() return newMethodIds; }; pfDecoder_.Update(dfs); + FetchPGOMismatchResult(); } void CompilationDriver::InitializeCompileQueue() @@ -180,7 +194,7 @@ bool CompilationDriver::FilterMethod(const CString &recordName, const MethodLite const MethodPcInfo &methodPCInfo, const std::string &methodName) const { if (methodPCInfo.methodsSize > bytecodeInfo_.GetMaxMethodSize() || - !pfDecoder_.Match(jsPandaFile_, recordName, methodLiteral)) { + !pfDecoder_.Match(recordName, methodLiteral->GetMethodId())) { return true; } diff --git a/ecmascript/compiler/compilation_driver.h b/ecmascript/compiler/compilation_driver.h index ecf4d4025cea76d15c3a33cabcc59b8776d90c52..47bebd365fb14a82b9d56357de1c4d732eaea925 100644 --- a/ecmascript/compiler/compilation_driver.h +++ b/ecmascript/compiler/compilation_driver.h @@ -21,6 +21,7 @@ namespace panda::ecmascript::kungfu { class AOTFileGenerator; +class CompilerLog; struct LOptions; class Module; class CompilationDriver { @@ -33,6 +34,7 @@ public: const std::string &fileName, const std::string &triple, LOptions *lOptions, + CompilerLog *log, bool outputAsm, size_t maxMethodsInModule); ~CompilationDriver(); @@ -49,8 +51,7 @@ public: { const auto &methodList = bytecodeInfo_.GetMethodList(); auto &resolvedMethodInfo = methodList.at(resolvedMethod.GetOffset()); - MethodLiteral *methodLiteral = jsPandaFile_->GetMethodLiteralByIndex(resolvedMethod.GetOffset()); - if (pfDecoder_.Match(jsPandaFile_, recordName, methodLiteral) && !resolvedMethodInfo.IsTypeInferAbort()) { + if (pfDecoder_.Match(recordName, resolvedMethod) && !resolvedMethodInfo.IsTypeInferAbort()) { return; } // update profile and update compile queue @@ -136,6 +137,8 @@ public: CompileLastModuleThenDestroyIfNeeded(); } + void FetchPGOMismatchResult(); + void AddResolvedMethod(const CString &recordName, uint32_t classLiteralOffset) { if (!IsPGOLoaded() || !bytecodeInfo_.HasClassDefMethod(classLiteralOffset)) { @@ -352,6 +355,7 @@ private: std::string fileName_ {}; std::string triple_ {}; LOptions *lOptions_ {nullptr}; + CompilerLog *log_ {nullptr}; bool outputAsm_ {false}; size_t maxMethodsInModule_ {0}; }; diff --git a/ecmascript/compiler/compiler_log.cpp b/ecmascript/compiler/compiler_log.cpp index 7f86589990001f61403c71dd81a402281a897d51..3eb148510a089d7bb104ae490aadaa9de1dc7b37 100644 --- a/ecmascript/compiler/compiler_log.cpp +++ b/ecmascript/compiler/compiler_log.cpp @@ -139,6 +139,7 @@ void CompilerLog::Print() const if (compilerLogTime_) { PrintTime(); } + PrintPGOMismatchedMethod(); PrintCompiledMethod(); } @@ -200,6 +201,21 @@ void CompilerLog::PrintCompiledMethod() const } } +void CompilerLog::PrintPGOMismatchedMethod() const +{ + if (totalPGOMethodCount_ == 0) { + return; + } + LOG_COMPILER(INFO) << " "; + LOG_COMPILER(INFO) << " Number of mismatched methods from ap file : " << mismatchPGOMethodCount_ << " / " + << totalPGOMethodCount_; + for (const auto &it : mismatchPGOMethodSet_) { + LOG_COMPILER(INFO) << " method: " << std::setw(METHOD_LENS) << it.first + << " in record: " << std::setw(RECORD_LENS) << it.second + << " has not been found in abc, and will be abandoned."; + } +} + void CompilerLog::AddMethodTime(const std::string& name, uint32_t id, double time) { auto methodInfo = std::make_pair(id, name); @@ -289,5 +305,13 @@ void PGOTypeLogList::PrintPGOTypeLog() { LOG_COMPILER(INFO) << log_; } -// namespace panda::ecmascript::kungfu + +void CompilerLog::SetPGOMismatchResult(uint32_t &totalMethodCount, uint32_t &mismatchMethodCount, + std::set> &mismatchMethodSet) +{ + totalPGOMethodCount_ = totalMethodCount; + mismatchPGOMethodCount_ = mismatchMethodCount; + mismatchPGOMethodSet_ = std::move(mismatchMethodSet); } + +} // namespace panda::ecmascript::kungfu diff --git a/ecmascript/compiler/compiler_log.h b/ecmascript/compiler/compiler_log.h index 7bb325b238c183a5c1111975df1610223ddacbb7..2d106839a3836b3a34da76020d1c4a4b16b89f22 100644 --- a/ecmascript/compiler/compiler_log.h +++ b/ecmascript/compiler/compiler_log.h @@ -108,6 +108,8 @@ public: void AddMethodTime(const std::string& name, uint32_t id, double time); void AddPassTime(const std::string& name, double time); int GetIndex(); + void SetPGOMismatchResult(uint32_t &totalMethodCount, uint32_t &mismatchMethodCount, + std::set> &mismatchMethodSet); std::map nameIndex_; @@ -125,6 +127,7 @@ private: void PrintMethodTime() const; void PrintTime() const; void PrintCompiledMethod() const; + void PrintPGOMismatchedMethod() const; int idx_ {0}; bool allMethod_ {false}; @@ -139,6 +142,9 @@ private: std::map timePassMap_ {}; std::map, double> timeMethodMap_ {}; std::set> compiledMethodSet_ {}; + uint32_t totalPGOMethodCount_ {0}; + uint32_t mismatchPGOMethodCount_ {0}; + std::set> mismatchPGOMethodSet_ {}; }; class MethodLogList { diff --git a/ecmascript/compiler/dead_code_elimination.cpp b/ecmascript/compiler/dead_code_elimination.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e638dfb4412f6bff2a65e5d634bd1d4bfdcd6225 --- /dev/null +++ b/ecmascript/compiler/dead_code_elimination.cpp @@ -0,0 +1,199 @@ +/* + * 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/compiler/dead_code_elimination.h" + +namespace panda::ecmascript::kungfu { + +GateRef DeadCodeElimination::VisitGate(GateRef gate) +{ + auto opcode = acc_.GetOpCode(gate); + switch (opcode) { + case OpCode::SWITCH_BRANCH: + case OpCode::IF_BRANCH: + return EliminateBranch(gate); + case OpCode::MERGE: + case OpCode::LOOP_BEGIN: + return EliminateMergeAndLoopBegin(gate); + case OpCode::DEPEND_SELECTOR: + return EliminateDependSelector(gate); + case OpCode::IF_EXCEPTION: + return EliminateIfException(gate); + case OpCode::LOOP_EXIT: + return EliminateLoopExit(gate); + default : + return EliminateGate(gate); + } + return Circuit::NullGate(); +} + +GateRef DeadCodeElimination::StateIsDead(GateRef gate) +{ + auto state = acc_.GetState(gate); + if (acc_.IsDead(state)) { + return state; + } + return Circuit::NullGate(); +} + +GateRef DeadCodeElimination::EliminateDependSelector(GateRef gate) +{ + GateRef state = StateIsDead(gate); + if (state != Circuit::NullGate() && acc_.IsDead(state)) { + return state; + } + auto stateInput = acc_.GetState(gate); + size_t dependCount = acc_.GetDependCount(gate); + GateRef result = Circuit::NullGate(); + for (size_t i = 0; i < dependCount; i++) { + auto depend = acc_.GetDep(gate, i); + if (acc_.IsDead(depend)) { + GateRef correspondingState = acc_.GetState(stateInput, i); + acc_.ReplaceDependIn(depend, i, deadGate_); + acc_.ReplaceStateIn(correspondingState, i, deadGate_); + visitor_->ReVisitGate(stateInput); + result = gate; + } + } + return result; +} + +GateRef DeadCodeElimination::EliminateIfException(GateRef gate) +{ + GateRef state = StateIsDead(gate); + if (state != Circuit::NullGate() && acc_.IsDead(state)) { + return state; + } + GateRef depend = acc_.GetDep(gate); + if (acc_.IsDead(depend)) { + return deadGate_; + } + return Circuit::NullGate(); +} + +GateRef DeadCodeElimination::EliminateLoopExit(GateRef gate) +{ + GateRef state = StateIsDead(gate); + if (state != Circuit::NullGate() && acc_.IsDead(state)) { + return DeleteLoopExit(gate); + } + return Circuit::NullGate(); +} + +GateRef DeadCodeElimination::EliminateBranch(GateRef gate) +{ + GateRef state = StateIsDead(gate); + if (state != Circuit::NullGate() && acc_.IsDead(state)) { + return state; + } + GateRef value = acc_.GetValueIn(gate, 0); + if (acc_.IsDead(value)) { + auto uses = acc_.Uses(gate); + for (auto it = uses.begin(); it != uses.end(); it++) { + if (acc_.IsIfOrSwitchRelated(*it)) { + ReplaceGate(*it, acc_.GetState(gate)); + return deadGate_; + } + } + UNREACHABLE(); + } + return gate; +} + +void DeadCodeElimination::DecreaseAllSelectors(GateRef gate, size_t count) +{ + auto uses = acc_.Uses(gate); + for (auto it = uses.begin(); it != uses.end(); it++) { + if (acc_.IsSelector(*it)) { + acc_.DecreaseIn(*it, count + 1); + } + } +} +GateRef DeadCodeElimination::EliminateMergeAndLoopBegin(GateRef gate) +{ + if (acc_.GetOpCode(gate) == OpCode::LOOP_BEGIN) { + auto loopEntry = acc_.GetIn(gate, 0); + if (acc_.IsDead(loopEntry)) { + return deadGate_; + } + } + size_t count = 0; + size_t inputCount = acc_.GetNumIns(gate); + for (size_t i = 0; i < inputCount; i++) { + auto input = acc_.GetIn(gate, count); + if (acc_.IsDead(input)) { + acc_.DecreaseIn(gate, count); + DecreaseAllSelectors(gate, count); + } else { + count++; + } + } + if (count == 0) { + return deadGate_; + } else if (count == 1) { + auto uses = acc_.Uses(gate); + for (auto it = uses.begin(); it != uses.end(); it++) { + if (acc_.IsSelector(*it)) { + TryFindAndDeleteLoopExit(*it); + auto selectorInput = acc_.GetIn(*it, 1); + ReplaceGate(*it, selectorInput); + } + } + return acc_.GetIn(gate, 0); + } + if (count < inputCount) { + auto uses = acc_.Uses(gate); + for (auto it = uses.begin(); it != uses.end(); it++) { + if (acc_.IsSelector(*it)) { + visitor_->ReVisitGate(*it); + } + } + return gate; + } + return Circuit::NullGate(); +} + +void DeadCodeElimination::TryFindAndDeleteLoopExit(GateRef gate) +{ + auto uses = acc_.Uses(gate); + for (auto it = uses.begin(); it != uses.end(); it++) { + if (acc_.GetOpCode(*it) == OpCode::LOOP_EXIT_VALUE || acc_.GetOpCode(*it) == OpCode::LOOP_EXIT_DEPEND) { + GateRef loopExit = acc_.GetState(gate); + DeleteLoopExit(loopExit); + } + } +} +GateRef DeadCodeElimination::DeleteLoopExit(GateRef gate) +{ + auto uses = acc_.Uses(gate); + for (auto it = uses.begin(); it != uses.end(); it++) { + if (acc_.GetOpCode(*it) == OpCode::LOOP_EXIT_VALUE) { + ReplaceGate(*it, acc_.GetValueIn(*it)); + } else if (acc_.GetOpCode(*it) == OpCode::LOOP_EXIT_DEPEND) { + ReplaceGate(*it, acc_.GetDep(*it)); + } + } + return acc_.GetState(gate); +} + +GateRef DeadCodeElimination::EliminateGate(GateRef gate) +{ + if (acc_.GetStateCount(gate) == 1) { + return StateIsDead(gate); + } + return Circuit::NullGate(); +} + +} \ No newline at end of file diff --git a/ecmascript/compiler/test_stubs.h b/ecmascript/compiler/dead_code_elimination.h similarity index 35% rename from ecmascript/compiler/test_stubs.h rename to ecmascript/compiler/dead_code_elimination.h index c7aa0ffecf63eddc024d4cacac6f1bc0f1a56e16..da92aed89b6063ee42d1f7072fd8dcc6c436cad4 100644 --- a/ecmascript/compiler/test_stubs.h +++ b/ecmascript/compiler/dead_code_elimination.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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 @@ -13,27 +13,37 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_COMPILER_TEST_STUBS_H -#define ECMASCRIPT_COMPILER_TEST_STUBS_H +#ifndef ECMASCRIPT_COMPILER_DEAD_CODE_ELIMINATION_H +#define ECMASCRIPT_COMPILER_DEAD_CODE_ELIMINATION_H -#include "ecmascript/compiler/stub_builder.h" -#include "ecmascript/compiler/test_stubs_signature.h" +#include "ecmascript/compiler/combined_pass_visitor.h" namespace panda::ecmascript::kungfu { -#ifndef NDEBUG -#define DECLARE_STUB_CLASS(name) \ - class name##StubBuilder : public StubBuilder { \ - public: \ - name##StubBuilder(CallSignature *callSignature, Environment *env) \ - : StubBuilder(callSignature, env) {} \ - ~name##StubBuilder() = default; \ - NO_MOVE_SEMANTIC(name##StubBuilder); \ - NO_COPY_SEMANTIC(name##StubBuilder); \ - void GenerateCircuit() override; \ - }; - TEST_STUB_SIGNATRUE_LIST(DECLARE_STUB_CLASS) -#undef DECLARE_STUB_CLASS -#endif +class DeadCodeElimination : public PassVisitor { +public: + DeadCodeElimination(Circuit* circuit, RPOVisitor* visitor, Chunk* chunk) + : PassVisitor(circuit, chunk, visitor) + { + deadGate_ = circuit->DeadGate(); + } + GateRef VisitGate(GateRef gate) override; + +private: + GateRef StateIsDead(GateRef gate); + GateRef EliminateMergeAndLoopBegin(GateRef gate); + GateRef EliminateBranch(GateRef gate); + GateRef EliminateReturn(GateRef gate); + GateRef EliminateIfException(GateRef gate); + GateRef EliminateLoopExit(GateRef gate); + GateRef EliminateDependSelector(GateRef gate); + GateRef EliminateValueSelector(GateRef gate); + GateRef EliminateGate(GateRef gate); + GateRef DeleteLoopExit(GateRef gate); + void TryFindAndDeleteLoopExit(GateRef gate); + void DecreaseAllSelectors(GateRef gate, size_t count); + GateRef deadGate_; +}; } + #endif \ No newline at end of file diff --git a/ecmascript/compiler/early_elimination.cpp b/ecmascript/compiler/early_elimination.cpp index 60ab86f214385c86114e4dd686df711add040270..9b18058fff7d9ef0a8771f3aea23675c55e0f122 100644 --- a/ecmascript/compiler/early_elimination.cpp +++ b/ecmascript/compiler/early_elimination.cpp @@ -17,25 +17,12 @@ namespace panda::ecmascript::kungfu { -void EarlyElimination::Run() +void EarlyElimination::Initialize() { dependChains_.resize(circuit_->GetMaxGateId() + 1, nullptr); // 1: +1 for size renames_.resize(circuit_->GetMaxGateId() + 1, Circuit::NullGate()); // 1: +1 for size GateRef entry = acc_.GetDependRoot(); VisitDependEntry(entry); - VisitGraph(); - - if (IsLogEnabled()) { - LOG_COMPILER(INFO) << ""; - LOG_COMPILER(INFO) << "\033[34m" - << "====================" - << " After early elimination " - << "[" << GetMethodName() << "]" - << "====================" - << "\033[0m"; - circuit_->PrintAllGatesWithBytecode(); - LOG_COMPILER(INFO) << "\033[34m" << "========================= End ==========================" << "\033[0m"; - } } DependInfoNode* EarlyElimination::GetLoopDependInfo(GateRef depend) @@ -82,14 +69,22 @@ GateRef EarlyElimination::VisitGate(GateRef gate) case OpCode::LOAD_PROPERTY: case OpCode::LOAD_ELEMENT: case OpCode::LOAD_ARRAY_LENGTH: + case OpCode::LOAD_TYPED_ARRAY_LENGTH: case OpCode::TYPED_ARRAY_CHECK: case OpCode::OBJECT_TYPE_CHECK: + case OpCode::OBJECT_TYPE_COMPARE: case OpCode::STABLE_ARRAY_CHECK: case OpCode::INDEX_CHECK: case OpCode::TYPED_CALL_CHECK: case OpCode::LOAD_CONST_OFFSET: case OpCode::TYPED_BINARY_OP: case OpCode::TYPED_UNARY_OP: + case OpCode::JSINLINETARGET_TYPE_CHECK: + case OpCode::INLINE_ACCESSOR_CHECK: + case OpCode::LOAD_GETTER: + case OpCode::LOAD_SETTER: + case OpCode::ECMA_STRING_CHECK: + case OpCode::TYPE_OF_CHECK: return TryEliminateGate(gate); case OpCode::STATE_SPLIT: return TryEliminateFrameState(gate); @@ -171,6 +166,7 @@ GateRef EarlyElimination::TryEliminateFrameState(GateRef gate) auto frameValues = acc_.GetValueIn(curFrame, 1); // 1: frameValues acc_.DeleteGate(frameValues); acc_.DeleteGate(curFrame); + return depIn; } else { dependChain = dependChain->UpdateFrameState(curFrame); } @@ -231,6 +227,7 @@ DependInfoNode* EarlyElimination::UpdateWrite(GateRef gate, DependInfoNode* depe case OpCode::STORE_PROPERTY_NO_BARRIER: case OpCode::STORE_CONST_OFFSET: case OpCode::STORE_ELEMENT: + case OpCode::STORE_MEMORY: return dependInfo->UpdateStoreProperty(this, gate); default: return new (chunk_) DependInfoNode(chunk_); @@ -239,31 +236,48 @@ DependInfoNode* EarlyElimination::UpdateWrite(GateRef gate, DependInfoNode* depe bool EarlyElimination::MayAccessOneMemory(GateRef lhs, GateRef rhs) { - if (acc_.GetOpCode(rhs) == OpCode::STORE_ELEMENT) { - return acc_.GetOpCode(lhs) == OpCode::LOAD_ELEMENT; - } + auto rop = acc_.GetOpCode(rhs); auto lop = acc_.GetOpCode(lhs); - ASSERT(acc_.GetOpCode(rhs) == OpCode::STORE_PROPERTY || - acc_.GetOpCode(rhs) == OpCode::STORE_PROPERTY_NO_BARRIER || - acc_.GetOpCode(rhs) == OpCode::STORE_CONST_OFFSET); - if (lop == OpCode::LOAD_PROPERTY) { - auto loff = acc_.GetValueIn(lhs, 1); - auto roff = acc_.GetValueIn(rhs, 1); - ASSERT(acc_.GetOpCode(loff) == OpCode::CONSTANT); - ASSERT(acc_.GetOpCode(roff) == OpCode::CONSTANT); - return loff == roff; - } else if (lop == OpCode::LOAD_CONST_OFFSET) { - auto loff = acc_.GetOffset(lhs); - auto roff = acc_.GetOffset(rhs); - return loff == roff; - } else { - return false; + switch (rop) { + case OpCode::STORE_MEMORY: + ASSERT(acc_.GetMemoryType(rhs) == MemoryType::ELEMENT_TYPE); + return acc_.GetOpCode(lhs) == OpCode::LOAD_ELEMENT; + case OpCode::STORE_ELEMENT: { + if (lop == OpCode::LOAD_ELEMENT) { + bool lopIsTypedArray = acc_.TypedOpIsTypedArray(lhs, TypedOpKind::TYPED_LOAD_OP); + bool ropIsTypedArray = acc_.TypedOpIsTypedArray(rhs, TypedOpKind::TYPED_STORE_OP); + return lopIsTypedArray == ropIsTypedArray; + } + return false; + } + case OpCode::STORE_PROPERTY: + case OpCode::STORE_PROPERTY_NO_BARRIER: { + if (lop == OpCode::LOAD_PROPERTY) { + auto loff = acc_.GetValueIn(lhs, 1); + auto roff = acc_.GetValueIn(rhs, 1); + ASSERT(acc_.GetOpCode(loff) == OpCode::CONSTANT); + ASSERT(acc_.GetOpCode(roff) == OpCode::CONSTANT); + return loff == roff; + } + break; + } + case OpCode::STORE_CONST_OFFSET: { + if (lop == OpCode::LOAD_CONST_OFFSET) { + auto loff = acc_.GetOffset(lhs); + auto roff = acc_.GetOffset(rhs); + return loff == roff; + } + break; + } + default: + break; } + return false; } bool EarlyElimination::CompareOrder(GateRef lhs, GateRef rhs) { - return GetGateOrder(lhs) < GetGateOrder(rhs); + return visitor_->GetGateOrder(lhs) < visitor_->GetGateOrder(rhs); } bool EarlyElimination::CheckReplacement(GateRef lhs, GateRef rhs) @@ -306,19 +320,39 @@ bool EarlyElimination::CheckReplacement(GateRef lhs, GateRef rhs) break; } case OpCode::TYPED_ARRAY_CHECK: - case OpCode::OBJECT_TYPE_CHECK: - case OpCode::INDEX_CHECK: { + case OpCode::INDEX_CHECK: + case OpCode::TYPE_OF_CHECK: { if (acc_.GetParamGateType(lhs) != acc_.GetParamGateType(rhs)) { return false; } break; } + case OpCode::OBJECT_TYPE_CHECK: + case OpCode::OBJECT_TYPE_COMPARE: { + if (acc_.GetObjectTypeAccessor(lhs).GetType() != acc_.GetObjectTypeAccessor(rhs).GetType()) { + return false; + } + break; + } case OpCode::LOAD_CONST_OFFSET: { if (acc_.GetOffset(lhs) != acc_.GetOffset(rhs)) { return false; } break; } + case OpCode::JSINLINETARGET_TYPE_CHECK: { + if (acc_.GetFuncGT(lhs) != acc_.GetFuncGT(rhs)) { + return false; + } + break; + } + case OpCode::LOAD_GETTER: + case OpCode::LOAD_SETTER: { + if (acc_.TryGetValue(lhs) != acc_.TryGetValue(rhs)) { + return false; + } + break; + } default: break; } @@ -447,6 +481,18 @@ GateRef DependInfoNode::LookupCheckedNode(EarlyElimination* elimination, GateRef return gate; } +void DependInfoNode::GetGates(std::vector& gates) const +{ + ChunkStack st(chunk_); + for (Node* node = head_; node != nullptr; node = node->next) { + st.push(node->gate); + } + while (!st.empty()) { + gates.emplace_back(st.top()); + st.pop(); + } +} + GateRef DependInfoNode::LookupNode(EarlyElimination* elimination, GateRef gate) { for (Node* node = head_; node != nullptr; node = node->next) { diff --git a/ecmascript/compiler/early_elimination.h b/ecmascript/compiler/early_elimination.h index f0c53dadc6eda099b8d41c81a547d8f2157a4234..ad152459d678361d6b248f970c64db8d36571523 100644 --- a/ecmascript/compiler/early_elimination.h +++ b/ecmascript/compiler/early_elimination.h @@ -17,8 +17,8 @@ #define ECMASCRIPT_COMPILER_EARLY_ELIMINATION_H #include "ecmascript/compiler/circuit_builder.h" +#include "ecmascript/compiler/combined_pass_visitor.h" #include "ecmascript/compiler/gate_accessor.h" -#include "ecmascript/compiler/graph_visitor.h" #include "ecmascript/mem/chunk_containers.h" namespace panda::ecmascript::kungfu { @@ -37,10 +37,12 @@ public: DependInfoNode* UpdateStoreProperty(EarlyElimination* elimination, GateRef gate); bool Equals(DependInfoNode* that); void Merge(EarlyElimination* elimination, DependInfoNode* that); + void GetGates(std::vector& gates) const; void CopyFrom(DependInfoNode *other) { head_ = other->head_; size_ = other->size_; + frameState_ = other->frameState_; } private: struct Node { @@ -55,31 +57,20 @@ private: Chunk* chunk_; }; -class EarlyElimination : public GraphVisitor { +class EarlyElimination : public PassVisitor { public: - EarlyElimination(Circuit *circuit, bool enableLog, const std::string& name, Chunk* chunk) - : GraphVisitor(circuit, chunk), enableLog_(enableLog), - methodName_(name), dependChains_(chunk), renames_(chunk) {} + EarlyElimination(Circuit* circuit, RPOVisitor* visitor, Chunk* chunk) + : PassVisitor(circuit, chunk, visitor), dependChains_(chunk), renames_(chunk) {} ~EarlyElimination() = default; - void Run(); - + void Initialize() override; GateRef VisitGate(GateRef gate) override; bool CheckReplacement(GateRef lhs, GateRef rhs); bool CheckRenameReplacement(GateRef lhs, GateRef rhs); bool MayAccessOneMemory(GateRef lhs, GateRef rhs); bool CompareOrder(GateRef lhs, GateRef rhs); private: - bool IsLogEnabled() const - { - return enableLog_; - } - - const std::string& GetMethodName() const - { - return methodName_; - } DependInfoNode* GetDependChain(GateRef dependIn) { @@ -98,8 +89,6 @@ private: DependInfoNode* GetLoopDependInfo(GateRef depend); GateRef Rename(GateRef gate); - bool enableLog_ {false}; - std::string methodName_; ChunkVector dependChains_; ChunkVector renames_; }; diff --git a/ecmascript/compiler/file_generators.cpp b/ecmascript/compiler/file_generators.cpp index ce13809894d0c89160249206de2dd096997381d3..617e275b5e85b2512be3f79c790fe0dbda84dad1 100644 --- a/ecmascript/compiler/file_generators.cpp +++ b/ecmascript/compiler/file_generators.cpp @@ -122,9 +122,17 @@ void Module::CollectFuncEntryInfo(std::map &addr2name, A // 2.After all functions compiled, the module sections would be fixed uintptr_t textAddr = GetTextAddr(); uint32_t textSize = GetTextSize(); - uint32_t rodataSize = GetRODataSize(); - aotInfo.AlignTextSec(); - aotInfo.UpdateCurTextSecOffset(rodataSize); + uintptr_t rodataAddrBeforeText = 0; + uint32_t rodataSizeBeforeText = 0; + uintptr_t rodataAddrAfterText = 0; + uint32_t rodataSizeAfterText = 0; + std::tie(rodataAddrBeforeText, rodataSizeBeforeText, rodataAddrAfterText, rodataSizeAfterText) = + GetMergedRODataAddrAndSize(textAddr); + aotInfo.AlignTextSec(AOTFileInfo::PAGE_ALIGN); + if (rodataSizeBeforeText != 0) { + aotInfo.UpdateCurTextSecOffset(rodataSizeBeforeText); + aotInfo.AlignTextSec(AOTFileInfo::TEXT_SEC_ALIGN); + } const size_t funcCount = funcInfo.size(); funcCount_ = funcCount; @@ -149,6 +157,10 @@ void Module::CollectFuncEntryInfo(std::map &addr2name, A offset, moduleIndex, delta, funcSize, calleeSaveRegisters[i]); } aotInfo.UpdateCurTextSecOffset(textSize); + if (rodataSizeAfterText != 0) { + aotInfo.AlignTextSec(AOTFileInfo::DATA_SEC_ALIGN); + aotInfo.UpdateCurTextSecOffset(rodataSizeAfterText); + } } void Module::CollectModuleSectionDes(ModuleSectionDes &moduleDes) const @@ -194,9 +206,9 @@ uintptr_t Module::GetSectionAddr(ElfSecName sec) const return assembler_->GetSectionAddr(sec); } -void Module::RunAssembler(const CompilerLog &log) +void Module::RunAssembler(const CompilerLog &log, bool fastCompileMode) { - assembler_->Run(log); + assembler_->Run(log, fastCompileMode); } void Module::DisassemblerFunc(std::map &addr2name, uint64_t textOffset, @@ -263,7 +275,22 @@ void StubFileGenerator::DisassembleAsmStubs(std::map &ad uint64_t AOTFileGenerator::RollbackTextSize(Module *module) { - return aotInfo_.GetCurTextSecOffset() - module->GetSectionSize(ElfSecName::TEXT); + uint64_t textAddr = module->GetSectionAddr(ElfSecName::TEXT); + uint32_t textSize = module->GetSectionSize(ElfSecName::TEXT); + uint64_t rodataAddrBeforeText = 0; + uint32_t rodataSizeBeforeText = 0; + uint64_t rodataAddrAfterText = 0; + uint32_t rodataSizeAfterText = 0; + std::tie(rodataAddrBeforeText, rodataSizeBeforeText, rodataAddrAfterText, rodataSizeAfterText) = + module->GetMergedRODataAddrAndSize(textAddr); + uint64_t textStart = 0; + if (rodataSizeAfterText == 0) { + textStart = aotInfo_.GetCurTextSecOffset() - textSize; + } else { + textStart = aotInfo_.GetCurTextSecOffset() - textSize - rodataSizeAfterText; + textStart = AlignDown(textStart, AOTFileInfo::DATA_SEC_ALIGN); + } + return textStart; } void AOTFileGenerator::CollectCodeInfo(Module *module, uint32_t moduleIdx) @@ -353,7 +380,8 @@ void AOTFileGenerator::CompileLatestModuleThenDestroy() uint32_t latestModuleIdx = GetModuleVecSize() - 1; { TimeScope timescope("LLVMIROpt", const_cast(log_)); - latestModule->RunAssembler(*(log_)); + bool fastCompileMode = vm_->GetJSOptions().GetFastAOTCompileMode(); + latestModule->RunAssembler(*(log_), fastCompileMode); } { TimeScope timescope("LLVMCodeGen", const_cast(log_)); @@ -402,7 +430,8 @@ void AOTFileGenerator::SaveAOTFile(const std::string &filename) if (aotInfo_.GetTotalCodeSize() == 0) { LOG_HOST_TOOL_ERROR << "The an file generated by the aot compiler is empty! " << "Maybe file in apPath is empty or all methods in ap file are mismatched"; - LOG_COMPILER(FATAL) << "error: code size of generated an file is empty!"; + LOG_COMPILER(ERROR) << "error: code size of generated an file is empty!"; + return; } if (!CreateDirIfNotExist(filename)) { LOG_COMPILER(ERROR) << "Fail to access dir:" << filename; diff --git a/ecmascript/compiler/file_generators.h b/ecmascript/compiler/file_generators.h index 5319f50018c41d4ea1acc1da6c895e072d6221e8..7adb74473954468663566993a963a2eb65c52e48 100644 --- a/ecmascript/compiler/file_generators.h +++ b/ecmascript/compiler/file_generators.h @@ -16,6 +16,7 @@ #ifndef ECMASCRIPT_COMPILER_FILE_GENERATORS_H #define ECMASCRIPT_COMPILER_FILE_GENERATORS_H +#include "ecmascript/base/number_helper.h" #include "ecmascript/compiler/aot_file/aot_file_manager.h" #include "ecmascript/compiler/assembler_module.h" #include "ecmascript/compiler/compiler_log.h" @@ -58,7 +59,21 @@ public: uintptr_t GetSectionAddr(ElfSecName sec) const; - void RunAssembler(const CompilerLog &log); + std::tuple GetMergedRODataAddrAndSize(uint64_t textAddr) const + { + uint64_t addrBeforeText = base::MAX_UINT64_VALUE; + uint32_t sizeBeforeText = 0; + uint64_t addrAfterText = base::MAX_UINT64_VALUE; + uint32_t sizeAfterText = 0; + for (uint8_t i = static_cast(ElfSecName::RODATA); i <= static_cast(ElfSecName::RODATA_CST32); + i++) { + UpdateRODataInfo(textAddr, addrBeforeText, sizeBeforeText, addrAfterText, sizeAfterText, + static_cast(i)); + } + return std::make_tuple(addrBeforeText, sizeBeforeText, addrAfterText, sizeAfterText); + } + + void RunAssembler(const CompilerLog &log, bool fastCompileMode); void DisassemblerFunc(std::map &addr2name, uint64_t textOffset, const CompilerLog &log, const MethodLogList &logList, std::ostringstream &codeStream); @@ -81,9 +96,22 @@ private: return assembler_->GetSectionSize(ElfSecName::TEXT); } - uint32_t GetRODataSize() const + void UpdateRODataInfo(uint64_t textAddr, uint64_t &addrBeforeText, uint32_t &sizeBeforeText, + uint64_t &addrAfterText, uint32_t &sizeAfterText, ElfSecName sec) const { - return assembler_->GetSectionSize(ElfSecName::RODATA_CST8); + uint64_t curSectionAddr = GetSectionAddr(sec); + if (curSectionAddr == 0) { + ASSERT(GetSectionSize(sec) == 0); + return; + } + ASSERT(curSectionAddr != textAddr); + if (curSectionAddr < textAddr) { + addrBeforeText = (curSectionAddr < addrBeforeText) ? curSectionAddr : addrBeforeText; + sizeBeforeText += GetSectionSize(sec); + } else { + addrAfterText = (curSectionAddr < addrAfterText) ? curSectionAddr : addrAfterText; + sizeAfterText += GetSectionSize(sec); + } } LLVMModule *llvmModule_ {nullptr}; @@ -121,7 +149,7 @@ protected: void RunLLVMAssembler() { for (auto m : modulePackage_) { - m.RunAssembler(*(log_)); + m.RunAssembler(*(log_), false); } } diff --git a/ecmascript/compiler/frame_states.cpp b/ecmascript/compiler/frame_states.cpp index c2d398e994730318a2a7c58e385a32f004b1c2c0..bf653209d514d5cd5990c1efc10f4ca9735a40bd 100644 --- a/ecmascript/compiler/frame_states.cpp +++ b/ecmascript/compiler/frame_states.cpp @@ -26,28 +26,17 @@ FrameStateBuilder::FrameStateBuilder(BytecodeCircuitBuilder *builder, gateAcc_(circuit), bcEndStateInfos_(circuit->chunk()), bbBeginStateInfos_(circuit->chunk()), + loopExitStateInfos_(circuit->chunk()), postOrderList_(circuit->chunk()) { } FrameStateBuilder::~FrameStateBuilder() { - for (auto state : bcEndStateInfos_) { - if (state != nullptr) { - delete state; - } - } - for (auto state : bbBeginStateInfos_) { - if (state != nullptr) { - delete state; - } - } - if (liveOutResult_ != nullptr) { - delete liveOutResult_; - } liveOutResult_ = nullptr; bcEndStateInfos_.clear(); bbBeginStateInfos_.clear(); + loopExitStateInfos_.clear(); builder_ = nullptr; } @@ -68,6 +57,11 @@ GateRef FrameStateBuilder::BuildFrameValues(FrameStateInfo *stateInfo) return circuit_->NewGate(circuit_->FrameValues(frameStateInputs), inList); } +GateRef FrameStateBuilder::BuildEmptyFrameValues() +{ + return circuit_->NewGate(circuit_->FrameValues(0), {}); +} + GateRef FrameStateBuilder::BuildFrameStateGate(size_t pcOffset, GateRef frameValues, FrameStateOutput output) { GateRef frameArgs = builder_->GetFrameArgs(); @@ -77,7 +71,7 @@ GateRef FrameStateBuilder::BuildFrameStateGate(size_t pcOffset, GateRef frameVal {frameArgs, frameValues, preFrameState}); } -void FrameStateBuilder::BindStateSplit(GateRef state, GateRef depend, GateRef frameState) +GateRef FrameStateBuilder::BindStateSplit(GateRef state, GateRef depend, GateRef frameState) { GateRef stateSplit = circuit_->NewGate(circuit_->StateSplit(), {state, depend, frameState}); auto uses = gateAcc_.Uses(depend); @@ -91,6 +85,7 @@ void FrameStateBuilder::BindStateSplit(GateRef state, GateRef depend, GateRef fr if (builder_->IsLogEnabled()) { gateAcc_.ShortPrint(frameState); } + return stateSplit; } void FrameStateBuilder::BindStateSplit(GateRef gate, GateRef frameState) @@ -109,7 +104,8 @@ void FrameStateBuilder::BindStateSplit(GateRef gate, GateRef frameState) FrameStateInfo *FrameStateBuilder::CreateEmptyStateInfo() { - auto frameInfo = new FrameStateInfo(numVregs_); + auto chunk = circuit_->chunk(); + auto frameInfo = chunk->New(chunk, numVregs_); for (size_t i = 0; i < numVregs_; i++) { frameInfo->SetValuesAt(i, Circuit::NullGate()); } @@ -169,7 +165,7 @@ bool FrameStateBuilder::MergeIntoPredBC(uint32_t predPc, size_t diff) auto value = frameInfo->ValuesAt(i); // if value not null, merge pred if (value == Circuit::NullGate() && predValue != Circuit::NullGate()) { - predValue = TryGetLoopExitValue(predValue, diff); + predValue = TryGetLoopExitValue(predValue, diff, i); frameInfo->SetValuesAt(i, predValue); changed = true; } @@ -185,6 +181,58 @@ GateRef FrameStateBuilder::GetPreBBInput(BytecodeRegion *bb, BytecodeRegion *pre return gate; } +GateRef FrameStateBuilder::GetPredStateGateBetweenBB(BytecodeRegion *bb, BytecodeRegion *predBb) +{ + GateRef gate = bb->stateCurrent; + if (bb->numOfLoopBacks != 0) { + ASSERT(bb->loopbackBlocks.size() != 0); + ASSERT(gateAcc_.GetStateCount(gate) > 1); + auto forwardState = gateAcc_.GetState(gate, 0); // 0: fowward + auto loopBackState = gateAcc_.GetState(gate, 1); // 1: back + size_t backIndex = 0; + size_t forwardIndex = 0; + for (size_t i = 0; i < bb->numOfStatePreds; ++i) { + auto predId = std::get<0>(bb->expandedPreds.at(i)); + if (bb->loopbackBlocks.count(predId)) { + if (predId == predBb->id) { + if (bb->numOfLoopBacks == 1) { + return loopBackState; + } + if (backIndex == 0) { + return gateAcc_.GetState(loopBackState); + } else { + auto loopBackMerge = gateAcc_.GetState(loopBackState); + return gateAcc_.GetState(loopBackMerge, backIndex); + } + } + backIndex++; + } else { + if (predId == predBb->id) { + auto mergeCount = bb->numOfStatePreds - bb->numOfLoopBacks; + if (mergeCount == 1) { + return forwardState; + } + return gateAcc_.GetState(forwardState, forwardIndex); + } + forwardIndex++; + } + } + UNREACHABLE(); + return Circuit::NullGate(); + } + + ASSERT(gateAcc_.GetStateCount(gate) == bb->numOfStatePreds); + // The phi input nodes need to be traversed in reverse order, because there is a bb with multiple def points + for (size_t i = bb->numOfStatePreds - 1; i >= 0; --i) { + auto predId = std::get<0>(bb->expandedPreds.at(i)); + if (predId == predBb->id) { + return gateAcc_.GetState(gate, i); + } + } + UNREACHABLE(); + return Circuit::NullGate(); +} + GateRef FrameStateBuilder::GetPhiComponent(BytecodeRegion *bb, BytecodeRegion *predBb, GateRef phi) { ASSERT(gateAcc_.GetOpCode(phi) == OpCode::VALUE_SELECTOR); @@ -249,7 +297,7 @@ bool FrameStateBuilder::MergeIntoPredBB(BytecodeRegion *bb, BytecodeRegion *pred auto target = GetPreBBInput(bb, predBb, phi); if (target != Circuit::NullGate()) { auto diff = LoopExitCount(predBb, bb); - target = TryGetLoopExitValue(target, diff); + target = TryGetLoopExitValue(target, diff, accumulatorIndex_); predLiveout->SetValuesAt(accumulatorIndex_, target); } } @@ -264,7 +312,7 @@ bool FrameStateBuilder::MergeIntoPredBB(BytecodeRegion *bb, BytecodeRegion *pred continue; } auto diff = LoopExitCount(predBb, bb); - target = TryGetLoopExitValue(target, diff); + target = TryGetLoopExitValue(target, diff, reg); predLiveout->SetValuesAt(reg, target); } } @@ -343,6 +391,7 @@ void FrameStateBuilder::BuildFrameState() bcEndStateInfos_.resize(builder_->GetLastBcIndex() + 1, nullptr); // 1: +1 pcOffsets size auto size = builder_->GetBasicBlockCount(); bbBeginStateInfos_.resize(size, nullptr); + loopExitStateInfos_.resize(circuit_->GetMaxGateId() + 1, nullptr); liveOutResult_ = CreateEmptyStateInfo(); BuildPostOrderList(size); ComputeLiveState(); @@ -418,18 +467,33 @@ bool FrameStateBuilder::IsAsyncResolveOrSusp(const BytecodeInfo &bytecodeInfo) return opcode == EcmaOpcode::SUSPENDGENERATOR_V8 || opcode == EcmaOpcode::ASYNCGENERATORRESOLVE_V8_V8_V8; } -void FrameStateBuilder::BuildStateSplitAfter(size_t index) +void FrameStateBuilder::BuildStateSplitAfter(size_t index, BytecodeRegion& bb) { auto gate = builder_->GetGateByBcIndex(index); ASSERT(gateAcc_.GetOpCode(gate) == OpCode::JS_BYTECODE); - auto pcOffset = builder_->GetPcOffset(index + 1); // 1: for after - auto stateInfo = GetFrameInfoAfter(index); + auto nextIndex = GetNearestNextIndex(index, bb); + if (builder_->GetBytecodeInfo(nextIndex).IsCall()) { + return; + } + auto pcOffset = builder_->GetPcOffset(nextIndex); + auto stateInfo = GetFrameInfoAfter(nextIndex - 1); // 1: after prev bc GateRef frameValues = BuildFrameValues(stateInfo); GateRef frameStateAfter = BuildFrameStateGate( pcOffset, frameValues, FrameStateOutput::Invalid()); BindStateSplit(gate, gate, frameStateAfter); } +size_t FrameStateBuilder::GetNearestNextIndex(size_t index, BytecodeRegion& bb) const +{ + index++; + auto gate = builder_->GetGateByBcIndex(index); + while ((gate == Circuit::NullGate() || gateAcc_.IsConstant(gate)) && index < bb.end) { + index++; + gate = builder_->GetGateByBcIndex(index); + } + return index; +} + void FrameStateBuilder::BuildStateSplitBefore(BytecodeRegion& bb, size_t index) { auto pcOffset = builder_->GetPcOffset(index); @@ -445,24 +509,71 @@ void FrameStateBuilder::BuildStateSplitBefore(BytecodeRegion& bb, size_t index) } } -bool FrameStateBuilder::ShouldInsertFrameStateBefore(BytecodeRegion& bb, - const BytecodeInfo &bytecodeInfo, size_t index) +void FrameStateBuilder::FindLoopExit(GateRef gate) { - // add Call State Split for inline - if (bytecodeInfo.IsCall()) { - return true; + // if find the bytecode gate, return. + if (builder_->IsBcIndexByGate(gate)) { + return; + } + + // if find the loopExit, do process. + if (gateAcc_.GetOpCode(gate) == OpCode::LOOP_EXIT) { + GateRef findBefore = gateAcc_.GetState(gate); + while (!builder_->IsBcIndexByGate(findBefore)) { + findBefore = gateAcc_.GetState(findBefore); + } + + // Build stateSplit After + size_t index = builder_->GetBcIndexByGate(findBefore); + auto pcOffset = builder_->GetPcOffset(index); + auto stateInfo = GetOrOCreateLoopExitStateInfo(gate); + ASSERT(stateInfo != nullptr); + GateRef frameValues = BuildFrameValues(stateInfo); + GateRef frameStateAfter = BuildFrameStateGate( + pcOffset, frameValues, FrameStateOutput::Invalid()); + + GateRef stateIn = gate; // stateIn - LoopExit + GateRef dependIn = Circuit::NullGate(); // dependIn - LoopExitDepend + auto uses = gateAcc_.Uses(gate); + for (auto useIt = uses.begin(); useIt != uses.end();) { + if (gateAcc_.GetOpCode(*useIt) == OpCode::LOOP_EXIT_DEPEND) { + dependIn = *useIt; + } + useIt++; + } + ASSERT(dependIn != Circuit::NullGate()); + + // Bind stateSplit after the loopExit and loopExitDepend. + BindStateSplit(stateIn, dependIn, frameStateAfter); } + + // continue to find the loopExit. + + FindLoopExit(gateAcc_.GetState(gate)); +} + +bool FrameStateBuilder::ShouldInsertFrameStateBefore(BytecodeRegion& bb, size_t index) +{ auto gate = builder_->GetGateByBcIndex(index); if (index == bb.start) { if (bb.numOfStatePreds > 1) { // 1: > 1 is merge + // backward to find loop exits, insert stateSplit before loopExit. + for (auto &predBb: bb.preds) { + if (LoopExitCount(predBb, &bb) > 0) { + auto target = GetPredStateGateBetweenBB(&bb, predBb); + FindLoopExit(target); + } + } return true; + } else if (bb.numOfStatePreds == 1) { // 1: == 1 maybe loopexit + auto predBb = (bb.preds.size() > 0) ? bb.preds.at(0) : bb.trys.at(0); + if (LoopExitCount(predBb, &bb) > 0) { + return true; + } } if (gateAcc_.GetOpCode(bb.dependCurrent) == OpCode::GET_EXCEPTION) { return true; } - if (gateAcc_.GetOpCode(bb.stateCurrent) == OpCode::IF_SUCCESS) { - return true; - } } else { if (gate == Circuit::NullGate() || gateAcc_.GetStateCount(gate) != 1) { return false; @@ -478,7 +589,12 @@ bool FrameStateBuilder::ShouldInsertFrameStateBefore(BytecodeRegion& bb, void FrameStateBuilder::BuildFrameState(BytecodeRegion& bb, const BytecodeInfo &bytecodeInfo, size_t index) { - bool needStateSplitBefore = ShouldInsertFrameStateBefore(bb, bytecodeInfo, index); + // Not bind state split for Call + if (bytecodeInfo.IsCall()) { + BuildCallFrameState(index, bb); + } + + bool needStateSplitBefore = ShouldInsertFrameStateBefore(bb, index); auto gate = builder_->GetGateByBcIndex(index); if (needStateSplitBefore && index != bb.start) { auto depend = gateAcc_.GetDep(gate); @@ -492,11 +608,21 @@ void FrameStateBuilder::BuildFrameState(BytecodeRegion& bb, if (!bytecodeInfo.NoSideEffects() && !bytecodeInfo.IsThrow()) { if (!gateAcc_.HasIfExceptionUse(gate)) { - BuildStateSplitAfter(index); + BuildStateSplitAfter(index, bb); } } } +void FrameStateBuilder::BuildCallFrameState(size_t index, BytecodeRegion& bb) +{ + auto pcOffset = builder_->GetPcOffset(index); + auto stateInfo = GetFrameInfoBefore(bb, index); + GateRef frameValues = BuildFrameValues(stateInfo); + GateRef frameState = BuildFrameStateGate(pcOffset, frameValues, FrameStateOutput::Invalid()); + auto gate = builder_->GetGateByBcIndex(index); + gateAcc_.ReplaceFrameStateIn(gate, frameState); +} + void FrameStateBuilder::BindBBStateSplit() { auto& dfsList = builder_->GetDfsList(); @@ -505,7 +631,11 @@ void FrameStateBuilder::BindBBStateSplit() if (builder_->IsFirstBasicBlock(bb.id)) { BuildStateSplitBefore(bb, bb.start); } + if (builder_->IsEntryBlock(bb.id)) { + BuildStateSplitBefore(bb, bb.start); + } ASSERT(!bb.isDead); + builder_->EnumerateBlock(bb, [&](const BytecodeInfo &bytecodeInfo) -> bool { auto &iterator = bb.GetBytecodeIterator(); auto index = iterator.Index(); @@ -560,23 +690,20 @@ void FrameStateBuilder::UpdateVirtualRegistersOfResume(GateRef gate) size_t FrameStateBuilder::LoopExitCount(BytecodeRegion* bb, BytecodeRegion* bbNext) { - size_t headDep = ((bbNext->numOfLoopBacks > 0) && (bbNext->loopbackBlocks.count(bb->id) == 0)) ? 1 : 0; - if (bbNext->loopDepth < headDep) { - // loop optimization disabled. - return 0; - } - size_t nextDep = bbNext->loopDepth - headDep; - ASSERT(bb->loopDepth >= nextDep); - return bb->loopDepth > nextDep; + return builder_->LoopExitCount(bb->id, bbNext->id); } -GateRef FrameStateBuilder::TryGetLoopExitValue(GateRef value, size_t diff) +GateRef FrameStateBuilder::TryGetLoopExitValue(GateRef value, size_t diff, size_t reg) { if ((gateAcc_.GetOpCode(value) != OpCode::LOOP_EXIT_VALUE) || (diff == 0)) { return value; } + for (size_t i = 0; i < diff; ++i) { ASSERT(gateAcc_.GetOpCode(value) == OpCode::LOOP_EXIT_VALUE); + GateRef loopExit = gateAcc_.GetState(value); + FrameStateInfo* loopExitFrameInfo = GetOrOCreateLoopExitStateInfo(loopExit); + loopExitFrameInfo->SetValuesAt(reg, value); value = gateAcc_.GetValueIn(value); } return value; diff --git a/ecmascript/compiler/frame_states.h b/ecmascript/compiler/frame_states.h index ddd5ef4e9b0fedbbdb73fef5f95e684c1d76f81c..58ef852b73576b3d0f47c9dd81f3903fd6426dcc 100644 --- a/ecmascript/compiler/frame_states.h +++ b/ecmascript/compiler/frame_states.h @@ -29,7 +29,8 @@ struct BytecodeRegion; class FrameStateInfo { public: - explicit FrameStateInfo(size_t numVregs) : values_(numVregs), liveout_(numVregs) {} + explicit FrameStateInfo(Chunk* chunk, size_t numVregs) + : values_(numVregs, chunk), liveout_(chunk, numVregs) {} void SetValuesAt(size_t index, GateRef gate) { @@ -65,7 +66,7 @@ public: } private: // [numVRegs_] [extra args] [numArgs_] [accumulator] - std::vector values_ {}; + ChunkVector values_; BitSet liveout_; }; @@ -100,23 +101,25 @@ private: { UpdateVirtualRegister(accumulatorIndex_, gate); } - void BindStateSplit(GateRef state, GateRef depend, GateRef frameState); + GateRef BindStateSplit(GateRef state, GateRef depend, GateRef frameState); void BindStateSplit(GateRef gate, GateRef frameState); void BindBBStateSplit(); void UpdateVirtualRegister(size_t id, size_t index, GateRef gate); GateRef BuildFrameStateGate(size_t pcOffset, GateRef frameValues, FrameStateOutput output); GateRef BuildFrameValues(FrameStateInfo *stateInfo); + GateRef BuildEmptyFrameValues(); FrameStateInfo *CreateEmptyStateInfo(); void BuildPostOrderList(size_t size); bool ComputeLiveOut(size_t bbId); void ComputeLiveState(); void ComputeLiveOutBC(uint32_t index, const BytecodeInfo &bytecodeInfo, size_t bbId); + void FindLoopExit(GateRef gate); bool IsAsyncResolveOrSusp(const BytecodeInfo &bytecodeInfo); bool MergeIntoPredBC(uint32_t predPc, size_t diff); bool MergeIntoPredBB(BytecodeRegion *bb, BytecodeRegion *predBb); size_t LoopExitCount(BytecodeRegion *bb, BytecodeRegion *bbNext); - GateRef TryGetLoopExitValue(GateRef value, size_t diff); + GateRef TryGetLoopExitValue(GateRef value, size_t diff, size_t reg); FrameStateInfo *GetOrOCreateBCEndStateInfo(uint32_t bcIndex) { auto currentInfo = bcEndStateInfos_[bcIndex]; @@ -126,8 +129,39 @@ private: } return currentInfo; } - FrameStateInfo *GetBBBeginStateInfo(size_t bbId) const + FrameStateInfo *GetOrOCreateLoopExitStateInfo(GateRef gate) { + ASSERT(gateAcc_.GetOpCode(gate) == OpCode::LOOP_EXIT); + size_t idx = gateAcc_.GetId(gate); + ASSERT(idx < circuit_ -> GetMaxGateId()); + auto currentInfo = loopExitStateInfos_[idx]; + if (currentInfo == nullptr) { + currentInfo = CreateEmptyStateInfo(); + loopExitStateInfos_[idx] = currentInfo; + } + return currentInfo; + } + FrameStateInfo *GetEntryBBBeginStateInfo() + { + auto entry = CreateEmptyStateInfo(); + auto first = bbBeginStateInfos_.at(1); // 1: first block + for (size_t i = 0; i < numVregs_; ++i) { + auto value = first->ValuesAt(i); + if (value == Circuit::NullGate()) { + continue; + } + if (gateAcc_.IsValueSelector(value)) { + value = gateAcc_.GetValueIn(value); + } + entry->SetValuesAt(i, value); + } + return entry; + } + FrameStateInfo *GetBBBeginStateInfo(size_t bbId) + { + if (bbId == 0) { // 0: entry block + return GetEntryBBBeginStateInfo(); + } return bbBeginStateInfos_.at(bbId); } void UpdateVirtualRegistersOfSuspend(GateRef gate); @@ -138,10 +172,12 @@ private: GateRef GetPreBBInput(BytecodeRegion *bb, BytecodeRegion *predBb, GateRef gate); GateRef GetPhiComponent(BytecodeRegion *bb, BytecodeRegion *predBb, GateRef phi); void BuildFrameState(BytecodeRegion& bb, const BytecodeInfo &bytecodeInfo, size_t index); - void BuildStateSplitAfter(size_t index); + void BuildStateSplitAfter(size_t index, BytecodeRegion& bb); void BuildStateSplitBefore(BytecodeRegion& bb, size_t index); - bool ShouldInsertFrameStateBefore(BytecodeRegion& bb, - const BytecodeInfo &bytecodeInfo, size_t index); + bool ShouldInsertFrameStateBefore(BytecodeRegion& bb, size_t index); + void BuildCallFrameState(size_t index, BytecodeRegion& bb); + size_t GetNearestNextIndex(size_t index, BytecodeRegion& bb) const; + GateRef GetPredStateGateBetweenBB(BytecodeRegion *bb, BytecodeRegion *predBb); BytecodeCircuitBuilder *builder_{nullptr}; FrameStateInfo *liveOutResult_{nullptr}; @@ -151,6 +187,7 @@ private: GateAccessor gateAcc_; ChunkVector bcEndStateInfos_; ChunkVector bbBeginStateInfos_; + ChunkVector loopExitStateInfos_; ChunkVector postOrderList_; }; } // panda::ecmascript::kungfu diff --git a/ecmascript/compiler/gate.cpp b/ecmascript/compiler/gate.cpp index cf8fe3f4ef4405e84b18d598aed619831314e37e..6e7cea47b032f605401222964ae1b7134c6ef620 100644 --- a/ecmascript/compiler/gate.cpp +++ b/ecmascript/compiler/gate.cpp @@ -14,6 +14,8 @@ */ #include "ecmascript/compiler/gate.h" +#include +#include namespace panda::ecmascript::kungfu { void Gate::CheckNullInput() const @@ -79,6 +81,16 @@ void Gate::CheckGeneralState(size_t idx) const } } +void Gate::CheckState(size_t idx) const +{ + auto gatePtr = GetInGateConst(idx); + OpCode actual = gatePtr->GetOpCode(); + if ((actual != OpCode::STATE_ENTRY) && (!gatePtr->meta_->IsState())) { + CheckFailed("State input does not match (expected: actual:" + + GateMetaData::Str(actual) + ")", idx); + } +} + void Gate::CheckStateInput() const { size_t stateStart = 0; @@ -108,7 +120,7 @@ void Gate::CheckStateInput() const break; } if (needCheck) { - CheckGeneralState(idx); + CheckState(idx); } } } @@ -164,7 +176,7 @@ void Gate::CheckValueInput(bool isArch64) const case OpCode::OBJECT_TYPE_CHECK: case OpCode::LOAD_ELEMENT: case OpCode::STORE_ELEMENT: - if (idx == valueStart + 1) { // 1: idx 1 + if (idx == valueStart) { // 1: idx 1 CheckInputMachineType(idx, MachineType::I64, isArch64); } break; @@ -187,6 +199,8 @@ void Gate::CheckDependInput() const for (size_t idx = dependStart; idx < dependEnd; idx++) { if (GetInGateConst(idx)->GetDependCount() == 0 && GetInGateConst(idx)->GetOpCode() != OpCode::DEPEND_ENTRY) { + LOG_COMPILER(ERROR) << "depend in of " << GetId() << GateMetaData::Str(GetOpCode()) << "is " + << GetInGateConst(idx)->GetId() << GateMetaData::Str(GetInGateConst(idx)->GetOpCode()); CheckFailed("Depend input is side-effect free", idx); } } @@ -710,15 +724,19 @@ std::string Gate::GateTypeStr(GateType gateType) const std::string(", L=") + std::to_string(l) + std::string(")"); } -void Gate::Print(std::string bytecode, bool inListPreview, size_t highlightIdx) const +void Gate::Print(std::string additionOp, bool inListPreview, size_t highlightIdx) const { auto opcode = GetOpCode(); if (opcode != OpCode::NOP && opcode != OpCode::DEAD) { std::string log("{\"id\":" + std::to_string(id_) + ", \"op\":\"" + GateMetaData::Str(opcode) + "\", "); - log += ((bytecode.compare("") == 0) ? "" : "\"bytecode\":\"") + bytecode; - log += ((bytecode.compare("") == 0) ? "" : "\", "); + std::string additionOpName = (opcode == OpCode::JS_BYTECODE) ? "bytecode" : "typedop"; + log += ((additionOp.compare("") == 0) ? "" : "\"" + additionOpName + "\":\"") + additionOp; + log += ((additionOp.compare("") == 0) ? "" : "\", "); log += "\"MType\":\"" + MachineTypeStr(GetMachineType()) + ", "; - log += "bitfield=" + std::to_string(TryGetValue()) + ", "; + + std::ostringstream oss; + oss << std::hex << TryGetValue(); + log += "bitfield=0x" + oss.str() + ", "; log += "type=" + GateTypeStr(type_) + ", "; log += "stamp=" + std::to_string(static_cast(stamp_)) + ", "; log += "mark=" + std::to_string(static_cast(mark_)) + ", "; @@ -774,7 +792,9 @@ void Gate::ShortPrint(std::string bytecode, bool inListPreview, size_t highlight log += ((bytecode.compare("") == 0) ? "" : "bytecode=") + bytecode; log += ((bytecode.compare("") == 0) ? "" : ", "); log += "\"MType\"=\"" + MachineTypeStr(GetMachineType()) + ", "; - log += "bitfield=" + std::to_string(TryGetValue()) + ", "; + std::ostringstream oss; + oss << std::hex << TryGetValue(); + log += "bitfield=0x" + oss.str() + ", "; log += "type=" + GateTypeStr(type_) + ", "; log += "\", in=["; @@ -839,9 +859,9 @@ size_t Gate::PrintInGate(size_t numIns, size_t idx, size_t size, bool inListPrev return idx; } -void Gate::PrintByteCode(std::string bytecode) const +void Gate::PrintGateWithAdditionOp(std::string additionOp) const { - Print(bytecode); + Print(additionOp); } MarkCode Gate::GetMark(TimeStamp stamp) const diff --git a/ecmascript/compiler/gate.h b/ecmascript/compiler/gate.h index 0f6b0a0f7d0b812c26de4945b1e543e4e4863a83..82a302ae985c30751a9ac769f139a44090172908 100644 --- a/ecmascript/compiler/gate.h +++ b/ecmascript/compiler/gate.h @@ -26,7 +26,10 @@ #include #include -#include "ecmascript/compiler/gate_meta_data.h" +#include "ecmascript/compiler/share_gate_meta_data.h" +#include "ecmascript/compiler/hcr_gate_meta_data.h" +#include "ecmascript/compiler/mcr_gate_meta_data.h" +#include "ecmascript/compiler/lcr_gate_meta_data.h" #include "ecmascript/compiler/type.h" #include "libpandabase/macros.h" @@ -181,7 +184,7 @@ public: void ShortPrint(std::string bytecode = "", bool inListPreview = false, size_t highlightIdx = -1) const; size_t PrintInGate(size_t numIns, size_t idx, size_t size, bool inListPreview, size_t highlightIdx, std::string &log, bool isEnd = false) const; - void PrintByteCode(std::string bytecode) const; + void PrintGateWithAdditionOp(std::string additionOp) const; void CheckNullInput() const; void CheckStateInput() const; void CheckValueInput(bool isArch64) const; @@ -222,9 +225,14 @@ public: return OneParameterMetaData::Cast(meta_); } - const TypedBinaryMegaData* GetTypedBinaryMegaData() const + const TypedBinaryMetaData* GetTypedBinaryMetaData() const { - return TypedBinaryMegaData::Cast(meta_); + return TypedBinaryMetaData::Cast(meta_); + } + + const TypedCallTargetCheckMetaData* GetTypedCallTargetCheckMetaData() const + { + return TypedCallTargetCheckMetaData::Cast(meta_); } const StringMetaData* GetStringMetaData() const @@ -243,6 +251,11 @@ public: return BoolMetaData::Cast(meta_); } + const TypedCallMetaData* GetTypedCallMetaData() const + { + return TypedCallMetaData::Cast(meta_); + } + std::string MachineTypeStr(MachineType machineType) const; std::string GateTypeStr(GateType gateType) const; ~Gate() = default; @@ -253,6 +266,7 @@ private: void CheckInputOpcode(size_t idx, OpCode expected) const; void CheckInputMachineType(size_t idx, MachineType expected, bool isArch64) const; void CheckNotInputMachineType(size_t idx, MachineType notExpected) const; + void CheckState(size_t idx) const; void CheckGeneralState(size_t idx) const; void CheckFailed(std::string errorString, size_t highlightIdx) const; void SetMetaData(const GateMetaData* meta) diff --git a/ecmascript/compiler/gate_accessor.cpp b/ecmascript/compiler/gate_accessor.cpp index b7531811e87d5ffd87462f39e2333c37a32f3af4..0f4360845fc80b938d21c0e908c63f894c4e15ec 100644 --- a/ecmascript/compiler/gate_accessor.cpp +++ b/ecmascript/compiler/gate_accessor.cpp @@ -48,6 +48,11 @@ bool GateAccessor::IsVisited(GateRef gate) const return GetMark(gate) == MarkCode::VISITED; } +bool GateAccessor::IsPrevisit(GateRef gate) const +{ + return GetMark(gate) == MarkCode::PREVISIT; +} + bool GateAccessor::IsNotMarked(GateRef gate) const { return GetMark(gate) == MarkCode::NO_MARK; @@ -63,6 +68,11 @@ void GateAccessor::SetVisited(GateRef gate) SetMark(gate, MarkCode::VISITED); } +void GateAccessor::SetPrevisit(GateRef gate) +{ + SetMark(gate, MarkCode::PREVISIT); +} + OpCode GateAccessor::GetOpCode(GateRef gate) const { Gate *gatePtr = circuit_->LoadGatePtr(gate); @@ -97,14 +107,58 @@ size_t GateAccessor::GetOffset(GateRef gate) const return gatePtr->GetOneParameterMetaData()->GetValue(); } +uint32_t GateAccessor::GetTrueWeight(GateRef gate) const +{ + ASSERT(GetOpCode(gate) == OpCode::IF_BRANCH); + Gate *gatePtr = circuit_->LoadGatePtr(gate); + auto accessor = BranchAccessor(gatePtr->GetOneParameterMetaData()->GetValue()); + return accessor.GetTrueWeight(); +} + +uint32_t GateAccessor::GetFalseWeight(GateRef gate) const +{ + ASSERT(GetOpCode(gate) == OpCode::IF_BRANCH); + Gate *gatePtr = circuit_->LoadGatePtr(gate); + auto accessor = BranchAccessor(gatePtr->GetOneParameterMetaData()->GetValue()); + return accessor.GetFalseWeight(); +} + +bool GateAccessor::HasBranchWeight(GateRef gate) const +{ + ASSERT(GetOpCode(gate) == OpCode::IF_BRANCH); + Gate *gatePtr = circuit_->LoadGatePtr(gate); + auto accessor = BranchAccessor(gatePtr->GetOneParameterMetaData()->GetValue()); + return (accessor.GetTrueWeight() != 0) || (accessor.GetFalseWeight() != 0); +} + size_t GateAccessor::GetIndex(GateRef gate) const { ASSERT(GetOpCode(gate) == OpCode::GET_GLOBAL_ENV_OBJ_HCLASS || - GetOpCode(gate) == OpCode::GET_GLOBAL_CONSTANT_VALUE); + GetOpCode(gate) == OpCode::GET_GLOBAL_CONSTANT_VALUE || + GetOpCode(gate) == OpCode::GET_GLOBAL_ENV_OBJ); Gate *gatePtr = circuit_->LoadGatePtr(gate); return gatePtr->GetOneParameterMetaData()->GetValue(); } +uint32_t GateAccessor::GetArraySize(GateRef gate) const +{ + ASSERT(GetOpCode(gate) == OpCode::CREATE_ARRAY || + GetOpCode(gate) == OpCode::CREATE_ARRAY_WITH_BUFFER); + Gate *gatePtr = circuit_->LoadGatePtr(gate); + auto array = gatePtr->GetOneParameterMetaData()->GetValue(); + return ArrayMetaDataAccessor(array).GetArrayLength(); +} + +void GateAccessor::SetArraySize(GateRef gate, uint32_t size) +{ + ASSERT(GetOpCode(gate) == OpCode::CREATE_ARRAY || + GetOpCode(gate) == OpCode::CREATE_ARRAY_WITH_BUFFER); + Gate *gatePtr = circuit_->LoadGatePtr(gate); + ArrayMetaDataAccessor accessor(gatePtr->GetOneParameterMetaData()->GetValue()); + accessor.SetArrayLength(size); + const_cast(gatePtr->GetOneParameterMetaData())->SetValue(accessor.ToValue()); +} + TypedUnaryAccessor GateAccessor::GetTypedUnAccessor(GateRef gate) const { ASSERT((GetOpCode(gate) == OpCode::TYPED_UNARY_OP)); @@ -112,6 +166,12 @@ TypedUnaryAccessor GateAccessor::GetTypedUnAccessor(GateRef gate) const return TypedUnaryAccessor(gatePtr->GetOneParameterMetaData()->GetValue()); } +TypedBinaryAccessor GateAccessor::GetTypedBinaryAccessor(GateRef gate) const +{ + Gate *gatePtr = circuit_->LoadGatePtr(gate); + return TypedBinaryAccessor(gatePtr->GetOneParameterMetaData()->GetValue()); +} + TypedJumpAccessor GateAccessor::GetTypedJumpAccessor(GateRef gate) const { ASSERT(GetOpCode(gate) == OpCode::TYPED_CONDITION_JUMP); @@ -119,6 +179,41 @@ TypedJumpAccessor GateAccessor::GetTypedJumpAccessor(GateRef gate) const return TypedJumpAccessor(gatePtr->GetOneParameterMetaData()->GetValue()); } +ArrayMetaDataAccessor GateAccessor::GetArrayMetaDataAccessor(GateRef gate) const +{ + ASSERT(GetOpCode(gate) == OpCode::STABLE_ARRAY_CHECK || + GetOpCode(gate) == OpCode::HCLASS_STABLE_ARRAY_CHECK || + GetOpCode(gate) == OpCode::CREATE_ARRAY || + GetOpCode(gate) == OpCode::CREATE_ARRAY_WITH_BUFFER); + Gate *gatePtr = circuit_->LoadGatePtr(gate); + return ArrayMetaDataAccessor(gatePtr->GetOneParameterMetaData()->GetValue()); +} + +ObjectTypeAccessor GateAccessor::GetObjectTypeAccessor(GateRef gate) const +{ + ASSERT(GetOpCode(gate) == OpCode::OBJECT_TYPE_CHECK || + GetOpCode(gate) == OpCode::OBJECT_TYPE_COMPARE); + Gate *gatePtr = circuit_->LoadGatePtr(gate); + return ObjectTypeAccessor(gatePtr->GetOneParameterMetaData()->GetValue()); +} + +bool GateAccessor::TypedOpIsTypedArray(GateRef gate, TypedOpKind kind) const +{ + switch (kind) { + case TypedOpKind::TYPED_LOAD_OP: { + TypedLoadOp op = GetTypedLoadOp(gate); + return TypedLoadOp::TYPED_ARRAY_FIRST <= op && op <=TypedLoadOp::TYPED_ARRAY_LAST; + } + case TypedOpKind::TYPED_STORE_OP: { + TypedStoreOp op = GetTypedStoreOp(gate); + return TypedStoreOp::TYPED_ARRAY_FIRST <= op && op <= TypedStoreOp::TYPED_ARRAY_LAST; + } + default: + LOG_ECMA(FATAL) << "this branch is unreachable"; + UNREACHABLE(); + } +} + TypedLoadOp GateAccessor::GetTypedLoadOp(GateRef gate) const { ASSERT(GetOpCode(gate) == OpCode::LOAD_ELEMENT); @@ -133,18 +228,32 @@ TypedStoreOp GateAccessor::GetTypedStoreOp(GateRef gate) const return static_cast(gatePtr->GetOneParameterMetaData()->GetValue()); } +TypedCallTargetCheckOp GateAccessor::GetTypedCallTargetCheckOp(GateRef gate) const +{ + ASSERT(GetOpCode(gate) == OpCode::TYPED_CALLTARGETCHECK_OP); + Gate *gatePtr = circuit_->LoadGatePtr(gate); + return gatePtr->GetTypedCallTargetCheckMetaData()->GetTypedCallTargetCheckOp(); +} + +MemoryType GateAccessor::GetMemoryType(GateRef gate) const +{ + ASSERT(GetOpCode(gate) == OpCode::STORE_MEMORY); + Gate *gatePtr = circuit_->LoadGatePtr(gate); + return static_cast(gatePtr->GetOneParameterMetaData()->GetValue()); +} + TypedBinOp GateAccessor::GetTypedBinaryOp(GateRef gate) const { ASSERT(GetOpCode(gate) == OpCode::TYPED_BINARY_OP); Gate *gatePtr = circuit_->LoadGatePtr(gate); - return gatePtr->GetTypedBinaryMegaData()->GetTypedBinaryOp(); + return gatePtr->GetTypedBinaryMetaData()->GetTypedBinaryOp(); } PGOSampleType GateAccessor::GetTypedBinaryType(GateRef gate) const { ASSERT(GetOpCode(gate) == OpCode::TYPED_BINARY_OP); Gate *gatePtr = circuit_->LoadGatePtr(gate); - return gatePtr->GetTypedBinaryMegaData()->GetType(); + return gatePtr->GetTypedBinaryMetaData()->GetType(); } bool GateAccessor::HasNumberType(GateRef gate) const @@ -163,22 +272,65 @@ bool GateAccessor::HasNumberType(GateRef gate) const return false; } +bool GateAccessor::HasStringType(GateRef gate) const +{ + // PGO has not collected string type yet, so skip the check for whether the sampleType is string. + GateType leftType = GetLeftType(gate); + GateType rightType = GetRightType(gate); + if (leftType.IsStringType() && rightType.IsStringType()) { + return true; + } + return false; +} + +// Include number, undefined, null and boolean type. +bool GateAccessor::HasPrimitiveNumberType(GateRef gate) const +{ + auto sampleType = GetTypedBinaryType(gate); + if (sampleType.IsNumber()) { + return true; + } + if (sampleType.IsNone()) { + GateType leftType = GetLeftType(gate); + GateType rightType = GetRightType(gate); + if (leftType.IsPrimitiveNumberType() && rightType.IsPrimitiveNumberType()) { + return true; + } + } + return false; +} + +GlobalTSTypeRef GateAccessor::GetFuncGT(GateRef gate) const +{ + ASSERT(GetOpCode(gate) == OpCode::JSINLINETARGET_TYPE_CHECK); + Gate *gatePtr = circuit_->LoadGatePtr(gate); + auto value = static_cast((gatePtr->GetOneParameterMetaData()->GetValue())); + return GlobalTSTypeRef(value); +} + GateType GateAccessor::GetParamGateType(GateRef gate) const { ASSERT(GetOpCode(gate) == OpCode::PRIMITIVE_TYPE_CHECK || - GetOpCode(gate) == OpCode::OBJECT_TYPE_CHECK || GetOpCode(gate) == OpCode::TYPED_ARRAY_CHECK || GetOpCode(gate) == OpCode::INDEX_CHECK || - GetOpCode(gate) == OpCode::JSCALLTARGET_TYPE_CHECK || - GetOpCode(gate) == OpCode::JSCALLTHISTARGET_TYPE_CHECK || - GetOpCode(gate) == OpCode::JSFASTCALLTARGET_TYPE_CHECK || - GetOpCode(gate) == OpCode::JSFASTCALLTHISTARGET_TYPE_CHECK || - GetOpCode(gate) == OpCode::JSCALLTARGET_FROM_DEFINEFUNC_CHECK); + GetOpCode(gate) == OpCode::TYPED_CALLTARGETCHECK_OP || + GetOpCode(gate) == OpCode::CREATE_ARRAY_WITH_BUFFER || + GetOpCode(gate) == OpCode::TYPE_OF_CHECK || + GetOpCode(gate) == OpCode::TYPE_OF); Gate *gatePtr = circuit_->LoadGatePtr(gate); GateTypeAccessor accessor(gatePtr->GetOneParameterMetaData()->GetValue()); return accessor.GetGateType(); } +bool GateAccessor::IsConvertSupport(GateRef gate) const +{ + ASSERT(GetOpCode(gate) == OpCode::CONVERT || + GetOpCode(gate) == OpCode::CHECK_AND_CONVERT); + Gate *gatePtr = circuit_->LoadGatePtr(gate); + ValuePairTypeAccessor accessor(gatePtr->GetOneParameterMetaData()->GetValue()); + return accessor.IsConvertSupport(); +} + ValueType GateAccessor::GetSrcType(GateRef gate) const { ASSERT(GetOpCode(gate) == OpCode::CONVERT || @@ -216,6 +368,22 @@ GateType GateAccessor::GetRightType(GateRef gate) const return accessor.GetRightType(); } +uint32_t GateAccessor::GetFirstValue(GateRef gate) const +{ + ASSERT(GetOpCode(gate) == OpCode::RANGE_GUARD); + Gate *gatePtr = circuit_->LoadGatePtr(gate); + UInt32PairAccessor accessor(gatePtr->GetOneParameterMetaData()->GetValue()); + return accessor.GetFirstValue(); +} + +uint32_t GateAccessor::GetSecondValue(GateRef gate) const +{ + ASSERT(GetOpCode(gate) == OpCode::RANGE_GUARD); + Gate *gatePtr = circuit_->LoadGatePtr(gate); + UInt32PairAccessor accessor(gatePtr->GetOneParameterMetaData()->GetValue()); + return accessor.GetSecondValue(); +} + size_t GateAccessor::GetVirtualRegisterIndex(GateRef gate) const { ASSERT(GetOpCode(gate) == OpCode::SAVE_REGISTER || @@ -242,14 +410,33 @@ bool GateAccessor::IsVtable(GateRef gate) const { ASSERT(GetOpCode(gate) == OpCode::LOAD_PROPERTY); Gate *gatePtr = circuit_->LoadGatePtr(gate); - return gatePtr->GetBoolMetaData()->getBool(); + return gatePtr->GetBoolMetaData()->GetBool(); +} + +bool GateAccessor::GetNoGCFlag(GateRef gate) const +{ + if (gate == Circuit::NullGate()) { + return false; + } + OpCode op = GetOpCode(gate); + if (op != OpCode::TYPEDCALL && op != OpCode::TYPEDFASTCALL) { + return false; + } + return TypedCallIsNoGC(gate); +} + +bool GateAccessor::TypedCallIsNoGC(GateRef gate) const +{ + ASSERT(GetOpCode(gate) == OpCode::TYPEDCALL || GetOpCode(gate) == OpCode::TYPEDFASTCALL); + Gate *gatePtr = circuit_->LoadGatePtr(gate); + return gatePtr->GetTypedCallMetaData()->IsNoGC(); } -bool GateAccessor::IsEmptyArray(GateRef gate) const +bool GateAccessor::IsNoGC(GateRef gate) const { - ASSERT(GetOpCode(gate) == OpCode::CREATE_ARRAY); + ASSERT(GetOpCode(gate) == OpCode::CALL_OPTIMIZED || GetOpCode(gate) == OpCode::FAST_CALL_OPTIMIZED); Gate *gatePtr = circuit_->LoadGatePtr(gate); - return gatePtr->GetBoolMetaData()->getBool(); + return gatePtr->GetBoolMetaData()->GetBool(); } uint32_t GateAccessor::TryGetPcOffset(GateRef gate) const @@ -261,11 +448,12 @@ uint32_t GateAccessor::TryGetPcOffset(GateRef gate) const return gatePtr->GetJSBytecodeMetaData()->GetPcOffset(); case OpCode::TYPED_CALL_BUILTIN: case OpCode::CONSTRUCT: - case OpCode::TYPEDCALL: - case OpCode::TYPEDFASTCALL: case OpCode::CALL_GETTER: case OpCode::CALL_SETTER: return static_cast(gatePtr->GetOneParameterMetaData()->GetValue()); + case OpCode::TYPEDCALL: + case OpCode::TYPEDFASTCALL: + return static_cast(gatePtr->GetTypedCallMetaData()->GetValue()); case OpCode::FRAME_STATE: { UInt32PairAccessor accessor(gatePtr->GetOneParameterMetaData()->GetValue()); return accessor.GetFirstValue(); @@ -276,6 +464,48 @@ uint32_t GateAccessor::TryGetPcOffset(GateRef gate) const return 0; } +uint32_t GateAccessor::TryGetMethodOffset(GateRef gate) const +{ + Gate *gatePtr = circuit_->LoadGatePtr(gate); + OpCode op = GetOpCode(gate); + switch (op) { + case OpCode::FRAME_ARGS: { + UInt32PairAccessor accessor(gatePtr->GetOneParameterMetaData()->GetValue()); + return accessor.GetFirstValue(); + } + default: + break; + } + return 0; +} + +GateRef GateAccessor::GetFrameArgs(GateRef gate) const +{ + if (!HasFrameState(gate)) { + return Circuit::NullGate(); + } + if (GetOpCode(gate) == OpCode::FRAME_STATE) { + return GetValueIn(gate, 0); // 0: frame args + } + GateRef frameState = GetFrameState(gate); + OpCode op = GetOpCode(frameState); + if (op == OpCode::FRAME_ARGS) { + return frameState; + } + if (op == OpCode::FRAME_STATE) { + return GetValueIn(frameState, 0); // 0: frame args + } + return Circuit::NullGate(); +} + +void GateAccessor::UpdateMethodOffset(GateRef gate, uint32_t methodOffset) +{ + ASSERT(GetOpCode(gate) == OpCode::FRAME_ARGS); + Gate *gatePtr = circuit_->LoadGatePtr(gate); + UInt32PairAccessor accessor(methodOffset, 0); + const_cast(gatePtr->GetOneParameterMetaData())->SetValue(accessor.ToValue()); +} + PGOSampleType GateAccessor::TryGetPGOType(GateRef gate) const { Gate *gatePtr = circuit_->LoadGatePtr(gate); @@ -283,6 +513,9 @@ PGOSampleType GateAccessor::TryGetPGOType(GateRef gate) const if (op == OpCode::JS_BYTECODE) { return gatePtr->GetJSBytecodeMetaData()->GetType(); } + if (op == OpCode::TYPED_BINARY_OP) { + return GetTypedBinaryType(gate); + } return PGOSampleType::NoneType(); } @@ -295,6 +528,43 @@ void GateAccessor::TrySetPGOType(GateRef gate, PGOSampleType type) } } +ElementsKind GateAccessor::TryGetElementsKind(GateRef gate) const +{ + Gate *gatePtr = circuit_->LoadGatePtr(gate); + OpCode op = GetOpCode(gate); + if (op == OpCode::JS_BYTECODE) { + return Elements::FixElementsKind(gatePtr->GetJSBytecodeMetaData()->GetElementsKind()); + } + return ElementsKind::GENERIC; +} + +ElementsKind GateAccessor::TryGetArrayElementsKind(GateRef gate) const +{ + Gate *gatePtr = circuit_->LoadGatePtr(gate); + OpCode op = GetOpCode(gate); + if (op == OpCode::JS_BYTECODE) { + ElementsKind kind = gatePtr->GetJSBytecodeMetaData()->GetElementsKind(); + if (Elements::IsGeneric(kind)) { + return kind; + } + std::vector kinds = gatePtr->GetJSBytecodeMetaData()->GetElementsKinds(); + for (auto &x : kinds) { + kind = Elements::MergeElementsKind(kind, x); + } + return kind; + } + return ElementsKind::GENERIC; +} + +void GateAccessor::TrySetElementsKind(GateRef gate, ElementsKind kind) +{ + Gate *gatePtr = circuit_->LoadGatePtr(gate); + OpCode op = GetOpCode(gate); + if (op == OpCode::JS_BYTECODE) { + const_cast(gatePtr->GetJSBytecodeMetaData())->SetElementsKind(kind); + } +} + EcmaOpcode GateAccessor::GetByteCodeOpcode(GateRef gate) const { ASSERT(GetOpCode(gate) == OpCode::JS_BYTECODE); @@ -473,6 +743,39 @@ bool GateAccessor::IsSelector(GateRef g) const return (op == OpCode::VALUE_SELECTOR) || (op == OpCode::DEPEND_SELECTOR); } +bool GateAccessor::IsFrameValues(GateRef g) const +{ + auto op = GetOpCode(g); + return op == OpCode::FRAME_VALUES; +} + +bool GateAccessor::IsIn(GateRef g, GateRef in) const +{ + size_t n = GetNumIns(g); + for (size_t id = 0; id < n; id++) { + GateRef i = GetIn(g, id); + if (i == in) { + return true; + } + } + return false; +} + +bool GateAccessor::IsSimpleState(GateRef g) const +{ + auto op = GetOpCode(g); + return (op == OpCode::IF_TRUE || + op == OpCode::IF_FALSE || + op == OpCode::SWITCH_CASE || + op == OpCode::DEFAULT_CASE || + op == OpCode::LOOP_BACK || + op == OpCode::MERGE || + op == OpCode::VALUE_SELECTOR || + op == OpCode::DEPEND_SELECTOR || + op == OpCode::DEPEND_RELAY || + op == OpCode::ORDINARY_BLOCK); +} + bool GateAccessor::IsControlCase(GateRef gate) const { return circuit_->IsControlCase(gate); @@ -564,6 +867,11 @@ bool GateAccessor::IsGeneralState(GateRef gate) const return GetMetaData(gate)->IsGeneralState(); } +bool GateAccessor::IsIfOrSwitchRelated(GateRef gate) const +{ + return GetMetaData(gate)->IsIfOrSwitchRelated(); +} + GateRef GateAccessor::GetDep(GateRef gate, size_t idx) const { Gate *gatePtr = circuit_->LoadGatePtr(gate); @@ -748,61 +1056,8 @@ void GateAccessor::ReplaceHirAndDeleteIfException(GateRef hirGate, // delete old gate DeleteGate(hirGate); if (ifException != Circuit::NullGate()) { - GraphEditor::RemoveDeadState(circuit_, ifException); - } -} - -void GateAccessor::EliminateRedundantPhi() -{ - std::vector gateList; - GetAllGates(gateList); - std::queue workList; - std::set inList; - for (auto gate : gateList) { - if (IsValueSelector(gate)) { - workList.push(gate); - inList.insert(gate); - } - } - - while (!workList.empty()) { - auto cur = workList.front(); - workList.pop(); - ASSERT(IsValueSelector(cur)); - GateRef first = GetValueIn(cur, 0); - bool sameIns = true; - bool selfUse = first == cur; - auto valueNum = GetNumValueIn(cur); - for (size_t i = 1; i < valueNum; ++i) { - GateRef input = GetValueIn(cur, i); - if (input != first) { - sameIns = false; - } - if (input == cur) { - ASSERT(IsLoopHead(GetState(cur))); - selfUse = true; - } - } - if ((!sameIns) && (!selfUse)) { - inList.erase(cur); - continue; - } - auto use = Uses(cur); - for (auto it = use.begin(); it != use.end(); ++it) { - if (((*it) == cur) || (!IsValueSelector(*it)) || inList.count(*it)) { - // selfUse or notPhi or inListPhi - continue; - } - workList.push(*it); - inList.insert(*it); - } - UpdateAllUses(cur, first); - } - for (auto phi : inList) { - ASSERT(IsValueSelector(phi)); - DeleteGate(phi); + ReplaceGate(ifException, circuit_->DeadGate()); } - return; } UseIterator GateAccessor::DeleteGate(const UseIterator &useIt) @@ -1038,6 +1293,146 @@ void GateAccessor::ReplaceGate(GateRef gate, GateRef state, GateRef depend, Gate DeleteGate(gate); } +void GateAccessor::ReplaceGate(GateRef gate, GateRef replacement) +{ + GateRef depend = Circuit::NullGate(); + if (GetDependCount(gate) > 0) { + ASSERT(GetDependCount(gate) == 1); // 1: one dep + depend = GetDep(gate); + } + GateRef state = Circuit::NullGate(); + if (GetStateCount(gate) > 0) { + ASSERT(GetStateCount(gate) == 1); // 1: one state + state = GetState(gate); + } + return ReplaceGate(gate, StateDepend {state, depend}, replacement); +} + +void GateAccessor::ReplaceGate(GateRef gate, StateDepend stateDepend, GateRef replacement) +{ + ASSERT(gate != replacement); + auto state = stateDepend.State(); + auto depend = stateDepend.Depend(); + auto uses = Uses(gate); + for (auto it = uses.begin(); it != uses.end();) { + if (IsStateIn(it)) { + ASSERT(state != Circuit::NullGate()); + it = ReplaceIn(it, state); + } else if (IsDependIn(it)) { + ASSERT(depend != Circuit::NullGate()); + it = ReplaceIn(it, depend); + } else { + it = ReplaceIn(it, replacement); + } + } + DeleteGate(gate); +} + + +// When Insert newGate, all the stateIn from state and dependIn from depend can be replaced to newGate +void GateAccessor::ReplaceInAfterInsert(GateRef state, GateRef depend, GateRef newGate) +{ + auto uses = Uses(state); + for (auto useIt = uses.begin(); useIt != uses.end();) { + if (IsStateIn(useIt) && (*useIt != newGate)) { + ASSERT(newGate != Circuit::NullGate()); + // Exception, for example, IF_TRUE / IF_FALSE -> DEPEND_RELAY, + // or LOOP_BEGIN / MERGE -> DEPEND_SELECTOR cannot be replaced + if (!IsState(*useIt)) { + useIt++; + continue; + } + useIt = ReplaceIn(useIt, newGate); + } else { + useIt++; + } + } + + uses = Uses(depend); + for (auto useIt = uses.begin(); useIt != uses.end();) { + if (IsDependIn(useIt) && (*useIt != newGate)) { + ASSERT(newGate != Circuit::NullGate()); + if (!IsState(*useIt)) { + useIt++; + continue; + } + useIt = ReplaceIn(useIt, newGate); + } else { + useIt++; + } + } +} + +// When loopExit, find stateSplit after DEPEND_SELECTOR +void GateAccessor::GetFrameStateDependIn(GateRef gate, GateRef &dependIn) +{ + auto uses = Uses(gate); + size_t stateSplitCount = 0; + GateRef stateSplit = Circuit::NullGate(); + for (auto it = uses.begin(); it != uses.end();) { + if (GetOpCode(*it) == OpCode::STATE_SPLIT) { + ASSERT(stateSplitCount < 1); // only one state Split; + stateSplitCount++; + stateSplit = *it; + break; + } else { + ++it; + } + } + + ASSERT(stateSplitCount <= 1); + if (stateSplitCount == 1 && stateSplit != Circuit::NullGate()) { + dependIn = stateSplit; + } +} + +// When ifOp or loopExit, insertAfter +// stateIn: IF_TRUE / IF_FALSE / MERGE +// dependIn: DEPEND_RELAY / DEPEND_SELECTOR, if stateSplit follow closely, after the stateSplit. + +void GateAccessor::GetStateInAndDependIn(GateRef insertAfter, GateRef &stateIn, GateRef &dependIn) +{ + if (GetOpCode(insertAfter) == OpCode::IF_TRUE || GetOpCode(insertAfter) == OpCode::IF_FALSE) { + auto uses = Uses(insertAfter); + for (auto it = uses.begin(); it != uses.end();) { + if (GetOpCode(*it) == OpCode::DEPEND_RELAY) { + stateIn = insertAfter; + dependIn = (*it); + break; + } else { + ++it; + } + } + } else if (GetOpCode(insertAfter) == OpCode::MERGE) { + auto uses = Uses(insertAfter); + for (auto it = uses.begin(); it != uses.end();) { + if (GetOpCode(*it) == OpCode::DEPEND_SELECTOR) { + stateIn = insertAfter; + dependIn = (*it); + GetFrameStateDependIn(*it, dependIn); + break; + } else { + ++it; + } + } + } + ASSERT(GetDependCount(dependIn) > 0); +} + +size_t GateAccessor::GetFrameDepth(GateRef gate, OpCode op) +{ + if (GetOpCode(gate) != op) { + return 0; + } + size_t depth = 0; + GateRef prev = GetFrameState(gate); + while ((GetOpCode(prev) == op)) { + depth++; + prev = GetFrameState(prev); + } + return depth; +} + GateRef GateAccessor::GetFrameState(GateRef gate) const { ASSERT(HasFrameState(gate)); @@ -1047,12 +1442,18 @@ GateRef GateAccessor::GetFrameState(GateRef gate) const } GateRef GateAccessor::FindNearestFrameState(GateRef gate) const +{ + auto statesplit = FindNearestStateSplit(gate); + return GetFrameState(statesplit); +} + +GateRef GateAccessor::FindNearestStateSplit(GateRef gate) const { auto statesplit = gate; while (GetOpCode(statesplit) != OpCode::STATE_SPLIT) { statesplit = GetDep(statesplit); } - return GetFrameState(statesplit); + return statesplit; } bool GateAccessor::HasFrameState(GateRef gate) const @@ -1145,6 +1546,11 @@ bool GateAccessor::IsNop(GateRef g) const return GetMetaData(g)->IsNop(); } +bool GateAccessor::IsDead(GateRef gate) const +{ + return GetMetaData(gate)->IsDead(); +} + bool GateAccessor::IsRoot(GateRef g) const { return GetMetaData(g)->IsRoot(); @@ -1195,4 +1601,63 @@ bool GateAccessor::HasIfExceptionUse(GateRef gate) const } return false; } + +bool GateAccessor::IsHeapObjectFromElementsKind(GateRef gate) +{ + OpCode opcode = GetOpCode(gate); + if (opcode == OpCode::JS_BYTECODE) { + auto bc = GetByteCodeOpcode(gate); + if (bc == EcmaOpcode::LDOBJBYVALUE_IMM8_V8 || bc == EcmaOpcode::LDOBJBYVALUE_IMM16_V8 || + bc == EcmaOpcode::LDTHISBYVALUE_IMM8 || bc == EcmaOpcode::LDTHISBYVALUE_IMM16) { + ElementsKind kind = TryGetElementsKind(gate); + return Elements::IsObject(kind); + } + return false; + } + + if (opcode == OpCode::LOAD_ELEMENT) { + TypedLoadOp typedOp = GetTypedLoadOp(gate); + return typedOp == TypedLoadOp::ARRAY_LOAD_OBJECT_ELEMENT; + } + + return false; +} + +bool GateAccessor::IsConstString(GateRef gate) +{ + OpCode op = GetOpCode(gate); + if (op == OpCode::JS_BYTECODE) { + EcmaOpcode ecmaOpcode = GetByteCodeOpcode(gate); + return ecmaOpcode == EcmaOpcode::LDA_STR_ID16; + } + return false; +} + +bool GateAccessor::IsSingleCharGate(GateRef gate) +{ + OpCode op = GetOpCode(gate); + if (op == OpCode::LOAD_ELEMENT) { + return GetTypedLoadOp(gate) == TypedLoadOp::STRING_LOAD_ELEMENT; + } + return false; +} + +uint32_t GateAccessor::GetStringIdFromLdaStrGate(GateRef gate) +{ + ASSERT(GetByteCodeOpcode(gate) == EcmaOpcode::LDA_STR_ID16); + GateRef stringId = GetValueIn(gate, 0); + return GetConstantValue(stringId); +} + +bool GateAccessor::IsLoopBackUse(const UseIterator &useIt) const +{ + if (IsStateIn(useIt)) { + return (useIt.GetIndex() == 1) && IsLoopHead(*useIt); + } + if ((IsValueSelector(*useIt) && IsValueIn(useIt)) || + (IsDependSelector(*useIt) && IsDependIn(useIt))) { + return (useIt.GetIndex() == 2) && IsLoopHead(GetState(*useIt)); // 2 means the Index + } + return false; +} } // namespace panda::ecmascript::kungfu diff --git a/ecmascript/compiler/gate_accessor.h b/ecmascript/compiler/gate_accessor.h index 254d5f921274a5e13dc030035e2e321a522a7c71..ec71db587cd2ce6374349f1731215d7b4254aa7e 100644 --- a/ecmascript/compiler/gate_accessor.h +++ b/ecmascript/compiler/gate_accessor.h @@ -17,8 +17,9 @@ #define ECMASCRIPT_COMPILER_GATE_ACCESSOR_H #include "ecmascript/compiler/circuit.h" -#include "ecmascript/compiler/gate_meta_data.h" -#include "ecmascript/pgo_profiler/pgo_profiler_type.h" +#include "ecmascript/compiler/mcr_gate_meta_data.h" +#include "ecmascript/elements.h" +#include "ecmascript/pgo_profiler/types/pgo_profiler_type.h" namespace panda::ecmascript::kungfu { @@ -380,23 +381,45 @@ public: ICmpCondition GetICmpCondition(GateRef gate) const; FCmpCondition GetFCmpCondition(GateRef gate) const; size_t GetOffset(GateRef gate) const; + uint32_t GetTrueWeight(GateRef gate) const; + uint32_t GetFalseWeight(GateRef gate) const; + bool HasBranchWeight(GateRef gate) const; size_t GetIndex(GateRef gate) const; + uint32_t GetArraySize(GateRef gate) const; + void SetArraySize(GateRef gate, uint32_t size); size_t GetVirtualRegisterIndex(GateRef gate) const; + bool TypedOpIsTypedArray(GateRef gate, TypedOpKind kind) const; TypedLoadOp GetTypedLoadOp(GateRef gate) const; TypedStoreOp GetTypedStoreOp(GateRef gate) const; + MemoryType GetMemoryType(GateRef gate) const; TypedBinOp GetTypedBinaryOp(GateRef gate) const; + TypedCallTargetCheckOp GetTypedCallTargetCheckOp(GateRef gate) const; PGOSampleType GetTypedBinaryType(GateRef gate) const; + bool HasPrimitiveNumberType(GateRef gate) const; bool HasNumberType(GateRef gate) const; + bool HasStringType(GateRef gate) const; + GlobalTSTypeRef GetFuncGT(GateRef gate) const; GateType GetParamGateType(GateRef gate) const; TypedUnaryAccessor GetTypedUnAccessor(GateRef gate) const; + TypedBinaryAccessor GetTypedBinaryAccessor(GateRef gate) const; TypedJumpAccessor GetTypedJumpAccessor(GateRef gate) const; + ArrayMetaDataAccessor GetArrayMetaDataAccessor(GateRef gate) const; + ObjectTypeAccessor GetObjectTypeAccessor(GateRef gate) const; uint64_t GetConstantValue(GateRef gate) const; const ChunkVector& GetConstantString(GateRef gate) const; bool IsVtable(GateRef gate) const; - bool IsEmptyArray(GateRef gate) const; + bool GetNoGCFlag(GateRef gate) const; + bool TypedCallIsNoGC(GateRef gate) const; + bool IsNoGC(GateRef gate) const; uint32_t TryGetPcOffset(GateRef gate) const; + uint32_t TryGetMethodOffset(GateRef gate) const; + GateRef GetFrameArgs(GateRef gate) const; + void UpdateMethodOffset(GateRef gate, uint32_t methodOffset); PGOSampleType TryGetPGOType(GateRef gate) const; void TrySetPGOType(GateRef gate, PGOSampleType type); + ElementsKind TryGetElementsKind(GateRef gate) const; + ElementsKind TryGetArrayElementsKind(GateRef gate) const; + void TrySetElementsKind(GateRef gate, ElementsKind kind); EcmaOpcode GetByteCodeOpcode(GateRef gate) const; void Print(GateRef gate) const; void ShortPrint(GateRef gate) const; @@ -411,6 +434,7 @@ public: UseIterator ReplaceIn(const UseIterator &useIt, GateRef replaceGate); // Add for lowering GateType GetGateType(GateRef gate) const; + bool IsConvertSupport(GateRef gate) const; ValueType GetSrcType(GateRef gate) const; ValueType GetDstType(GateRef gate) const; void SetGateType(GateRef gate, GateType gt); @@ -424,6 +448,9 @@ public: size_t GetInValueCount(GateRef gate) const; size_t GetInValueStarts(GateRef gate) const; void UpdateAllUses(GateRef gate, GateRef replaceValueIn); + void ReplaceInAfterInsert(GateRef state, GateRef depend, GateRef newGate); + void GetFrameStateDependIn(GateRef gate, GateRef &dependIn); + void GetStateInAndDependIn(GateRef insertAfter, GateRef &stateIn, GateRef &dependIn); void ReplaceIn(GateRef gate, size_t index, GateRef in); void ReplaceStateIn(GateRef gate, GateRef in, size_t index = 0); void ReplaceDependIn(GateRef gate, GateRef in, size_t index = 0); @@ -437,7 +464,9 @@ public: int GetInt32FromConstant(GateRef gate) const; bool IsInGateNull(GateRef gate, size_t idx) const; bool IsSelector(GateRef g) const; + bool IsSimpleState(GateRef g) const; bool IsValueSelector(GateRef g) const; + bool IsFrameValues(GateRef g) const; bool IsControlCase(GateRef gate) const; bool IsLoopExit(GateRef gate) const; bool IsLoopExitRelated(GateRef gate) const; @@ -461,8 +490,10 @@ public: void SetMark(GateRef gate, MarkCode mark); bool IsFinished(GateRef gate) const; bool IsVisited(GateRef gate) const; + bool IsPrevisit(GateRef gate) const; bool IsNotMarked(GateRef gate) const; void SetFinished(GateRef gate); + void SetPrevisit(GateRef gate); void SetVisited(GateRef gate); bool IsStateIn(const UseIterator &useIt) const; bool IsDependIn(const UseIterator &useIt) const; @@ -477,8 +508,12 @@ public: bool IsFrameStateIn(GateRef gate, size_t index) const; void EliminateRedundantPhi(); void ReplaceGate(GateRef gate, GateRef state, GateRef depend, GateRef value); + void ReplaceGate(GateRef gate, StateDepend stateDepend, GateRef replacement); + void ReplaceGate(GateRef gate, GateRef replacement); GateType GetLeftType(GateRef gate) const; GateType GetRightType(GateRef gate) const; + uint32_t GetFirstValue(GateRef gate) const; + uint32_t GetSecondValue(GateRef gate) const; GateRef GetGlueFromArgList() const; void GetArgsOuts(std::vector& outs) const; void GetReturnOuts(std::vector& outs) const; @@ -492,6 +527,12 @@ public: void DeleteGateIfNoUse(GateRef gate); GateRef GetDependSelectorFromMerge(GateRef gate); bool HasIfExceptionUse(GateRef gate) const; + bool IsIn(GateRef g, GateRef in) const; + bool IsHeapObjectFromElementsKind(GateRef gate); + bool IsConstString(GateRef gate); + bool IsSingleCharGate(GateRef gate); + uint32_t GetStringIdFromLdaStrGate(GateRef gate); + bool IsIfOrSwitchRelated(GateRef gate) const; GateRef GetCircuitRoot() const { @@ -523,16 +564,19 @@ public: return gate == GetStateRoot(); } + size_t GetFrameDepth(GateRef gate, OpCode op); GateRef GetFrameState(GateRef gate) const; void ReplaceFrameStateIn(GateRef gate, GateRef in); bool HasFrameState(GateRef gate) const; GateRef FindNearestFrameState(GateRef gate) const; + GateRef FindNearestStateSplit(GateRef gate) const; void SetMetaData(GateRef gate, const GateMetaData* meta); void ReplaceHirWithIfBranch(GateRef hirGate, StateDepend success, StateDepend exception, GateRef value); void ReplaceHirDirectly(GateRef hirGate, StateDepend replacement, GateRef value); void ReplaceHirAndDeleteIfException(GateRef hirGate, StateDepend replacement, GateRef value); + bool IsLoopBackUse(const UseIterator &useIt) const; private: const GateMetaData *GetMetaData(GateRef gate) const; UseIterator ReplaceHirIfSuccess(const UseIterator &useIt, GateRef state); diff --git a/ecmascript/compiler/gate_meta_data_builder.h b/ecmascript/compiler/gate_meta_data_builder.h index 972454ecccfe66daeabe32f0448cece34964e303..4a39d39e3fba073daa90f8a78b0774523e76fb21 100644 --- a/ecmascript/compiler/gate_meta_data_builder.h +++ b/ecmascript/compiler/gate_meta_data_builder.h @@ -19,6 +19,11 @@ #include #include "ecmascript/compiler/bytecodes.h" +#include "ecmascript/compiler/share_opcodes.h" +#include "ecmascript/compiler/share_gate_meta_data.h" +#include "ecmascript/compiler/lcr_gate_meta_data.h" +#include "ecmascript/compiler/mcr_gate_meta_data.h" +#include "ecmascript/compiler/hcr_gate_meta_data.h" #include "ecmascript/compiler/type.h" #include "ecmascript/mem/chunk.h" #include "ecmascript/mem/chunk_containers.h" @@ -112,6 +117,11 @@ public: GATE_META_DATA_LIST_WITH_PC_OFFSET(DECLARE_GATE_META) #undef DECLARE_GATE_META +#define DECLARE_GATE_META_FOR_CALL(NAME, OP, R, S, D, V) \ + const GateMetaData* NAME(uint64_t value, uint64_t pcOffset, bool noGC); + GATE_META_DATA_LIST_FOR_CALL(DECLARE_GATE_META_FOR_CALL) +#undef DECLARE_GATE_META_FOR_CALL + #define DECLARE_GATE_META(NAME, OP, R, S, D, V) \ const GateMetaData* NAME(uint64_t pcOffset) const; GATE_META_DATA_LIST_WITH_PC_OFFSET_FIXED_VALUE(DECLARE_GATE_META) @@ -122,6 +132,11 @@ public: GATE_META_DATA_LIST_WITH_BOOL(DECLARE_GATE_META) #undef DECLARE_GATE_META +#define DECLARE_GATE_META_WITH_BOOL_VALUE_IN(NAME, OP, R, S, D, V) \ + const GateMetaData* NAME(size_t value, bool flag); + GATE_META_DATA_LIST_WITH_BOOL_VALUE_IN(DECLARE_GATE_META_WITH_BOOL_VALUE_IN) +#undef DECLARE_GATE_META_WITH_BOOL_VALUE_IN + explicit GateMetaBuilder(Chunk* chunk); const GateMetaData* JSBytecode(size_t valuesIn, EcmaOpcode opcode, uint32_t pcOffset, GateFlags flags) @@ -131,7 +146,12 @@ public: const GateMetaData* TypedBinaryOp(uint64_t value, TypedBinOp binOp, PGOSampleType type) { - return new (chunk_) TypedBinaryMegaData(value, binOp, type); + return new (chunk_) TypedBinaryMetaData(value, binOp, type); + } + + const GateMetaData* TypedCallTargetCheckOp(uint32_t numIns, uint64_t value, TypedCallTargetCheckOp checkOp) + { + return new (chunk_) TypedCallTargetCheckMetaData(numIns, value, checkOp); } const GateMetaData* Nop() @@ -147,7 +167,7 @@ public: return meta; } - const GateMetaData* ConstString(const std::string &str) + const GateMetaData* ConstString(std::string_view str) { return new (chunk_) StringMetaData(chunk_, str); } diff --git a/ecmascript/compiler/graph_editor.cpp b/ecmascript/compiler/graph_editor.cpp index 920e9258b01dadb4a4a8b4dd7de8441833d02aba..25b0de9a8babdcaf35471303e21440cd86941394 100644 --- a/ecmascript/compiler/graph_editor.cpp +++ b/ecmascript/compiler/graph_editor.cpp @@ -26,6 +26,21 @@ void GraphEditor::RemoveDeadState(Circuit* circuit, GateRef gate) editor.RemoveGate(); } +void GraphEditor::EliminateRedundantPhi(Circuit* circuit, bool enableLog, const std::string& methodName) +{ + GraphEditor editor(circuit); + editor.EliminatePhi(); + if (enableLog) { + LOG_COMPILER(INFO) << " "; + LOG_COMPILER(INFO) << "\033[34m" << "=================" + << " After Redundant Phi Elimination " + << "[" << methodName << "] " + << "=================" << "\033[0m"; + circuit->PrintAllGatesWithBytecode(); + LOG_COMPILER(INFO) << "\033[34m" << "=========================== End ===========================" << "\033[0m"; + } +} + void GraphEditor::ReplaceGate(GateRef gate) { auto uses = acc_.Uses(gate); @@ -55,12 +70,10 @@ void GraphEditor::RemoveGate() case OpCode::DEPEND_SELECTOR: // dead op, just continue break; + case OpCode::LOOP_BEGIN: case OpCode::MERGE: PropagateMerge(edge); break; - case OpCode::LOOP_BACK: - PropagateLoopBack(edge); - break; default: PropagateGate(edge); break; @@ -111,25 +124,85 @@ void GraphEditor::PropagateMerge(const Edge& edge) } } -void GraphEditor::PropagateLoopBack(const Edge& edge) +void GraphEditor::EliminatePhi() { - GateRef gate = edge.GetGate(); + circuit_->AdvanceTime(); + std::vector gateList; + acc_.GetAllGates(gateList); + std::vector phis; + std::queue workList; + // nomarked phis are unused phis + // set previsit for phis in worklist + // set visited for used phis + // set finished for used gate which is self-use or has same inputs - auto uses = acc_.Uses(gate); - auto firstUse = uses.begin(); - if (firstUse == uses.end()) { - // LoopBegin => LoopBack - ReplaceGate(gate); - } else { - // maybe Merge => LoopBack - GateRef loopBegin = *firstUse; - GateRef otherStateIn = acc_.GetState(loopBegin, 0); + for (auto gate : gateList) { + if (acc_.IsValueSelector(gate)) { + phis.emplace_back(gate); + continue; + } + if (acc_.IsFrameValues(gate)) { + continue; + } + // get used phi + auto valueNum = acc_.GetNumValueIn(gate); + for (size_t i = 0; i < valueNum; ++i) { + GateRef input = acc_.GetValueIn(gate, i); + if (acc_.IsValueSelector(input) && acc_.IsNotMarked(input)) { + acc_.SetPrevisit(input); + workList.push(input); + } + } + } - firstUse = acc_.Uses(loopBegin).begin(); - acc_.ReplaceIn(*firstUse, firstUse.GetIndex(), otherStateIn); - ReplaceGate(gate); - acc_.DeleteGate(loopBegin); + // visit used phi + while (!workList.empty()) { + auto cur = workList.front(); + workList.pop(); + acc_.SetVisited(cur); + auto valueNum = acc_.GetNumValueIn(cur); + bool selfUse = false; + bool sameIns = true; + GateRef first = acc_.GetValueIn(cur, 0); + for (size_t i = 0; i < valueNum; ++i) { + GateRef input = acc_.GetValueIn(cur, i); + if (input == cur) { + selfUse = true; + } + if (input != first) { + sameIns = false; + } + if (acc_.IsValueSelector(input) && acc_.IsNotMarked(input)) { + workList.push(input); + acc_.SetPrevisit(input); + } + } + + if ((!sameIns) && (!selfUse)) { + continue; + } + + acc_.SetFinished(cur); + auto uses = acc_.Uses(cur); + for (auto it = uses.begin(); it != uses.end(); it++) { + if (acc_.IsValueSelector(*it) && acc_.IsVisited(*it)) { + // revisit user + acc_.SetPrevisit(*it); + workList.push(*it); + } + } + acc_.UpdateAllUses(cur, first); + acc_.DeleteGate(cur); } -} -} // namespace panda::ecmascript::kungfu \ No newline at end of file + // delete unused phi + for (auto phi : phis) { + if (acc_.IsValueSelector(phi) && acc_.IsNotMarked(phi)) { + auto optimizedGate = circuit_->GetConstantGate(MachineType::I64, + JSTaggedValue::VALUE_OPTIMIZED_OUT, GateType::TaggedValue()); + acc_.UpdateAllUses(phi, optimizedGate); + acc_.DeleteGate(phi); + } + } +} +} // namespace panda::ecmascript::kungfu diff --git a/ecmascript/compiler/graph_editor.h b/ecmascript/compiler/graph_editor.h index 2fec84bc8d30276f1eede4e6ea42af7a2bb9fafc..1ffa90cb77f05cb5cf28465336d1f0b484230e27 100644 --- a/ecmascript/compiler/graph_editor.h +++ b/ecmascript/compiler/graph_editor.h @@ -31,12 +31,13 @@ public: ~GraphEditor() = default; static void RemoveDeadState(Circuit* circuit, GateRef gate); + static void EliminateRedundantPhi(Circuit* circuit, bool enableLog, const std::string& methodName); private: void ReplaceGate(GateRef gate); void RemoveGate(); void PropagateGate(const Edge& edge); void PropagateMerge(const Edge& edge); - void PropagateLoopBack(const Edge& edge); + void EliminatePhi(); Circuit *circuit_ {nullptr}; GateAccessor acc_; @@ -44,4 +45,4 @@ private: ChunkVector workList_; }; } // panda::ecmascript::kungfu -#endif // ECMASCRIPT_COMPILER_GRAPH_VISITOR_H \ No newline at end of file +#endif // ECMASCRIPT_COMPILER_GRAPH_EDITOR_H diff --git a/ecmascript/compiler/graph_linearizer.cpp b/ecmascript/compiler/graph_linearizer.cpp index 0333f6131b161a1dc523b02fa75785c6e07e0d91..402a01414c7960ae2e96e5f524d1e103b409b6ef 100644 --- a/ecmascript/compiler/graph_linearizer.cpp +++ b/ecmascript/compiler/graph_linearizer.cpp @@ -14,6 +14,7 @@ */ #include "ecmascript/compiler/graph_linearizer.h" +#include "ecmascript/compiler/base/bit_set.h" #include "ecmascript/compiler/scheduler.h" namespace panda::ecmascript::kungfu { @@ -21,6 +22,7 @@ void GraphLinearizer::Run(ControlFlowGraph &result) { LinearizeGraph(); LinearizeRegions(result); + if (IsLogEnabled()) { LOG_COMPILER(INFO) << ""; LOG_COMPILER(INFO) << "\033[34m" @@ -59,7 +61,7 @@ public: auto state = acc_.GetState(fixedGate); auto region = linearizer_->FindPredRegion(state); linearizer_->AddFixedGateToRegion(fixedGate, region); - linearizer_->ScheduleGate(fixedGate, region); + linearizer_->BindGate(fixedGate, region); } } @@ -101,9 +103,14 @@ public: case OpCode::SWITCH_CASE: case OpCode::STATE_ENTRY: case OpCode::IF_EXCEPTION: - case OpCode::IF_SUCCESS: + case OpCode::IF_SUCCESS: { linearizer_->CreateGateRegion(gate); + if (linearizer_->onlyBB_) { + GateRegion* region = linearizer_->GateToRegion(gate); + currentRegion_ = region; + } break; + } case OpCode::LOOP_BACK: case OpCode::IF_BRANCH: case OpCode::SWITCH_BRANCH: @@ -117,8 +124,14 @@ public: endStateList_.emplace_back(gate); } break; - default: + default: { + if (linearizer_->onlyBB_) { + auto& info = linearizer_->GetGateInfo(gate); + info.region = currentRegion_; + linearizer_->BindGate(gate, currentRegion_); + } break; + } } } } @@ -150,6 +163,7 @@ private: ChunkDeque pendingList_; ChunkVector endStateList_; GateAccessor acc_; + GateRegion* currentRegion_; bool scheduleLIR_; }; @@ -306,6 +320,269 @@ private: ChunkVector> semiDomTree_; }; +struct LoopInfo { + GateRegion* loopHead {nullptr}; + BitSet* loopBodys {nullptr}; + ChunkVector* loopExits {nullptr}; + LoopInfo* outer {nullptr}; +}; + +class LoopInfoBuilder { +public: + explicit LoopInfoBuilder(GraphLinearizer *linearizer, Chunk* chunk) + : linearizer_(linearizer), pendingList_(chunk), + loops_(chunk), loopbacks_(chunk), chunk_(chunk), + dfsStack_(chunk), acc_(linearizer->circuit_) {} + + void Run() + { + ComputeLoopNumber(); + ComputeLoopInfo(); + ComputeLoopExit(); + ComputeLoopHeader(); + ComputeLoopDepth(); + if (linearizer_->IsLogEnabled()) { + for (size_t i = 0; i < numLoops_; i++) { + auto& loopInfo = loops_[i]; + PrintLoop(loopInfo); + } + } + } + + void PrintLoop(LoopInfo& loopInfo) + { + auto size = linearizer_->regionList_.size(); + LOG_COMPILER(INFO) << "--------------------------------- LoopInfo Start ---------------------------------"; + LOG_COMPILER(INFO) << "Head: " << acc_.GetId(loopInfo.loopHead->state_); + LOG_COMPILER(INFO) << "loopNumber: " << loopInfo.loopHead->loopNumber_; + LOG_COMPILER(INFO) << "Body: ["; + for (size_t i = 0; i < size; i++) { + if (loopInfo.loopBodys->TestBit(i)) { + auto current = linearizer_->regionList_.at(i)->state_; + LOG_COMPILER(INFO) << acc_.GetId(current) << ", "; + } + } + LOG_COMPILER(INFO) << "]"; + LOG_COMPILER(INFO) << "Exit: ["; + if (loopInfo.loopExits != nullptr) { + for (auto region : *loopInfo.loopExits) { + auto current = region->state_; + LOG_COMPILER(INFO) << acc_.GetId(current) << ", "; + } + } + LOG_COMPILER(INFO) << "]"; + LOG_COMPILER(INFO) << "--------------------------------- LoopInfo End ---------------------------------"; + } + + void ComputeLoopInfo() + { + auto size = linearizer_->regionList_.size(); + loops_.resize(numLoops_, LoopInfo()); + + for (auto curState : loopbacks_) { + GateRegion* curRegion = curState.region; + GateRegion* loopHead = curRegion->succs_[curState.index]; + auto loopNumber = loopHead->GetLoopNumber(); + auto& loopInfo = loops_[loopNumber]; + + if (loopInfo.loopHead == nullptr) { + loopInfo.loopHead = loopHead; + loopInfo.loopBodys = chunk_->New(chunk_, size); + } + if (curRegion != loopHead) { + loopInfo.loopBodys->SetBit(curRegion->GetId()); + pendingList_.emplace_back(curRegion); + } + PropagateLoopBody(loopInfo); + } + } + + void PropagateLoopBody(LoopInfo& loopInfo) + { + while (!pendingList_.empty()) { + auto curRegion = pendingList_.back(); + pendingList_.pop_back(); + for (auto pred : curRegion->preds_) { + if (pred != loopInfo.loopHead) { + if (!loopInfo.loopBodys->TestBit(pred->GetId())) { + loopInfo.loopBodys->SetBit(pred->GetId()); + pendingList_.emplace_back(pred); + } + } + } + } + } + + void ComputeLoopNumber() + { + auto size = linearizer_->regionList_.size(); + dfsStack_.resize(size, DFSState(nullptr, 0)); + linearizer_->circuit_->AdvanceTime(); + + auto entry = linearizer_->regionList_.front(); + auto currentDepth = Push(entry, 0); + while (currentDepth > 0) { + auto& curState = dfsStack_[currentDepth - 1]; // -1: for current + auto curRegion = curState.region; + auto index = curState.index; + + if (index == curRegion->succs_.size()) { + currentDepth--; + curRegion->SetFinished(acc_); + } else { + GateRegion* succ = curRegion->succs_[index]; + curState.index = ++index; + if (succ->IsFinished(acc_)) { + continue; + } + if (succ->IsUnvisited(acc_)) { + currentDepth = Push(succ, currentDepth); + } else { + ASSERT(succ->IsVisited(acc_)); + loopbacks_.emplace_back(DFSState(curRegion, index - 1)); // -1: for prev + if (!succ->HasLoopNumber()) { + succ->SetLoopNumber(numLoops_++); + } + } + } + } + } + + void ComputeLoopExit() + { + linearizer_->circuit_->AdvanceTime(); + auto entry = linearizer_->regionList_.front(); + LoopInfo *loopInfo = nullptr; + auto currentDepth = Push(entry, 0); + while (currentDepth > 0) { + auto &curState = dfsStack_[currentDepth - 1]; // -1: for current + auto curRegion = curState.region; + auto index = curState.index; + GateRegion* succ = nullptr; + if (index >= curRegion->succs_.size()) { + if (curRegion->HasLoopNumber()) { + if (curRegion->IsVisited(acc_)) { + ASSERT(loopInfo != nullptr && loopInfo->loopHead == curRegion); + loopInfo = loopInfo->outer; + } + } + curRegion->SetFinished(acc_); + currentDepth--; + } else { + succ = curRegion->succs_[index]; + curState.index = ++index; + if (!succ->IsUnvisited(acc_)) { + continue; + } + if (loopInfo != nullptr) { + if (!loopInfo->loopBodys->TestBit(succ->GetId())) { + AddLoopExit(succ, loopInfo); + } else { + succ->loopHead_ = loopInfo->loopHead; + } + } + currentDepth = Push(succ, currentDepth); + loopInfo = EnterInnerLoop(succ, loopInfo); + } + } + } + + void AddLoopExit(GateRegion* succ, LoopInfo *loopInfo) + { + if (loopInfo->loopExits == nullptr) { + loopInfo->loopExits = chunk_->New>(chunk_); + } + loopInfo->loopExits->emplace_back(succ); + } + + LoopInfo *EnterInnerLoop(GateRegion* succ, LoopInfo *loopInfo) + { + // enter inner loop + if (succ->HasLoopNumber()) { + auto& innerLoop = loops_[succ->GetLoopNumber()]; + innerLoop.outer = loopInfo; + loopInfo = &innerLoop; + } + return loopInfo; + } + + void ComputeLoopHeader() + { + auto size = linearizer_->regionList_.size(); + for (size_t i = 0; i < numLoops_; i++) { + auto& loopInfo = loops_[i]; + for (size_t j = 0; j < size; j++) { + if (loopInfo.loopBodys->TestBit(j)) { + auto current = linearizer_->regionList_.at(j); + if (!CheckRegionDomLoopExist(current, loopInfo)) { + current->loopHead_ = nullptr; + } + } + } + } + } + + void ComputeLoopDepth() + { + auto size = linearizer_->regionList_.size(); + for (size_t cur = 0; cur < size; cur++) { + GateRegion* region = linearizer_->regionList_[cur]; + int loopDepth = 0; + int innerLoopIndex = -1; + for (int i = static_cast(numLoops_) - 1; i >= 0; i--) { + auto& loopInfo = loops_[i]; + if (loopInfo.loopBodys->TestBit(cur)) { + loopDepth++; + innerLoopIndex = i; + } + } + region->SetLoopDepth(loopDepth); + region->SetInnerLoopIndex(innerLoopIndex); + } + } + + bool CheckRegionDomLoopExist(GateRegion* region, LoopInfo& loopInfo) + { + if (loopInfo.loopExits == nullptr) { + return true; + } + for (auto exitRegion : *loopInfo.loopExits) { + if (linearizer_->GetCommonDominator(region, exitRegion) != region) { + return false; + } + } + return true; + } + + size_t Push(GateRegion *region, size_t depth) + { + if (region->IsUnvisited(acc_)) { + dfsStack_[depth].region = region; + dfsStack_[depth].index = 0; + region->SetVisited(acc_); + return depth + 1; + } + return depth; + } + +private: + struct DFSState { + DFSState(GateRegion *region, size_t index) + : region(region), index(index) {} + + GateRegion *region; + size_t index; + }; + GraphLinearizer* linearizer_ {nullptr}; + ChunkDeque pendingList_; + ChunkVector loops_; + ChunkVector loopbacks_; + Chunk* chunk_ {nullptr}; + ChunkVector dfsStack_; + GateAccessor acc_; + size_t numLoops_{0}; +}; + class GateScheduler { public: explicit GateScheduler(GraphLinearizer *linearizer) @@ -397,7 +674,7 @@ public: while (!pendingList_.empty()) { auto curGate = pendingList_.back(); pendingList_.pop_back(); - VisitScheduleGate(curGate); + ComputeLowerBoundAndScheduleGate(curGate); } } } @@ -425,7 +702,7 @@ public: } } - void VisitScheduleGate(GateRef curGate) + void ComputeLowerBoundAndScheduleGate(GateRef curGate) { auto& curInfo = linearizer_->GetGateInfo(curGate); if (!curInfo.IsSchedulable() || @@ -435,12 +712,28 @@ public: auto region = GetCommonDominatorOfAllUses(curGate); if (scheduleUpperBound_) { ASSERT(curInfo.upperBound != nullptr); - ASSERT(GetCommonDominator(region, curInfo.upperBound) == curInfo.upperBound); - region = curInfo.upperBound; + ASSERT(linearizer_->GetCommonDominator(region, curInfo.upperBound) == curInfo.upperBound); + auto uppermost = curInfo.upperBound->depth_; + auto upperRegion = GetUpperBoundRegion(region); + while (upperRegion != nullptr && upperRegion->depth_ >= uppermost) { + region = upperRegion; + upperRegion = GetUpperBoundRegion(region); + } } ScheduleGate(curGate, region); } + GateRegion* GetUpperBoundRegion(GateRegion* region) + { + if (region->IsLoopHead()) { + return region->iDominator_; + } + if (region->loopHead_ != nullptr) { + return region->loopHead_->iDominator_; + } + return nullptr; + } + void ScheduleGate(GateRef gate, GateRegion* region) { auto ins = acc_.Ins(gate); @@ -456,7 +749,7 @@ public: } } ASSERT(!linearizer_->IsScheduled(gate)); - linearizer_->ScheduleGate(gate, region); + linearizer_->BindGate(gate, region); } GateRegion* GetCommonDominatorOfAllUses(GateRef curGate) @@ -484,27 +777,15 @@ public: region = useRegion; } else { ASSERT(useRegion != nullptr); - region = GetCommonDominator(region, useRegion); + region = linearizer_->GetCommonDominator(region, useRegion); } } return region; } - GateRegion* GetCommonDominator(GateRegion* left, GateRegion* right) const - { - while (left != right) { - if (left->depth_ < right->depth_) { - right = right->iDominator_; - } else { - left = left->iDominator_; - } - } - return left; - } - bool IsInSameDominatorChain(GateRegion* left, GateRegion* right) const { - auto dom = GetCommonDominator(left, right); + auto dom = linearizer_->GetCommonDominator(left, right); return left == dom || right == dom; } @@ -512,7 +793,7 @@ public: { for (auto gate : fixedGateList_) { GateRegion* region = linearizer_->GateToRegion(gate); - linearizer_->ScheduleGate(gate, region); + linearizer_->BindGate(gate, region); } #ifndef NDEBUG Verify(); @@ -547,11 +828,18 @@ void GraphLinearizer::LinearizeGraph() builder.Run(); ImmediateDominatorsGenerator generator(this, chunk_, regionList_.size()); generator.Run(); - GateScheduler scheduler(this); - scheduler.Prepare(); - scheduler.ScheduleUpperBound(); - scheduler.ScheduleFloatingGate(); - scheduler.ScheduleFixedGate(); + if (IsEnableLoopInvariantCodeMotion() && loopNumber_ > 0) { + scheduleUpperBound_ = true; + LoopInfoBuilder loopInfoBuilder(this, chunk_); + loopInfoBuilder.Run(); + } + if (!onlyBB_) { + GateScheduler scheduler(this); + scheduler.Prepare(); + scheduler.ScheduleUpperBound(); + scheduler.ScheduleFloatingGate(); + scheduler.ScheduleFixedGate(); + } } void GraphLinearizer::CreateGateRegion(GateRef gate) @@ -569,18 +857,99 @@ void GraphLinearizer::CreateGateRegion(GateRef gate) void GraphLinearizer::LinearizeRegions(ControlFlowGraph &result) { + size_t liveNum = OptimizeCFG(); + ASSERT(result.size() == 0); - result.resize(regionList_.size()); + result.resize(liveNum); auto uses = acc_.Uses(acc_.GetArgRoot()); for (auto useIt = uses.begin(); useIt != uses.end(); useIt++) { regionList_.front()->gateList_.emplace_back(*useIt); } + size_t i = 0; + for (size_t id = 0; id < regionList_.size(); id++) { + GateRegion* r = regionList_[id]; + if (r->IsDead()) { + continue; + } + auto& gates = r->GetGates(); + auto& bb = result[i]; + bb.insert(bb.end(), gates.begin(), gates.end()); + i++; + } +} + +bool GateRegion::IsSimple(GateAccessor *acc) const +{ + for (auto g : gateList_) { + bool isSimple = acc->IsSimpleState(g); + bool complexOut = HasComplexOuts(); + if (!isSimple || complexOut) { + return false; + } + } + return true; +} + +size_t GraphLinearizer::OptimizeControls(GateRegion *region) +{ + size_t deads = 0; + GateRegion* target = region; + do { + GateRegion* succ = target->GetSimpleSuccRegion(); + if (succ == nullptr) { + break; + } + MoveAndClear(target, succ); + target = succ; + deads++; + } while (target->IsSimple(&acc_)); + return deads; +} + +void GraphLinearizer::MoveAndClear(GateRegion* from, GateRegion* to) +{ + ASSERT(from != to); + ASSERT(to->GetPreds().size() == 1); + for (GateRef g: from->GetGates()) { + ASSERT(acc_.IsSimpleState(g)); + OpCode op = acc_.GetOpCode(g); + switch (op) { + case OpCode::IF_TRUE: + case OpCode::IF_FALSE: + case OpCode::SWITCH_CASE: + case OpCode::DEFAULT_CASE: + case OpCode::LOOP_BACK: + case OpCode::ORDINARY_BLOCK: + case OpCode::MERGE: + case OpCode::VALUE_SELECTOR: + to->AddGate(g); + break; + default: + break; + } + } + for (auto p : from->GetPreds()) { + p->ReplaceSucc(from, to); + } + to->RemovePred(from); + from->SetDead(); +#ifndef NDEBUG + from->Clear(); +#endif +} + +size_t GraphLinearizer::OptimizeCFG() +{ + size_t liveNum = regionList_.size(); for (size_t i = 0; i < regionList_.size(); i++) { - auto region = regionList_[i]; - auto &gateList = region->gateList_; - result[i].insert(result[i].end(), gateList.begin(), gateList.end()); + GateRegion* src = regionList_[i]; + if (!src->IsDead() && src->IsSimple(&acc_)) { + size_t dead = OptimizeControls(src); + liveNum -= dead; + } } + return liveNum; } GateRegion* GraphLinearizer::FindPredRegion(GateRef input) @@ -595,16 +964,33 @@ GateRegion* GraphLinearizer::FindPredRegion(GateRef input) return predRegion; } +GateRegion* GraphLinearizer::GetCommonDominator(GateRegion* left, GateRegion* right) const +{ + while (left != right) { + if (left->depth_ < right->depth_) { + right = right->iDominator_; + } else { + left = left->iDominator_; + } + } + return left; +} + void GraphLinearizer::PrintGraph(const char* title) { LOG_COMPILER(INFO) << "======================== " << title << " ========================"; + int bbIdx = 0; for (size_t i = 0; i < regionList_.size(); i++) { auto bb = regionList_[i]; + if (bb->IsDead()) { + continue; + } auto front = bb->gateList_.front(); auto opcode = acc_.GetOpCode(front); - LOG_COMPILER(INFO) << "B" << bb->id_ << ": " << "depth: [" << bb->depth_ << "] " + auto loopHeadId = bb->loopHead_ != nullptr ? bb->loopHead_->id_ : 0; + LOG_COMPILER(INFO) << "B" << bb->id_ << "_LB" << bbIdx << ": " << "depth: [" << bb->depth_ << "] " << opcode << "(" << acc_.GetId(front) << ") " - << "IDom B" << bb->iDominator_->id_; + << "IDom B" << bb->iDominator_->id_ << " loop Header: " << loopHeadId; std::string log("\tPreds: "); for (size_t k = 0; k < bb->preds_.size(); ++k) { log += std::to_string(bb->preds_[k]->id_) + ", "; @@ -619,6 +1005,7 @@ void GraphLinearizer::PrintGraph(const char* title) acc_.Print(*it); } LOG_COMPILER(INFO) << ""; + bbIdx++; } } } // namespace panda::ecmascript::kungfu diff --git a/ecmascript/compiler/graph_linearizer.h b/ecmascript/compiler/graph_linearizer.h index 427ad8dcbf0e38a13d9d2a8a0d83256a6d6adbc0..a796d8488a7d5d4c03ec13e3a1ee3e5f9ced0166 100644 --- a/ecmascript/compiler/graph_linearizer.h +++ b/ecmascript/compiler/graph_linearizer.h @@ -45,11 +45,26 @@ public: acc.SetMark(state_, MarkCode::VISITED); } + void SetFinished(GateAccessor& acc) + { + acc.SetMark(state_, MarkCode::FINISHED); + } + + bool IsUnvisited(GateAccessor& acc) const + { + return acc.GetMark(state_) == MarkCode::NO_MARK; + } + bool IsVisited(GateAccessor& acc) const { return acc.GetMark(state_) == MarkCode::VISITED; } + bool IsFinished(GateAccessor& acc) const + { + return acc.GetMark(state_) == MarkCode::FINISHED; + } + bool IsLoopHead() const { return stateKind_ == StateKind::LOOP_HEAD; @@ -60,31 +75,173 @@ public: return state_; } + void SetDead() + { + stateKind_ = StateKind::DEAD; + } + + bool IsDead() const + { + return stateKind_ == StateKind::DEAD; + } + + bool IsSimple(GateAccessor *acc) const; + + bool HasComplexOuts() const + { + return succs_.size() > 1; + } + + GateRegion* GetSimpleSuccRegion() const + { + if (succs_.size() == 1) { + GateRegion* dst = succs_[0]; + if (dst->GetPreds().size() == 1) { + return dst; + } + } + return nullptr; + } + + void ReplaceSucc(GateRegion* oldSucc, GateRegion* newSucc) + { + for (size_t i = 0; i < succs_.size(); i++) { + if (succs_[i] == oldSucc) { + succs_[i] = newSucc; + } + } + newSucc->AddPred(this); + } + + bool RemovePred(GateRegion* removedRegion) + { + for (auto it = preds_.begin(); it != preds_.end(); it++) { + if (*it == removedRegion) { + preds_.erase(it); + return true; + } + } + return false; + } + + void AddPred(GateRegion* r) + { + for (auto p : preds_) { + if (p == r) { + return; + } + } + preds_.emplace_back(r); + } + + void AddGates(ChunkVector& gates) + { + gateList_.insert(gateList_.end(), gates.begin(), gates.end()); + } + + ChunkVector& GetGates() + { + return gateList_; + } + + ChunkVector& GetPreds() + { + return preds_; + } + size_t GetId() const { return id_; } + void Clear() + { + id_ = 0; + depth_ = INVALID_DEPTH; + iDominator_ = nullptr; + gateList_.clear(); + preds_.clear(); + succs_.clear(); + dominatedRegions_.clear(); + } + + void SetLoopNumber(size_t loopNumber) + { + loopNumber_ = static_cast(loopNumber); + } + + size_t GetLoopNumber() const + { + return static_cast(loopNumber_); + } + + bool HasLoopNumber() const + { + return loopNumber_ >= 0; + } + + GateRegion* GetDominator() + { + return iDominator_; + } + + ChunkVector& GetDominatedRegions() + { + return dominatedRegions_; + } + + int32_t GetDepth() + { + return depth_; + } + + void SetLoopDepth(size_t loopDepth) + { + loopDepth_ = loopDepth; + } + + size_t GetLoopDepth() + { + return loopDepth_; + } + + void SetInnerLoopIndex(size_t innerLoopIndex) + { + innerLoopIndex_ = static_cast(innerLoopIndex); + } + + int GetInnerLoopIndex() + { + return innerLoopIndex_; + } + private: enum StateKind { BRANCH, MERGE, LOOP_HEAD, OTHER, + DEAD }; static constexpr int32_t INVALID_DEPTH = -1; size_t id_ {0}; + size_t loopDepth_ {0}; // the loop nesting level of this block + int innerLoopIndex_ {-1}; // number of the innermost loop of this block int32_t depth_ {INVALID_DEPTH}; GateRegion* iDominator_ {nullptr}; + GateRegion* loopHead_ {nullptr}; ChunkVector gateList_; ChunkVector preds_; ChunkVector succs_; ChunkVector dominatedRegions_; GateRef state_ {Circuit::NullGate()}; StateKind stateKind_ {StateKind::OTHER}; + int32_t loopNumber_ {INVALID_DEPTH}; + friend class ArrayBoundsCheckElimination; friend class CFGBuilder; friend class GateScheduler; friend class ImmediateDominatorsGenerator; + friend class LoopInfoBuilder; friend class GraphLinearizer; friend class StateDependBuilder; }; @@ -93,12 +250,18 @@ class GraphLinearizer { public: using ControlFlowGraph = std::vector>; - GraphLinearizer(Circuit *circuit, bool enableLog, const std::string& name, Chunk* chunk) + GraphLinearizer(Circuit *circuit, bool enableLog, const std::string& name, Chunk* chunk, + bool onlyBB = false, bool loopInvariantCodeMotion = false) : enableLog_(enableLog), methodName_(name), chunk_(chunk), circuit_(circuit), - acc_(circuit), gateIdToGateInfo_(chunk), - regionList_(chunk), regionRootList_(chunk) {} + acc_(circuit), gateIdToGateInfo_(chunk), regionList_(chunk), regionRootList_(chunk), + onlyBB_(onlyBB), loopInvariantCodeMotion_(loopInvariantCodeMotion) {} void Run(ControlFlowGraph &result); + + GateRef GetStateOfSchedulableGate(GateRef gate) const + { + return GateToRegion(gate)->GetState(); + } private: enum class ScheduleModel { LIR, JS_OPCODE }; enum class ScheduleState { NONE, FIXED, SELECTOR, SCHEDELABLE }; @@ -140,10 +303,16 @@ private: return methodName_; } + bool IsEnableLoopInvariantCodeMotion() const + { + return loopInvariantCodeMotion_; + } + void LinearizeGraph(); void LinearizeRegions(ControlFlowGraph &result); void CreateGateRegion(GateRef gate); GateRegion* FindPredRegion(GateRef input); + GateRegion* GetCommonDominator(GateRegion* left, GateRegion* right) const; GateInfo& GetGateInfo(GateRef gate) { @@ -206,7 +375,7 @@ private: regionRootList_.emplace_back(gate); } - void ScheduleGate(GateRef gate, GateRegion* region) + void BindGate(GateRef gate, GateRegion* region) { GateInfo& info = GetGateInfo(gate); info.region = region; @@ -231,6 +400,7 @@ private: regionRootList_.clear(); scheduleUpperBound_ = false; model_ = ScheduleModel::LIR; + loopNumber_ = 0; } void EnableScheduleUpperBound() @@ -248,10 +418,13 @@ private: return model_ == ScheduleModel::LIR; } + size_t OptimizeCFG(); + size_t OptimizeControls(GateRegion *region); + void MoveAndClear(GateRegion *from, GateRegion *to); void PrintGraph(const char* title); bool enableLog_ {false}; - bool scheduleUpperBound_ { false}; + bool scheduleUpperBound_ {false}; ScheduleModel model_ {ScheduleModel::LIR}; std::string methodName_; Chunk* chunk_ {nullptr}; @@ -263,9 +436,14 @@ private: ChunkVector regionList_; ChunkVector regionRootList_; + bool onlyBB_ {false}; // dont schedule + bool loopInvariantCodeMotion_ {false}; + + friend class ArrayBoundsCheckElimination; friend class CFGBuilder; friend class GateScheduler; friend class ImmediateDominatorsGenerator; + friend class LoopInfoBuilder; friend class StateSplitLinearizer; }; }; // namespace panda::ecmascript::kungfu diff --git a/ecmascript/compiler/hcr_gate_meta_data.cpp b/ecmascript/compiler/hcr_gate_meta_data.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c992d22ea83d8ae67f76a09f55c2af0daca1eac2 --- /dev/null +++ b/ecmascript/compiler/hcr_gate_meta_data.cpp @@ -0,0 +1,22 @@ +/* + * 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/compiler/gate.h" +#include "ecmascript/compiler/hcr_gate_meta_data.h" +#include "ecmascript/compiler/gate_meta_data_builder.h" + +namespace panda::ecmascript::kungfu { + +} \ No newline at end of file diff --git a/ecmascript/compiler/hcr_gate_meta_data.h b/ecmascript/compiler/hcr_gate_meta_data.h new file mode 100644 index 0000000000000000000000000000000000000000..a9786514e28870a6d5ba49a9e8b59fd1ae59fe4e --- /dev/null +++ b/ecmascript/compiler/hcr_gate_meta_data.h @@ -0,0 +1,121 @@ +/* + * 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_COMPILER_HCR_GATE_META_DATA_H +#define ECMASCRIPT_COMPILER_HCR_GATE_META_DATA_H + +#include + +#include "ecmascript/compiler/bytecodes.h" +#include "ecmascript/compiler/type.h" +#include "ecmascript/mem/chunk.h" +#include "ecmascript/mem/chunk_containers.h" + +#include "ecmascript/elements.h" +#include "ecmascript/pgo_profiler/types/pgo_profiler_type.h" +#include "libpandabase/macros.h" + +#include "ecmascript/compiler/share_gate_meta_data.h" + +namespace panda::ecmascript::kungfu { + +class JSBytecodeMetaData : public GateMetaData { +public: + explicit JSBytecodeMetaData(size_t valuesIn, EcmaOpcode opcode, uint32_t pcOffset, GateFlags flags) + : GateMetaData(OpCode::JS_BYTECODE, flags, 1, 1, valuesIn), + opcode_(opcode), pcOffset_(pcOffset) + { + SetKind(GateMetaData::Kind::JSBYTECODE); + } + + static const JSBytecodeMetaData* Cast(const GateMetaData* meta) + { + meta->AssertKind(GateMetaData::Kind::JSBYTECODE); + return static_cast(meta); + } + + uint32_t GetPcOffset() const + { + return pcOffset_; + } + + void SetType(PGOSampleType type) + { + type_ = type; + } + + PGOSampleType GetType() const + { + return type_; + } + + EcmaOpcode GetByteCodeOpcode() const + { + return opcode_; + } + + void SetElementsKind(ElementsKind kind) + { + elementsKinds_.emplace_back(kind); + } + + ElementsKind GetElementsKind() const + { + auto size = elementsKinds_.size(); + if (size == 0) { + return ElementsKind::GENERIC; + } + return elementsKinds_[0]; + } + + std::vector GetElementsKinds() const + { + return elementsKinds_; + } + +private: + EcmaOpcode opcode_; + uint32_t pcOffset_; + PGOSampleType type_; + std::vector elementsKinds_ {}; +}; + + +class FrameStateOutput { +public: + static constexpr uint32_t INVALID_INDEX = static_cast(-1); + explicit FrameStateOutput(uint32_t value) : index_(value) {} + + static FrameStateOutput Invalid() + { + return FrameStateOutput(INVALID_INDEX); + } + + bool IsInvalid() const + { + return index_ == INVALID_INDEX; + } + + uint32_t GetValue() const + { + return index_; + } +private: + uint32_t index_; +}; + +} + +#endif // ECMASCRIPT_COMPILER_HCR_GATE_META_DATA_H diff --git a/ecmascript/compiler/hcr_opcodes.h b/ecmascript/compiler/hcr_opcodes.h new file mode 100644 index 0000000000000000000000000000000000000000..61fb5a4c2f5e9b21ecbb847fb56827437c79fde9 --- /dev/null +++ b/ecmascript/compiler/hcr_opcodes.h @@ -0,0 +1,56 @@ +/* + * 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_COMPILER_HCR_OPCODE_H +#define ECMASCRIPT_COMPILER_HCR_OPCODE_H + +namespace panda::ecmascript::kungfu { + +#define HCR_IMMUTABLE_META_DATA_CACHE_LIST(V) \ + V(ToLength, TO_LENGTH, GateFlags::NONE_FLAG, 1, 1, 1) + +#define HCR_GATE_META_DATA_LIST_WITH_VALUE_IN(V) \ + V(NoGcRuntimeCall, NOGC_RUNTIME_CALL, GateFlags::NO_WRITE, 0, 1, value) \ + V(BytecodeCall, BYTECODE_CALL, GateFlags::NONE_FLAG, 0, 1, value) \ + V(DebuggerBytecodeCall, DEBUGGER_BYTECODE_CALL, GateFlags::NONE_FLAG, 0, 1, value) \ + V(BuiltinsCallWithArgv, BUILTINS_CALL_WITH_ARGV, GateFlags::NONE_FLAG, 0, 1, value) \ + V(BuiltinsCall, BUILTINS_CALL, GateFlags::NONE_FLAG, 0, 1, value) \ + V(RuntimeCall, RUNTIME_CALL, GateFlags::NONE_FLAG, 0, 1, value) \ + V(RuntimeCallWithArgv, RUNTIME_CALL_WITH_ARGV, GateFlags::NONE_FLAG, 0, 1, value) \ + V(SaveRegister, SAVE_REGISTER, GateFlags::NONE_FLAG, 0, 1, value) + +#define HCR_GATE_META_DATA_LIST_WITH_PC_OFFSET(V) \ + V(Construct, CONSTRUCT, GateFlags::HAS_FRAME_STATE, 1, 1, value) + +#define HCR_GATE_META_DATA_LIST_WITH_PC_OFFSET_FIXED_VALUE(V) \ + V(CallGetter, CALL_GETTER, GateFlags::HAS_FRAME_STATE, 1, 1, 2) \ + V(CallSetter, CALL_SETTER, GateFlags::HAS_FRAME_STATE, 1, 1, 3) + +#define HCR_GATE_META_DATA_LIST_WITH_VALUE(V) \ + V(CreateArray, CREATE_ARRAY, GateFlags::NONE_FLAG, 1, 1, 0) \ + V(CreateArrayWithBuffer, CREATE_ARRAY_WITH_BUFFER, GateFlags::CHECKABLE, 1, 1, 2) \ + V(RestoreRegister, RESTORE_REGISTER, GateFlags::NONE_FLAG, 0, 0, 1) + +#define HCR_GATE_META_DATA_LIST_WITH_SIZE(V) \ + HCR_GATE_META_DATA_LIST_WITH_VALUE_IN(V) + +#define HCR_GATE_META_DATA_LIST_WITH_ONE_PARAMETER(V) \ + HCR_GATE_META_DATA_LIST_WITH_VALUE(V) + +#define HCR_GATE_OPCODE_LIST(V) \ + V(JS_BYTECODE) + +} +#endif // ECMASCRIPT_COMPILER_HCR_OPCODE_H \ No newline at end of file diff --git a/ecmascript/compiler/ic_stub_builder.cpp b/ecmascript/compiler/ic_stub_builder.cpp index 5a8d54678b0c88a74a9c83eb12cbce7cdd749435..068c8d08f2a8b0b4fbda054c4b44b3bb1db66fa3 100644 --- a/ecmascript/compiler/ic_stub_builder.cpp +++ b/ecmascript/compiler/ic_stub_builder.cpp @@ -74,6 +74,7 @@ void ICStubBuilder::ValuedICAccessor(Variable* cachedHandler, Label *tryICHandle Bind(&isHeapObject); { Label tryPoly(env); + Label tryWithElementPoly(env); GateRef hclass = LoadHClass(receiver_); Branch(Equal(LoadObjectFromWeakRef(firstValue), hclass), tryElementIC, @@ -81,11 +82,28 @@ void ICStubBuilder::ValuedICAccessor(Variable* cachedHandler, Label *tryICHandle Bind(&tryPoly); { Label firstIsKey(env); - Branch(Int64Equal(firstValue, propKey_), &firstIsKey, slowPath_); + Branch(Int64Equal(firstValue, propKey_), &firstIsKey, &tryWithElementPoly); Bind(&firstIsKey); - GateRef handler = CheckPolyHClass(cachedHandler->ReadVariable(), hclass); - cachedHandler->WriteVariable(handler); - Branch(TaggedIsHole(cachedHandler->ReadVariable()), slowPath_, tryICHandler); + { + GateRef handler = CheckPolyHClass(cachedHandler->ReadVariable(), hclass); + cachedHandler->WriteVariable(handler); + Branch(TaggedIsHole(cachedHandler->ReadVariable()), slowPath_, tryICHandler); + } + Bind(&tryWithElementPoly); + { + Label checkSecond(env); + Label checkPoly(env); + Branch(TaggedIsWeak(firstValue), slowPath_, &checkSecond); + Bind(&checkSecond); + { + Branch(TaggedIsHole(cachedHandler->ReadVariable()), &checkPoly, slowPath_); + } + Bind(&checkPoly); + { + cachedHandler->WriteVariable(CheckPolyHClass(firstValue, hclass)); + Branch(TaggedIsHole(cachedHandler->ReadVariable()), slowPath_, tryElementIC); + } + } } } Bind(¬HeapObject); @@ -96,7 +114,8 @@ void ICStubBuilder::ValuedICAccessor(Variable* cachedHandler, Label *tryICHandle } } -void ICStubBuilder::LoadICByName(Variable* result, Label* tryFastPath, Label *slowPath, Label *success) +void ICStubBuilder::LoadICByName( + Variable *result, Label *tryFastPath, Label *slowPath, Label *success, ProfileOperation callback) { auto env = GetEnvironment(); Label loadWithHandler(env); @@ -108,7 +127,7 @@ void ICStubBuilder::LoadICByName(Variable* result, Label* tryFastPath, Label *sl NamedICAccessor(&cachedHandler, &loadWithHandler); Bind(&loadWithHandler); { - GateRef ret = LoadICWithHandler(glue_, receiver_, receiver_, *cachedHandler); + GateRef ret = LoadICWithHandler(glue_, receiver_, receiver_, *cachedHandler, callback); result->WriteVariable(ret); Branch(TaggedIsHole(ret), slowPath_, success_); } @@ -132,29 +151,52 @@ void ICStubBuilder::StoreICByName(Variable* result, Label* tryFastPath, Label *s } } -void ICStubBuilder::LoadICByValue(Variable* result, Label* tryFastPath, Label *slowPath, Label *success) +void ICStubBuilder::LoadICByValue( + Variable *result, Label *tryFastPath, Label *slowPath, Label *success, ProfileOperation callback) { auto env = GetEnvironment(); Label loadWithHandler(env); Label loadElement(env); + Label handlerInfoIsElement(env); + Label handlerInfoNotElement(env); + Label handlerInfoIsStringElement(env); + Label handlerInfoNotStringElement(env); + Label exit(env); SetLabels(tryFastPath, slowPath, success); GateRef secondValue = GetValueFromTaggedArray( profileTypeInfo_, Int32Add(slotId_, Int32(1))); DEFVARIABLE(cachedHandler, VariableType::JS_ANY(), secondValue); + DEFVARIABLE(ret, VariableType::JS_ANY(), secondValue); + ValuedICAccessor(&cachedHandler, &loadWithHandler, &loadElement); Bind(&loadElement); { - GateRef ret = LoadElement(glue_, receiver_, propKey_); - result->WriteVariable(ret); - Branch(TaggedIsHole(ret), slowPath_, success_); + GateRef handlerInfo = GetInt32OfTInt(*cachedHandler); + Branch(IsElement(handlerInfo), &handlerInfoIsElement, &handlerInfoNotElement); + Bind(&handlerInfoIsElement); + { + ret = LoadElement(glue_, receiver_, propKey_); + Jump(&exit); + } + Bind(&handlerInfoNotElement); + { + Branch(IsStringElement(handlerInfo), &handlerInfoIsStringElement, &handlerInfoNotStringElement); + Bind(&handlerInfoNotStringElement); + Jump(&exit); + Bind(&handlerInfoIsStringElement); + ret = LoadStringElement(glue_, receiver_, propKey_); + Jump(&exit); + } } Bind(&loadWithHandler); { - GateRef ret = LoadICWithHandler(glue_, receiver_, receiver_, *cachedHandler); - result->WriteVariable(ret); - Branch(TaggedIsHole(ret), slowPath_, success_); + ret = LoadICWithHandler(glue_, receiver_, receiver_, *cachedHandler, callback); + Jump(&exit); } + Bind(&exit); + result->WriteVariable(*ret); + Branch(TaggedIsHole(*ret), slowPath_, success_); } void ICStubBuilder::StoreICByValue(Variable* result, Label* tryFastPath, Label *slowPath, Label *success) @@ -170,7 +212,7 @@ void ICStubBuilder::StoreICByValue(Variable* result, Label* tryFastPath, Label * ValuedICAccessor(&cachedHandler, &storeWithHandler, &storeElement); Bind(&storeElement); { - GateRef ret = ICStoreElement(glue_, receiver_, propKey_, value_, secondValue); + GateRef ret = ICStoreElement(glue_, receiver_, propKey_, value_, *cachedHandler); result->WriteVariable(ret); Branch(TaggedIsHole(ret), slowPath_, success_); } diff --git a/ecmascript/compiler/ic_stub_builder.h b/ecmascript/compiler/ic_stub_builder.h index c3ac0537381c380e023e726492da055f58426b88..34fefd3165fc780aefdd86b8b684ffcae0bb9dd3 100644 --- a/ecmascript/compiler/ic_stub_builder.h +++ b/ecmascript/compiler/ic_stub_builder.h @@ -44,9 +44,11 @@ public: propKey_ = propKey; } - void LoadICByName(Variable* result, Label* tryFastPath, Label *slowPath, Label *success); + void LoadICByName(Variable* result, Label* tryFastPath, Label *slowPath, Label *success, + ProfileOperation callback); void StoreICByName(Variable* result, Label* tryFastPath, Label *slowPath, Label *success); - void LoadICByValue(Variable* result, Label* tryFastPath, Label *slowPath, Label *success); + void LoadICByValue(Variable* result, Label* tryFastPath, Label *slowPath, Label *success, + ProfileOperation callback); void StoreICByValue(Variable* result, Label* tryFastPath, Label *slowPath, Label *success); void TryLoadGlobalICByName(Variable* result, Label* tryFastPath, Label *slowPath, Label *success); void TryStoreGlobalICByName(Variable* result, Label* tryFastPath, Label *slowPath, Label *success); diff --git a/ecmascript/compiler/interpreter_stub-inl.h b/ecmascript/compiler/interpreter_stub-inl.h index 0de404c324733136bae4dde8942dd34b205150c0..e14211c89916d740a7fcca82e398ab17e46d4904 100644 --- a/ecmascript/compiler/interpreter_stub-inl.h +++ b/ecmascript/compiler/interpreter_stub-inl.h @@ -247,7 +247,8 @@ GateRef InterpreterStubBuilder::GetProfileTypeInfoFromMethod(GateRef method) GateRef InterpreterStubBuilder::GetModuleFromFunction(GateRef function) { - return Load(VariableType::JS_POINTER(), function, IntPtr(JSFunction::ECMA_MODULE_OFFSET)); + GateRef method = GetMethodFromFunction(function); + return Load(VariableType::JS_POINTER(), method, IntPtr(Method::ECMA_MODULE_OFFSET)); } GateRef InterpreterStubBuilder::GetHomeObjectFromFunction(GateRef function) @@ -260,6 +261,12 @@ GateRef InterpreterStubBuilder::GetConstpoolFromMethod(GateRef method) return Load(VariableType::JS_POINTER(), method, IntPtr(Method::CONSTANT_POOL_OFFSET)); } +GateRef InterpreterStubBuilder::GetModule(GateRef sp) +{ + GateRef currentFunc = GetFunctionFromFrame(GetFrame(sp)); + return GetModuleFromFunction(currentFunc); +} + GateRef InterpreterStubBuilder::GetResumeModeFromGeneratorObject(GateRef obj) { GateRef bitfieldOffset = IntPtr(JSGeneratorObject::BIT_FIELD_OFFSET); @@ -314,12 +321,6 @@ void InterpreterStubBuilder::SetHomeObjectToFunction(GateRef glue, GateRef funct Store(VariableType::JS_ANY(), glue, function, offset, value); } -void InterpreterStubBuilder::SetModuleToFunction(GateRef glue, GateRef function, GateRef value) -{ - GateRef offset = IntPtr(JSFunction::ECMA_MODULE_OFFSET); - Store(VariableType::JS_POINTER(), glue, function, offset, value); -} - void InterpreterStubBuilder::SetFrameState(GateRef glue, GateRef sp, GateRef function, GateRef acc, GateRef env, GateRef pc, GateRef prev, GateRef type) { diff --git a/ecmascript/compiler/interpreter_stub.cpp b/ecmascript/compiler/interpreter_stub.cpp index 77d5004cd29159ca0f79f9a6f2f419ce74208bb1..a896e62f47e66d0074388f504c5d02da96eeac53 100644 --- a/ecmascript/compiler/interpreter_stub.cpp +++ b/ecmascript/compiler/interpreter_stub.cpp @@ -13,6 +13,7 @@ * limitations under the License. */ +#include #include "ecmascript/base/number_helper.h" #include "ecmascript/compiler/access_object_stub_builder.h" #include "ecmascript/compiler/bc_call_signature.h" @@ -24,6 +25,7 @@ #include "ecmascript/compiler/profiler_stub_builder.h" #include "ecmascript/compiler/stub_builder-inl.h" #include "ecmascript/compiler/variable_type.h" +#include "ecmascript/dfx/vm_thread_control.h" #include "ecmascript/global_env_constants.h" #include "ecmascript/ic/profile_type_info.h" #include "ecmascript/interpreter/interpreter_assembly.h" @@ -35,7 +37,7 @@ #include "libpandafile/bytecode_instruction-inl.h" namespace panda::ecmascript::kungfu { -#define DECLARE_ASM_HANDLER_BASE(name, needPrint, V) \ +#define DECLARE_ASM_HANDLER_BASE(name, needPrint, V, format) \ void name##StubBuilder::GenerateCircuit() \ { \ GateRef glue = PtrArgument(static_cast(InterpreterHandlerInputs::GLUE)); \ @@ -49,7 +51,7 @@ void name##StubBuilder::GenerateCircuit() GateRef hotnessCounter = Int32Argument( \ static_cast(InterpreterHandlerInputs::HOTNESS_COUNTER)); \ DebugPrintInstruction(); \ - V() \ + V(format) \ GenerateCircuitImpl(glue, sp, pc, constpool, profileTypeInfo, acc, hotnessCounter, callback); \ } @@ -59,23 +61,25 @@ void name##StubBuilder::GenerateCircuitImpl(GateRef glue, GateRef sp, GateRef pc GateRef acc, GateRef hotnessCounter, \ [[maybe_unused]] ProfileOperation callback) -#define REGISTER_PROFILE_CALL_BACK() \ - ProfileOperation callback([this, glue, sp, pc, profileTypeInfo](GateRef value, OperationType type) { \ - ProfilerStubBuilder profiler(this); \ - profiler.PGOProfiler(glue, pc, GetFunctionFromFrame(GetFrame(sp)), profileTypeInfo, value, type); \ - }); +#define REGISTER_PROFILE_CALL_BACK(format) \ + ProfileOperation callback( \ + [this, glue, sp, pc, profileTypeInfo](const std::initializer_list &values, OperationType type) { \ + ProfilerStubBuilder profiler(this); \ + profiler.PGOProfiler(glue, pc, GetFunctionFromFrame(GetFrame(sp)), profileTypeInfo, values, format, type); \ + }); -#define REGISTER_NULL_CALL_BACK() ProfileOperation callback; +#define REGISTER_NULL_CALL_BACK(format) ProfileOperation callback; -#define DECLARE_ASM_HANDLER(name) \ - DECLARE_ASM_HANDLER_BASE(name, true, REGISTER_NULL_CALL_BACK) \ +#define DECLARE_ASM_HANDLER(name) \ + DECLARE_ASM_HANDLER_BASE(name, true, REGISTER_NULL_CALL_BACK, SlotIDFormat::IMM8) \ DECLARE_ASM_HANDLE_IMPLEMENT(name) -#define DECLARE_ASM_HANDLER_NOPRINT(name) \ - DECLARE_ASM_HANDLER_BASE(name, false, REGISTER_NULL_CALL_BACK) \ +#define DECLARE_ASM_HANDLER_NOPRINT(name) \ + DECLARE_ASM_HANDLER_BASE(name, false, REGISTER_NULL_CALL_BACK, SlotIDFormat::IMM8) \ DECLARE_ASM_HANDLE_IMPLEMENT(name) -#define DECLARE_ASM_HANDLER_PROFILE(name, ...) DECLARE_ASM_HANDLER_BASE(name, true, REGISTER_PROFILE_CALL_BACK) +#define DECLARE_ASM_HANDLER_PROFILE(name, base, format) \ + DECLARE_ASM_HANDLER_BASE(name, true, REGISTER_PROFILE_CALL_BACK, format) // TYPE:{OFFSET, ACC_VARACC, JUMP, SSD} #define DISPATCH_BAK(TYPE, ...) DISPATCH_##TYPE(__VA_ARGS__) @@ -121,17 +125,22 @@ void name##StubBuilder::GenerateCircuitImpl(GateRef glue, GateRef sp, GateRef pc GateRef iVecOffset = IntPtr(JSThread::GlueData::GetInterruptVectorOffset(env->IsArch32Bit())); \ GateRef interruptsFlag = Load(VariableType::INT8(), glue, iVecOffset); \ varHotnessCounter = Int32(EcmaInterpreter::METHOD_HOTNESS_THRESHOLD); \ + Label initialized(env); \ Label callRuntime(env); \ - Branch(BoolOr(TaggedIsUndefined(*varProfileTypeInfo), Int8Equal(interruptsFlag, \ - Int8(VmThreadControl::VM_NEED_SUSPENSION))), &callRuntime, &dispatch); \ + Branch(BoolOr(TaggedIsUndefined(*varProfileTypeInfo), \ + Int8Equal(interruptsFlag, Int8(VmThreadControl::VM_NEED_SUSPENSION))), \ + &callRuntime, &initialized); \ Bind(&callRuntime); \ { \ - if (callback.IsEmpty()) { \ + if (!(callback).IsEmpty()) { \ varProfileTypeInfo = CallRuntime(glue, RTSTUB_ID(UpdateHotnessCounterWithProf), { func }); \ } else { \ varProfileTypeInfo = CallRuntime(glue, RTSTUB_ID(UpdateHotnessCounter), { func }); \ } \ + Jump(&dispatch); \ } \ + Bind(&initialized); \ + (callback).TryDump(); \ Jump(&dispatch); \ } \ Bind(&dispatch); @@ -160,6 +169,36 @@ void name##StubBuilder::GenerateCircuitImpl(GateRef glue, GateRef sp, GateRef pc CheckPendingException(glue, sp, pc, constpool, profileTypeInfo, acc, hotnessCounter, \ res, offset) +#define METHOD_ENTRY(func) \ + auto env = GetEnvironment(); \ + METHOD_ENTRY_ENV_DEFINED(func) + +#define METHOD_ENTRY_ENV_DEFINED(func) \ + GateRef isDebugModeOffset = IntPtr(JSThread::GlueData::GetIsDebugModeOffset(env->Is32Bit())); \ + GateRef isDebugMode = Load(VariableType::BOOL(), glue, isDebugModeOffset); \ + Label isDebugModeTrue(env); \ + Label isDebugModeFalse(env); \ + Branch(isDebugMode, &isDebugModeTrue, &isDebugModeFalse); \ + Bind(&isDebugModeTrue); \ + { \ + CallRuntime(glue, RTSTUB_ID(MethodEntry), { func }); \ + Jump(&isDebugModeFalse); \ + } \ + Bind(&isDebugModeFalse) + +#define METHOD_EXIT() \ + GateRef isDebugModeOffset = IntPtr(JSThread::GlueData::GetIsDebugModeOffset(env->Is32Bit())); \ + GateRef isDebugMode = Load(VariableType::BOOL(), glue, isDebugModeOffset); \ + Label isDebugModeTrue(env); \ + Label isDebugModeFalse(env); \ + Branch(isDebugMode, &isDebugModeTrue, &isDebugModeFalse); \ + Bind(&isDebugModeTrue); \ + { \ + CallRuntime(glue, RTSTUB_ID(MethodExit), {}); \ + Jump(&isDebugModeFalse); \ + } \ + Bind(&isDebugModeFalse) + template void InterpreterStubBuilder::DebugPrintInstruction() { @@ -396,7 +435,6 @@ DECLARE_ASM_HANDLER(HandleDefinegettersetterbyvalueV8V8V8V8) GateRef setter = GetVregValue(sp, ZExtInt8ToPtr(ReadInst8_3(pc))); GateRef res = CallRuntime(glue, RTSTUB_ID(DefineGetterSetterByValue), { obj, prop, getter, setter, acc }); // acc is flag - callback.ProfileObjLayoutByStore(obj); CHECK_EXCEPTION_WITH_ACC(res, INT_PTR(DEFINEGETTERSETTERBYVALUE_V8_V8_V8_V8)); } @@ -432,16 +470,24 @@ DECLARE_ASM_HANDLER(HandleCreateemptyobject) DECLARE_ASM_HANDLER(HandleCreateemptyarrayImm8) { DEFVARIABLE(varAcc, VariableType::JS_ANY(), acc); + DEFVARIABLE(varSp, VariableType::NATIVE_POINTER(), sp); NewObjectStubBuilder newBuilder(this); - varAcc = newBuilder.CreateEmptyArray(glue); + GateRef frame = GetFrame(*varSp); + GateRef func = GetFunctionFromFrame(frame); + GateRef slotId = ZExtInt8ToInt32(ReadInst8_0(pc)); + varAcc = newBuilder.CreateEmptyArray(glue, func, pc, profileTypeInfo, slotId, callback); DISPATCH_WITH_ACC(CREATEEMPTYARRAY_IMM8); } DECLARE_ASM_HANDLER(HandleCreateemptyarrayImm16) { DEFVARIABLE(varAcc, VariableType::JS_ANY(), acc); + DEFVARIABLE(varSp, VariableType::NATIVE_POINTER(), sp); NewObjectStubBuilder newBuilder(this); - varAcc = newBuilder.CreateEmptyArray(glue); + GateRef frame = GetFrame(*varSp); + GateRef func = GetFunctionFromFrame(frame); + GateRef slotId = ZExtInt16ToInt32(ReadInst16_0(pc)); + varAcc = newBuilder.CreateEmptyArray(glue, func, pc, profileTypeInfo, slotId, callback); DISPATCH_WITH_ACC(CREATEEMPTYARRAY_IMM16); } @@ -920,7 +966,7 @@ DECLARE_ASM_HANDLER(HandleInstanceofImm8V8) GateRef slotId = ZExtInt8ToInt32(ReadInst8_0(pc)); GateRef target = acc; AccessObjectStubBuilder builder(this); - GateRef result = InstanceOf(glue, obj, target, profileTypeInfo, slotId); + GateRef result = InstanceOf(glue, obj, target, profileTypeInfo, slotId, callback); CHECK_PENDING_EXCEPTION(result, INT_PTR(INSTANCEOF_IMM8_V8)); } @@ -2295,6 +2341,7 @@ DECLARE_ASM_HANDLER(HandleJnezImm32) DECLARE_ASM_HANDLER(HandleReturn) { auto env = GetEnvironment(); + METHOD_EXIT(); DEFVARIABLE(varPc, VariableType::NATIVE_POINTER(), pc); DEFVARIABLE(varSp, VariableType::NATIVE_POINTER(), sp); DEFVARIABLE(varConstpool, VariableType::JS_POINTER(), constpool); @@ -2305,12 +2352,18 @@ DECLARE_ASM_HANDLER(HandleReturn) Label pcEqualNullptr(env); Label pcNotEqualNullptr(env); Label updateHotness(env); + Label isStable(env); Label tryContinue(env); Label dispatch(env); Label slowPath(env); GateRef frame = GetFrame(*varSp); - Branch(TaggedIsUndefined(*varProfileTypeInfo), &updateHotness, &tryContinue); + Branch(TaggedIsUndefined(*varProfileTypeInfo), &updateHotness, &isStable); + Bind(&isStable); + { + Branch(ProfilerStubBuilder(env).IsProfileTypeInfoChanged(*varProfileTypeInfo, callback), &updateHotness, + &tryContinue); + } Bind(&updateHotness); { GateRef function = GetFunctionFromFrame(frame); @@ -2358,6 +2411,7 @@ DECLARE_ASM_HANDLER(HandleReturn) DECLARE_ASM_HANDLER(HandleReturnundefined) { auto env = GetEnvironment(); + METHOD_EXIT(); DEFVARIABLE(varPc, VariableType::NATIVE_POINTER(), pc); DEFVARIABLE(varSp, VariableType::NATIVE_POINTER(), sp); DEFVARIABLE(varConstpool, VariableType::JS_POINTER(), constpool); @@ -2368,12 +2422,18 @@ DECLARE_ASM_HANDLER(HandleReturnundefined) Label pcEqualNullptr(env); Label pcNotEqualNullptr(env); Label updateHotness(env); + Label isStable(env); Label tryContinue(env); Label dispatch(env); Label slowPath(env); GateRef frame = GetFrame(*varSp); - Branch(TaggedIsUndefined(*varProfileTypeInfo), &updateHotness, &tryContinue); + Branch(TaggedIsUndefined(*varProfileTypeInfo), &updateHotness, &isStable); + Bind(&isStable); + { + Branch(ProfilerStubBuilder(env).IsProfileTypeInfoChanged(*varProfileTypeInfo, callback), &updateHotness, + &tryContinue); + } Bind(&updateHotness); { GateRef function = GetFunctionFromFrame(frame); @@ -2422,6 +2482,7 @@ DECLARE_ASM_HANDLER(HandleReturnundefined) DECLARE_ASM_HANDLER(HandleSuspendgeneratorV8) { auto env = GetEnvironment(); + METHOD_EXIT(); DEFVARIABLE(varPc, VariableType::NATIVE_POINTER(), pc); DEFVARIABLE(varSp, VariableType::NATIVE_POINTER(), sp); DEFVARIABLE(varConstpool, VariableType::JS_POINTER(), constpool); @@ -2432,6 +2493,7 @@ DECLARE_ASM_HANDLER(HandleSuspendgeneratorV8) Label pcEqualNullptr(env); Label pcNotEqualNullptr(env); Label updateHotness(env); + Label isStable(env); Label tryContinue(env); Label dispatch(env); Label slowPath(env); @@ -2449,7 +2511,12 @@ DECLARE_ASM_HANDLER(HandleSuspendgeneratorV8) } Bind(¬Exception); varAcc = res; - Branch(TaggedIsUndefined(*varProfileTypeInfo), &updateHotness, &tryContinue); + Branch(TaggedIsUndefined(*varProfileTypeInfo), &updateHotness, &isStable); + Bind(&isStable); + { + Branch(ProfilerStubBuilder(env).IsProfileTypeInfoChanged(*varProfileTypeInfo, callback), &updateHotness, + &tryContinue); + } Bind(&updateHotness); { GateRef function = GetFunctionFromFrame(frame); @@ -2507,6 +2574,7 @@ DECLARE_ASM_HANDLER(HandleDeprecatedSuspendgeneratorPrefV8V8) Label pcEqualNullptr(env); Label pcNotEqualNullptr(env); Label updateHotness(env); + Label isStable(env); Label tryContinue(env); Label dispatch(env); Label slowPath(env); @@ -2524,7 +2592,12 @@ DECLARE_ASM_HANDLER(HandleDeprecatedSuspendgeneratorPrefV8V8) } Bind(¬Exception); varAcc = res; - Branch(TaggedIsUndefined(*varProfileTypeInfo), &updateHotness, &tryContinue); + Branch(TaggedIsUndefined(*varProfileTypeInfo), &updateHotness, &isStable); + Bind(&isStable); + { + Branch(ProfilerStubBuilder(env).IsProfileTypeInfoChanged(*varProfileTypeInfo, callback), &updateHotness, + &tryContinue); + } Bind(&updateHotness); { GateRef function = GetFunctionFromFrame(frame); @@ -2576,7 +2649,7 @@ DECLARE_ASM_HANDLER(HandleTryldglobalbynameImm8Id16) GateRef slotId = ZExtInt8ToInt32(ReadInst8_0(pc)); AccessObjectStubBuilder builder(this); StringIdInfo info = { constpool, pc, StringIdInfo::Offset::BYTE_1, StringIdInfo::Length::BITS_16 }; - GateRef result = builder.TryLoadGlobalByName(glue, 0, info, profileTypeInfo, slotId); + GateRef result = builder.TryLoadGlobalByName(glue, 0, info, profileTypeInfo, slotId, callback); CHECK_EXCEPTION_WITH_VARACC(result, INT_PTR(TRYLDGLOBALBYNAME_IMM8_ID16)); } @@ -2587,7 +2660,7 @@ DECLARE_ASM_HANDLER(HandleTryldglobalbynameImm16Id16) GateRef slotId = ZExtInt16ToInt32(ReadInst16_0(pc)); AccessObjectStubBuilder builder(this); StringIdInfo info = { constpool, pc, StringIdInfo::Offset::BYTE_2, StringIdInfo::Length::BITS_16 }; - GateRef result = builder.TryLoadGlobalByName(glue, 0, info, profileTypeInfo, slotId); + GateRef result = builder.TryLoadGlobalByName(glue, 0, info, profileTypeInfo, slotId, callback); CHECK_EXCEPTION_WITH_VARACC(result, INT_PTR(TRYLDGLOBALBYNAME_IMM16_ID16)); } @@ -2596,7 +2669,7 @@ DECLARE_ASM_HANDLER(HandleTrystglobalbynameImm8Id16) GateRef slotId = ZExtInt16ToInt32(ReadInst8_0(pc)); AccessObjectStubBuilder builder(this); StringIdInfo info = { constpool, pc, StringIdInfo::Offset::BYTE_1, StringIdInfo::Length::BITS_16 }; - GateRef result = builder.TryStoreGlobalByName(glue, 0, info, acc, profileTypeInfo, slotId); + GateRef result = builder.TryStoreGlobalByName(glue, 0, info, acc, profileTypeInfo, slotId, callback); CHECK_EXCEPTION(result, INT_PTR(TRYSTGLOBALBYNAME_IMM8_ID16)); } @@ -2605,7 +2678,7 @@ DECLARE_ASM_HANDLER(HandleTrystglobalbynameImm16Id16) GateRef slotId = ZExtInt16ToInt32(ReadInst16_0(pc)); AccessObjectStubBuilder builder(this); StringIdInfo info = { constpool, pc, StringIdInfo::Offset::BYTE_2, StringIdInfo::Length::BITS_16 }; - GateRef result = builder.TryStoreGlobalByName(glue, 0, info, acc, profileTypeInfo, slotId); + GateRef result = builder.TryStoreGlobalByName(glue, 0, info, acc, profileTypeInfo, slotId, callback); CHECK_EXCEPTION(result, INT_PTR(TRYSTGLOBALBYNAME_IMM16_ID16)); } @@ -2616,7 +2689,7 @@ DECLARE_ASM_HANDLER(HandleLdglobalvarImm16Id16) GateRef slotId = ZExtInt16ToInt32(ReadInst16_0(pc)); AccessObjectStubBuilder builder(this); StringIdInfo info = { constpool, pc, StringIdInfo::Offset::BYTE_2, StringIdInfo::Length::BITS_16 }; - GateRef result = builder.LoadGlobalVar(glue, 0, info, profileTypeInfo, slotId); + GateRef result = builder.LoadGlobalVar(glue, 0, info, profileTypeInfo, slotId, callback); CHECK_EXCEPTION_WITH_VARACC(result, INT_PTR(LDGLOBALVAR_IMM16_ID16)); } @@ -2828,6 +2901,7 @@ DECLARE_ASM_HANDLER(HandleCreateasyncgeneratorobjV8) DECLARE_ASM_HANDLER(HandleAsyncgeneratorresolveV8V8V8) { auto env = GetEnvironment(); + METHOD_EXIT(); DEFVARIABLE(varPc, VariableType::NATIVE_POINTER(), pc); DEFVARIABLE(varSp, VariableType::NATIVE_POINTER(), sp); DEFVARIABLE(varConstpool, VariableType::JS_POINTER(), constpool); @@ -2838,6 +2912,7 @@ DECLARE_ASM_HANDLER(HandleAsyncgeneratorresolveV8V8V8) Label pcEqualNullptr(env); Label pcNotEqualNullptr(env); Label updateHotness(env); + Label isStable(env); Label tryContinue(env); Label dispatch(env); Label slowPath(env); @@ -2857,7 +2932,12 @@ DECLARE_ASM_HANDLER(HandleAsyncgeneratorresolveV8V8V8) } Bind(¬Exception); varAcc = res; - Branch(TaggedIsUndefined(*varProfileTypeInfo), &updateHotness, &tryContinue); + Branch(TaggedIsUndefined(*varProfileTypeInfo), &updateHotness, &isStable); + Bind(&isStable); + { + Branch(ProfilerStubBuilder(env).IsProfileTypeInfoChanged(*varProfileTypeInfo, callback), &updateHotness, + &tryContinue); + } Bind(&updateHotness); { GateRef function = GetFunctionFromFrame(frame); @@ -3014,7 +3094,7 @@ DECLARE_ASM_HANDLER(HandleLdobjbyvalueImm8V8) GateRef slotId = ZExtInt8ToInt32(ReadInst8_0(pc)); AccessObjectStubBuilder builder(this); - GateRef result = builder.LoadObjByValue(glue, receiver, propKey, profileTypeInfo, slotId); + GateRef result = builder.LoadObjByValue(glue, receiver, propKey, profileTypeInfo, slotId, callback); CHECK_EXCEPTION_WITH_VARACC(result, INT_PTR(LDOBJBYVALUE_IMM8_V8)); } @@ -3028,7 +3108,7 @@ DECLARE_ASM_HANDLER(HandleLdobjbyvalueImm16V8) GateRef slotId = ZExtInt8ToInt32(ReadInst16_0(pc)); AccessObjectStubBuilder builder(this); - GateRef result = builder.LoadObjByValue(glue, receiver, propKey, profileTypeInfo, slotId); + GateRef result = builder.LoadObjByValue(glue, receiver, propKey, profileTypeInfo, slotId, callback); CHECK_EXCEPTION_WITH_VARACC(result, INT_PTR(LDOBJBYVALUE_IMM16_V8)); } @@ -3091,7 +3171,7 @@ DECLARE_ASM_HANDLER(HandleLdobjbyindexImm8Imm16) Branch(TaggedIsHeapObject(receiver), &fastPath, &slowPath); Bind(&fastPath); { - GateRef result = GetPropertyByIndex(glue, receiver, index); + GateRef result = GetPropertyByIndex(glue, receiver, index, callback); Label notHole(env); Branch(TaggedIsHole(result), &slowPath, ¬Hole); Bind(¬Hole); @@ -3116,7 +3196,7 @@ DECLARE_ASM_HANDLER(HandleLdobjbyindexImm16Imm16) Branch(TaggedIsHeapObject(receiver), &fastPath, &slowPath); Bind(&fastPath); { - GateRef result = GetPropertyByIndex(glue, receiver, index); + GateRef result = GetPropertyByIndex(glue, receiver, index, callback); Label notHole(env); Branch(TaggedIsHole(result), &slowPath, ¬Hole); Bind(¬Hole); @@ -3141,7 +3221,7 @@ DECLARE_ASM_HANDLER(HandleWideLdobjbyindexPrefImm32) Branch(TaggedIsHeapObject(receiver), &fastPath, &slowPath); Bind(&fastPath); { - GateRef result = GetPropertyByIndex(glue, receiver, index); + GateRef result = GetPropertyByIndex(glue, receiver, index, callback); Label notHole(env); Branch(TaggedIsHole(result), &slowPath, ¬Hole); Bind(¬Hole); @@ -3167,7 +3247,7 @@ DECLARE_ASM_HANDLER(HandleDeprecatedLdobjbyindexPrefV8Imm32) Branch(TaggedIsHeapObject(receiver), &fastPath, &slowPath); Bind(&fastPath); { - GateRef result = GetPropertyByIndex(glue, receiver, index); + GateRef result = GetPropertyByIndex(glue, receiver, index, callback); Label notHole(env); Branch(TaggedIsHole(result), &slowPath, ¬Hole); Bind(¬Hole); @@ -3451,7 +3531,6 @@ DECLARE_ASM_HANDLER(HandleDefineclasswithbufferImm8Id16Id16Imm16V8) } Bind(&isNotException); SetLexicalEnvToFunction(glue, res, lexicalEnv); - SetModuleToFunction(glue, res, module); CallRuntime(glue, RTSTUB_ID(SetClassConstructorLength), { res, Int16ToTaggedInt(length) }); callback.ProfileDefineClass(res); varAcc = res; @@ -3486,7 +3565,6 @@ DECLARE_ASM_HANDLER(HandleDefineclasswithbufferImm16Id16Id16Imm16V8) } Bind(&isNotException); SetLexicalEnvToFunction(glue, res, lexicalEnv); - SetModuleToFunction(glue, res, module); CallRuntime(glue, RTSTUB_ID(SetClassConstructorLength), { res, Int16ToTaggedInt(length) }); callback.ProfileDefineClass(res); varAcc = res; @@ -3523,7 +3601,6 @@ DECLARE_ASM_HANDLER(HandleDeprecatedDefineclasswithbufferPrefId16Imm16Imm16V8V8) } Bind(&isNotException); SetLexicalEnvToFunction(glue, res, lexicalEnv); - SetModuleToFunction(glue, res, module); CallRuntime(glue, RTSTUB_ID(SetClassConstructorLength), { res, Int16ToTaggedInt(length) }); varAcc = res; DISPATCH_WITH_ACC(DEPRECATED_DEFINECLASSWITHBUFFER_PREF_ID16_IMM16_IMM16_V8_V8); @@ -3569,6 +3646,7 @@ DECLARE_ASM_HANDLER(HandleCallarg0Imm8) { GateRef actualNumArgs = Int32(InterpreterAssembly::ActualNumArgsOfCall::CALLARG0); GateRef func = acc; + METHOD_ENTRY(func); GateRef jumpSize = INT_PTR(CALLARG0_IMM8); GateRef res = JSCallDispatch(glue, func, actualNumArgs, jumpSize, hotnessCounter, JSCallMode::CALL_ARG0, {}, callback); @@ -3591,6 +3669,7 @@ DECLARE_ASM_HANDLER(HandleCallarg1Imm8V8) GateRef actualNumArgs = Int32(InterpreterAssembly::ActualNumArgsOfCall::CALLARG1); GateRef a0 = ReadInst8_1(pc); GateRef func = acc; + METHOD_ENTRY(func); GateRef a0Value = GetVregValue(sp, ZExtInt8ToPtr(a0)); GateRef jumpSize = INT_PTR(CALLARG1_IMM8_V8); GateRef res = JSCallDispatch(glue, func, actualNumArgs, jumpSize, hotnessCounter, @@ -3617,6 +3696,7 @@ DECLARE_ASM_HANDLER(HandleCallargs2Imm8V8V8) GateRef a0 = ReadInst8_1(pc); GateRef a1 = ReadInst8_2(pc); GateRef func = acc; + METHOD_ENTRY(func); GateRef a0Value = GetVregValue(sp, ZExtInt8ToPtr(a0)); GateRef a1Value = GetVregValue(sp, ZExtInt8ToPtr(a1)); GateRef jumpSize = INT_PTR(CALLARGS2_IMM8_V8_V8); @@ -3647,6 +3727,7 @@ DECLARE_ASM_HANDLER(HandleCallargs3Imm8V8V8V8) GateRef a1 = ReadInst8_2(pc); GateRef a2 = ReadInst8_3(pc); GateRef func = acc; + METHOD_ENTRY(func); GateRef a0Value = GetVregValue(sp, ZExtInt8ToPtr(a0)); GateRef a1Value = GetVregValue(sp, ZExtInt8ToPtr(a1)); GateRef a2Value = GetVregValue(sp, ZExtInt8ToPtr(a2)); @@ -3677,6 +3758,7 @@ DECLARE_ASM_HANDLER(HandleCallrangeImm8Imm8V8) { GateRef actualNumArgs = ZExtInt8ToInt32(ReadInst8_1(pc)); GateRef func = acc; + METHOD_ENTRY(func); GateRef argv = PtrAdd(sp, PtrMul(ZExtInt8ToPtr(ReadInst8_2(pc)), IntPtr(8))); // 8: byteSize GateRef jumpSize = INT_PTR(CALLRANGE_IMM8_IMM8_V8); GateRef numArgs = ZExtInt32ToPtr(actualNumArgs); @@ -3689,6 +3771,7 @@ DECLARE_ASM_HANDLER(HandleWideCallrangePrefImm16V8) { GateRef actualNumArgs = ZExtInt16ToInt32(ReadInst16_1(pc)); GateRef func = acc; + METHOD_ENTRY(func); GateRef argv = PtrAdd(sp, PtrMul(ZExtInt8ToPtr(ReadInst8_2(pc)), IntPtr(8))); // 8: byteSize GateRef jumpSize = INT_PTR(WIDE_CALLRANGE_PREF_IMM16_V8); GateRef numArgs = ZExtInt32ToPtr(actualNumArgs); @@ -3716,6 +3799,7 @@ DECLARE_ASM_HANDLER(HandleCallthisrangeImm8Imm8V8) GateRef actualNumArgs = ZExtInt8ToInt32(ReadInst8_1(pc)); GateRef thisReg = ZExtInt8ToPtr(ReadInst8_2(pc)); GateRef func = acc; + METHOD_ENTRY(func); GateRef thisValue = GetVregValue(sp, thisReg); GateRef argv = PtrAdd(sp, PtrMul( PtrAdd(thisReg, IntPtr(1)), IntPtr(8))); // 1: skip this @@ -3731,6 +3815,7 @@ DECLARE_ASM_HANDLER(HandleWideCallthisrangePrefImm16V8) GateRef actualNumArgs = ZExtInt16ToInt32(ReadInst16_1(pc)); GateRef thisReg = ZExtInt8ToPtr(ReadInst8_3(pc)); GateRef func = acc; + METHOD_ENTRY(func); GateRef thisValue = GetVregValue(sp, thisReg); GateRef argv = PtrAdd(sp, PtrMul( PtrAdd(thisReg, IntPtr(1)), IntPtr(8))); // 1: skip this @@ -3762,6 +3847,7 @@ DECLARE_ASM_HANDLER(HandleCallthis0Imm8V8) GateRef actualNumArgs = Int32(InterpreterAssembly::ActualNumArgsOfCall::CALLARG0); GateRef thisValue = GetVregValue(sp, ZExtInt8ToPtr(ReadInst8_1(pc))); GateRef func = acc; + METHOD_ENTRY(func); GateRef jumpSize = INT_PTR(CALLTHIS0_IMM8_V8); GateRef res = JSCallDispatch(glue, func, actualNumArgs, jumpSize, hotnessCounter, JSCallMode::CALL_THIS_ARG0, { thisValue }, callback); @@ -3774,6 +3860,7 @@ DECLARE_ASM_HANDLER(HandleCallthis1Imm8V8V8) GateRef thisValue = GetVregValue(sp, ZExtInt8ToPtr(ReadInst8_1(pc))); GateRef a0 = ReadInst8_2(pc); GateRef func = acc; + METHOD_ENTRY(func); GateRef a0Value = GetVregValue(sp, ZExtInt8ToPtr(a0)); GateRef jumpSize = INT_PTR(CALLTHIS1_IMM8_V8_V8); GateRef res = JSCallDispatch(glue, func, actualNumArgs, jumpSize, hotnessCounter, @@ -3788,6 +3875,7 @@ DECLARE_ASM_HANDLER(HandleCallthis2Imm8V8V8V8) GateRef a0 = ReadInst8_2(pc); GateRef a1 = ReadInst8_3(pc); GateRef func = acc; + METHOD_ENTRY(func); GateRef a0Value = GetVregValue(sp, ZExtInt8ToPtr(a0)); GateRef a1Value = GetVregValue(sp, ZExtInt8ToPtr(a1)); GateRef jumpSize = INT_PTR(CALLTHIS2_IMM8_V8_V8_V8); @@ -3804,6 +3892,7 @@ DECLARE_ASM_HANDLER(HandleCallthis3Imm8V8V8V8V8) GateRef a1 = ReadInst8_3(pc); GateRef a2 = ReadInst8_4(pc); GateRef func = acc; + METHOD_ENTRY(func); GateRef a0Value = GetVregValue(sp, ZExtInt8ToPtr(a0)); GateRef a1Value = GetVregValue(sp, ZExtInt8ToPtr(a1)); GateRef a2Value = GetVregValue(sp, ZExtInt8ToPtr(a2)); @@ -3817,9 +3906,11 @@ DECLARE_ASM_HANDLER(HandleCreatearraywithbufferImm8Id16) { GateRef imm = ZExtInt16ToInt32(ReadInst16_1(pc)); GateRef currentFunc = GetFunctionFromFrame(GetFrame(sp)); + GateRef slotId = ZExtInt8ToInt32(ReadInst8_0(pc)); NewObjectStubBuilder newBuilder(this); - GateRef res = newBuilder.CreateArrayWithBuffer(glue, imm, currentFunc); + GateRef res = newBuilder.CreateArrayWithBuffer(glue, imm, currentFunc, pc, + profileTypeInfo, slotId, callback); CHECK_EXCEPTION_WITH_ACC(res, INT_PTR(CREATEARRAYWITHBUFFER_IMM8_ID16)); } @@ -3827,9 +3918,11 @@ DECLARE_ASM_HANDLER(HandleCreatearraywithbufferImm16Id16) { GateRef imm = ZExtInt16ToInt32(ReadInst16_2(pc)); GateRef currentFunc = GetFunctionFromFrame(GetFrame(sp)); + GateRef slotId = ZExtInt16ToInt32(ReadInst16_0(pc)); NewObjectStubBuilder newBuilder(this); - GateRef res = newBuilder.CreateArrayWithBuffer(glue, imm, currentFunc); + GateRef res = newBuilder.CreateArrayWithBuffer(glue, imm, currentFunc, pc, + profileTypeInfo, slotId, callback); CHECK_EXCEPTION_WITH_ACC(res, INT_PTR(CREATEARRAYWITHBUFFER_IMM16_ID16)); } @@ -3837,9 +3930,11 @@ DECLARE_ASM_HANDLER(HandleDeprecatedCreatearraywithbufferPrefImm16) { GateRef imm = ZExtInt16ToInt32(ReadInst16_1(pc)); GateRef currentFunc = GetFunctionFromFrame(GetFrame(sp)); + GateRef slotId = ZExtInt8ToInt32(ReadInst8_0(pc)); NewObjectStubBuilder newBuilder(this); - GateRef res = newBuilder.CreateArrayWithBuffer(glue, imm, currentFunc); + GateRef res = newBuilder.CreateArrayWithBuffer(glue, imm, currentFunc, pc, + profileTypeInfo, slotId, callback); CHECK_EXCEPTION_WITH_ACC(res, INT_PTR(DEPRECATED_CREATEARRAYWITHBUFFER_PREF_IMM16)); } @@ -3851,6 +3946,7 @@ DECLARE_ASM_HANDLER(HandleCreateobjectwithbufferImm8Id16) GateRef result = GetObjectLiteralFromConstPool(glue, constpool, imm, module); GateRef currentEnv = GetEnvFromFrame(GetFrame(sp)); GateRef res = CallRuntime(glue, RTSTUB_ID(CreateObjectHavingMethod), { result, currentEnv }); + callback.ProfileCreateObject(res); CHECK_EXCEPTION_WITH_ACC(res, INT_PTR(CREATEOBJECTWITHBUFFER_IMM8_ID16)); } @@ -3862,6 +3958,7 @@ DECLARE_ASM_HANDLER(HandleCreateobjectwithbufferImm16Id16) GateRef result = GetObjectLiteralFromConstPool(glue, constpool, imm, module); GateRef currentEnv = GetEnvFromFrame(GetFrame(sp)); GateRef res = CallRuntime(glue, RTSTUB_ID(CreateObjectHavingMethod), { result, currentEnv }); + callback.ProfileCreateObject(res); CHECK_EXCEPTION_WITH_ACC(res, INT_PTR(CREATEOBJECTWITHBUFFER_IMM16_ID16)); } @@ -3916,6 +4013,7 @@ DECLARE_ASM_HANDLER(HandleNewobjrangeImm8Imm8V8) GateRef argv = PtrAdd(sp, PtrMul( PtrAdd(firstArgRegIdx, firstArgOffset), IntPtr(8))); // 8: skip function GateRef jumpSize = IntPtr(-BytecodeInstruction::Size(BytecodeInstruction::Format::IMM8_IMM8_V8)); + METHOD_ENTRY_ENV_DEFINED(ctor); res = JSCallDispatch(glue, ctor, actualNumArgs, jumpSize, hotnessCounter, JSCallMode::CALL_CONSTRUCTOR_WITH_ARGV, { ZExtInt32ToPtr(actualNumArgs), argv, *thisObj }, callback); @@ -3986,6 +4084,7 @@ DECLARE_ASM_HANDLER(HandleNewobjrangeImm16Imm8V8) PtrAdd(firstArgRegIdx, firstArgOffset), IntPtr(8))); // 8: skip function GateRef jumpSize = IntPtr(-static_cast(BytecodeInstruction::Size(BytecodeInstruction::Format::IMM16_IMM8_V8))); + METHOD_ENTRY_ENV_DEFINED(ctor); res = JSCallDispatch(glue, ctor, actualNumArgs, jumpSize, hotnessCounter, JSCallMode::CALL_CONSTRUCTOR_WITH_ARGV, { ZExtInt32ToPtr(actualNumArgs), argv, *thisObj }, callback); @@ -4090,7 +4189,7 @@ DECLARE_ASM_HANDLER(HandleDefinefuncImm8Id16Imm8) GateRef methodId = ReadInst16_1(pc); GateRef length = ReadInst8_3(pc); DEFVARIABLE(result, VariableType::JS_POINTER(), - GetMethodFromConstPool(glue, constpool, ZExtInt16ToInt32(methodId))); + GetMethodFromConstPool(glue, constpool, GetModule(sp), ZExtInt16ToInt32(methodId))); result = CallRuntime(glue, RTSTUB_ID(DefineFunc), { *result }); Label notException(env); CHECK_EXCEPTION_WITH_JUMP(*result, ¬Exception); @@ -4103,7 +4202,6 @@ DECLARE_ASM_HANDLER(HandleDefinefuncImm8Id16Imm8) GateRef envHandle = GetEnvFromFrame(frame); SetLexicalEnvToFunction(glue, *result, envHandle); GateRef currentFunc = GetFunctionFromFrame(frame); - SetModuleToFunction(glue, *result, GetModuleFromFunction(currentFunc)); SetHomeObjectToFunction(glue, *result, GetHomeObjectFromFunction(currentFunc)); varAcc = *result; DISPATCH_WITH_ACC(DEFINEFUNC_IMM8_ID16_IMM8); @@ -4117,7 +4215,7 @@ DECLARE_ASM_HANDLER(HandleDefinefuncImm16Id16Imm8) GateRef methodId = ReadInst16_2(pc); GateRef length = ReadInst8_4(pc); DEFVARIABLE(result, VariableType::JS_POINTER(), - GetMethodFromConstPool(glue, constpool, ZExtInt16ToInt32(methodId))); + GetMethodFromConstPool(glue, constpool, GetModule(sp), ZExtInt16ToInt32(methodId))); result = CallRuntime(glue, RTSTUB_ID(DefineFunc), { *result }); Label notException(env); CHECK_EXCEPTION_WITH_JUMP(*result, ¬Exception); @@ -4130,7 +4228,6 @@ DECLARE_ASM_HANDLER(HandleDefinefuncImm16Id16Imm8) GateRef envHandle = GetEnvFromFrame(frame); SetLexicalEnvToFunction(glue, *result, envHandle); GateRef currentFunc = GetFunctionFromFrame(frame); - SetModuleToFunction(glue, *result, GetModuleFromFunction(currentFunc)); SetHomeObjectToFunction(glue, *result, GetHomeObjectFromFunction(currentFunc)); varAcc = *result; DISPATCH_WITH_ACC(DEFINEFUNC_IMM16_ID16_IMM8); @@ -4144,7 +4241,7 @@ DECLARE_ASM_HANDLER(HandleDefinemethodImm8Id16Imm8) GateRef methodId = ReadInst16_1(pc); GateRef length = ReadInst8_3(pc); DEFVARIABLE(result, VariableType::JS_POINTER(), - GetMethodFromConstPool(glue, constpool, ZExtInt16ToInt32(methodId))); + GetMethodFromConstPool(glue, constpool, GetModule(sp), ZExtInt16ToInt32(methodId))); result = CallRuntime(glue, RTSTUB_ID(DefineMethod), { *result, acc }); Label notException(env); CHECK_EXCEPTION_WITH_JUMP(*result, ¬Exception); @@ -4155,8 +4252,6 @@ DECLARE_ASM_HANDLER(HandleDefinemethodImm8Id16Imm8) Int32(JSFunction::LENGTH_INLINE_PROPERTY_INDEX), VariableType::INT64()); GateRef lexEnv = GetEnvFromFrame(GetFrame(sp)); SetLexicalEnvToFunction(glue, *result, lexEnv); - GateRef currentFunc = GetFunctionFromFrame(GetFrame(sp)); - SetModuleToFunction(glue, *result, GetModuleFromFunction(currentFunc)); varAcc = *result; DISPATCH_WITH_ACC(DEFINEMETHOD_IMM8_ID16_IMM8); } @@ -4169,7 +4264,7 @@ DECLARE_ASM_HANDLER(HandleDefinemethodImm16Id16Imm8) GateRef methodId = ReadInst16_2(pc); GateRef length = ReadInst8_4(pc); DEFVARIABLE(result, VariableType::JS_POINTER(), - GetMethodFromConstPool(glue, constpool, ZExtInt16ToInt32(methodId))); + GetMethodFromConstPool(glue, constpool, GetModule(sp), ZExtInt16ToInt32(methodId))); result = CallRuntime(glue, RTSTUB_ID(DefineMethod), { *result, acc }); Label notException(env); CHECK_EXCEPTION_WITH_JUMP(*result, ¬Exception); @@ -4180,8 +4275,6 @@ DECLARE_ASM_HANDLER(HandleDefinemethodImm16Id16Imm8) Int32(JSFunction::LENGTH_INLINE_PROPERTY_INDEX), VariableType::INT64()); GateRef lexEnv = GetEnvFromFrame(GetFrame(sp)); SetLexicalEnvToFunction(glue, *result, lexEnv); - GateRef currentFunc = GetFunctionFromFrame(GetFrame(sp)); - SetModuleToFunction(glue, *result, GetModuleFromFunction(currentFunc)); varAcc = *result; DISPATCH_WITH_ACC(DEFINEMETHOD_IMM16_ID16_IMM8); } @@ -4354,7 +4447,7 @@ DECLARE_ASM_HANDLER(HandleLdthisbyvalueImm16) GateRef slotId = ZExtInt16ToInt32(ReadInst16_0(pc)); AccessObjectStubBuilder builder(this); - GateRef result = builder.LoadObjByValue(glue, receiver, propKey, profileTypeInfo, slotId); + GateRef result = builder.LoadObjByValue(glue, receiver, propKey, profileTypeInfo, slotId, callback); CHECK_EXCEPTION_WITH_VARACC(result, INT_PTR(LDTHISBYVALUE_IMM16)); } DECLARE_ASM_HANDLER(HandleLdthisbyvalueImm8) @@ -4366,7 +4459,7 @@ DECLARE_ASM_HANDLER(HandleLdthisbyvalueImm8) GateRef slotId = ZExtInt8ToInt32(ReadInst8_0(pc)); AccessObjectStubBuilder builder(this); - GateRef result = builder.LoadObjByValue(glue, receiver, propKey, profileTypeInfo, slotId); + GateRef result = builder.LoadObjByValue(glue, receiver, propKey, profileTypeInfo, slotId, callback); CHECK_EXCEPTION_WITH_VARACC(result, INT_PTR(LDTHISBYVALUE_IMM8)); } DECLARE_ASM_HANDLER(HandleStthisbynameImm16Id16) @@ -4578,10 +4671,80 @@ DECLARE_ASM_HANDLER(SingleStepDebugging) DECLARE_ASM_HANDLER(BCDebuggerEntry) { + auto env = GetEnvironment(); + Label callByteCodeChanged(env); + Label isFrameDroppedTrue(env); + Label isFrameDroppedFalse(env); + Label isEntryFrameDroppedPending(env); + Label isEntryFrameDroppedNotTrue(env); + Label pcEqualNullptr(env); + Label pcNotEqualNullptr(env); GateRef frame = GetFrame(sp); + GateRef isEntryFrameDropped = Load(VariableType::INT8(), glue, + IntPtr(JSThread::GlueData::GetEntryFrameDroppedStateOffset(env->Is32Bit()))); + Branch(Int8Equal(isEntryFrameDropped, Int8(JSThread::FrameDroppedState::StatePending)), + &isEntryFrameDroppedPending, &callByteCodeChanged); + Bind(&isEntryFrameDroppedPending); + { + Store(VariableType::INT8(), glue, glue, + IntPtr(JSThread::GlueData::GetEntryFrameDroppedStateOffset(env->Is32Bit())), + Int8(JSThread::FrameDroppedState::StateFalse)); + DEFVARIABLE(varPc, VariableType::NATIVE_POINTER(), pc); + DEFVARIABLE(varAcc, VariableType::JS_ANY(), acc); + varPc = GetPcFromFrame(frame); + varAcc = GetAccFromFrame(frame); + Dispatch(glue, sp, *varPc, constpool, profileTypeInfo, *varAcc, hotnessCounter, IntPtr(0)); + } + Bind(&callByteCodeChanged); SetPcToFrame(glue, frame, pc); // NOTIFY_DEBUGGER_EVENT() CallRuntime(glue, RTSTUB_ID(NotifyBytecodePcChanged), {}); + GateRef isFrameDropped = Load(VariableType::BOOL(), glue, + IntPtr(JSThread::GlueData::GetIsFrameDroppedOffset(env->Is32Bit()))); + Branch(isFrameDropped, &isFrameDroppedTrue, &isFrameDroppedFalse); + Bind(&isFrameDroppedTrue); + { + DEFVARIABLE(varPc, VariableType::NATIVE_POINTER(), pc); + DEFVARIABLE(varSp, VariableType::NATIVE_POINTER(), sp); + DEFVARIABLE(varConstpool, VariableType::JS_POINTER(), constpool); + DEFVARIABLE(varProfileTypeInfo, VariableType::JS_POINTER(), profileTypeInfo); + DEFVARIABLE(varAcc, VariableType::JS_ANY(), acc); + DEFVARIABLE(varHotnessCounter, VariableType::INT32(), hotnessCounter); + GateRef state = GetFrame(*varSp); + GateRef currentSp = *varSp; + Store(VariableType::BOOL(), glue, glue, + IntPtr(JSThread::GlueData::GetIsFrameDroppedOffset(env->Is32Bit())), False()); + varSp = Load(VariableType::NATIVE_POINTER(), state, + IntPtr(AsmInterpretedFrame::GetBaseOffset(env->IsArch32Bit()))); + isEntryFrameDropped = Load(VariableType::INT8(), glue, + IntPtr(JSThread::GlueData::GetEntryFrameDroppedStateOffset(env->Is32Bit()))); + Branch(Int8Equal(isEntryFrameDropped, Int8(JSThread::FrameDroppedState::StateTrue)), + &pcEqualNullptr, &isEntryFrameDroppedNotTrue); + Bind(&isEntryFrameDroppedNotTrue); + GateRef prevState = GetFrame(*varSp); + varPc = GetPcFromFrame(prevState); + Branch(IntPtrEqual(*varPc, IntPtr(0)), &pcEqualNullptr, &pcNotEqualNullptr); + Bind(&pcEqualNullptr); + { + CallNGCRuntime(glue, RTSTUB_ID(ResumeRspAndReturn), { *varAcc, *varSp, currentSp }); + Return(); + } + Bind(&pcNotEqualNullptr); + { + GateRef function = GetFunctionFromFrame(prevState); + GateRef method = Load(VariableType::JS_ANY(), function, IntPtr(JSFunctionBase::METHOD_OFFSET)); + varConstpool = GetConstpoolFromMethod(method); + varProfileTypeInfo = GetProfileTypeInfoFromMethod(method); + varHotnessCounter = GetHotnessCounterFromMethod(method); + GateRef jumpSize = IntPtr(0); + CallNGCRuntime(glue, RTSTUB_ID(ResumeRspAndRollback), + { glue, currentSp, *varPc, *varConstpool, *varProfileTypeInfo, + *varAcc, *varHotnessCounter, jumpSize }); + Return(); + } + } + Bind(&isFrameDroppedFalse); + SetAccToFrame(glue, frame, acc); // goto normal handle stub DispatchDebugger(glue, sp, pc, constpool, profileTypeInfo, acc, hotnessCounter); } diff --git a/ecmascript/compiler/interpreter_stub.h b/ecmascript/compiler/interpreter_stub.h index 4e81b734b5657375c3724874da67ed4b403a01c4..69e97dd4ead6eb10fd4667dd8e4930ceddfe7a72 100644 --- a/ecmascript/compiler/interpreter_stub.h +++ b/ecmascript/compiler/interpreter_stub.h @@ -98,6 +98,7 @@ public: inline GateRef GetEnvFromFrame(GateRef frame); inline GateRef GetEnvFromFunction(GateRef frame); inline GateRef GetConstpoolFromMethod(GateRef function); + inline GateRef GetModule(GateRef sp); inline GateRef GetProfileTypeInfoFromMethod(GateRef function); inline GateRef GetModuleFromFunction(GateRef function); inline GateRef GetHomeObjectFromFunction(GateRef function); @@ -113,7 +114,6 @@ public: inline void SetAccToFrame(GateRef glue, GateRef frame, GateRef value); inline void SetEnvToFrame(GateRef glue, GateRef frame, GateRef value); inline void SetHomeObjectToFunction(GateRef glue, GateRef function, GateRef value); - inline void SetModuleToFunction(GateRef glue, GateRef function, GateRef value); inline void SetFrameState(GateRef glue, GateRef sp, GateRef function, GateRef acc, GateRef env, GateRef pc, GateRef prev, GateRef type); @@ -187,7 +187,7 @@ public: INTERPRETER_BC_STUB_LIST(DECLARE_HANDLE_STUB_CLASS) ASM_INTERPRETER_BC_HELPER_STUB_LIST(DECLARE_HANDLE_STUB_CLASS) -#define DECLARE_HANDLE_PROFILE_STUB_CLASS(name, base) \ +#define DECLARE_HANDLE_PROFILE_STUB_CLASS(name, base, ...) \ class name##StubBuilder : public base##StubBuilder { \ public: \ explicit name##StubBuilder(CallSignature *callSignature, Environment *env) \ diff --git a/ecmascript/compiler/later_elimination.cpp b/ecmascript/compiler/later_elimination.cpp index 940de6d5983d9800ee218f391b151fb801db865f..00bfeb65b79963df25807cb368e692d17faa0b5a 100644 --- a/ecmascript/compiler/later_elimination.cpp +++ b/ecmascript/compiler/later_elimination.cpp @@ -15,29 +15,17 @@ #include "ecmascript/compiler/later_elimination.h" namespace panda::ecmascript::kungfu { -void LaterElimination::Run() + +void LaterElimination::Initialize() { dependChains_.resize(circuit_->GetMaxGateId() + 1, nullptr); // 1: +1 for size GateRef entry = acc_.GetDependRoot(); VisitDependEntry(entry); - VisitGraph(); - - if (IsLogEnabled()) { - LOG_COMPILER(INFO) << ""; - LOG_COMPILER(INFO) << "\033[34m" - << "====================" - << " After late elimination " - << "[" << GetMethodName() << "]" - << "====================" - << "\033[0m"; - circuit_->PrintAllGatesWithBytecode(); - LOG_COMPILER(INFO) << "\033[34m" << "========================= End ==========================" << "\033[0m"; - } } GateRef LaterElimination::VisitDependEntry(GateRef gate) { - auto empty = new (chunk_) DependChainNodes(chunk_); + auto empty = new (chunk_) DependChains(chunk_); return UpdateDependChain(gate, empty); } @@ -47,11 +35,22 @@ GateRef LaterElimination::VisitGate(GateRef gate) switch (opcode) { case OpCode::GET_CONSTPOOL: case OpCode::GET_GLOBAL_ENV: + case OpCode::GET_GLOBAL_ENV_OBJ: case OpCode::GET_GLOBAL_ENV_OBJ_HCLASS: case OpCode::GET_GLOBAL_CONSTANT_VALUE: case OpCode::ARRAY_GUARDIAN_CHECK: case OpCode::HCLASS_STABLE_ARRAY_CHECK: case OpCode::HEAP_OBJECT_CHECK: + case OpCode::INT32_UNSIGNED_UPPER_BOUND_CHECK: + case OpCode::OVERFLOW_CHECK: + case OpCode::VALUE_CHECK_NEG_OVERFLOW: + case OpCode::FLOAT64_CHECK_RIGHT_IS_ZERO: + case OpCode::INT32_CHECK_RIGHT_IS_ZERO: + case OpCode::INT32_DIV_WITH_CHECK: + case OpCode::LEX_VAR_IS_HOLE_CHECK: + case OpCode::COW_ARRAY_CHECK: + case OpCode::FLATTEN_TREE_STRING_CHECK: + case OpCode::CHECK_AND_CONVERT: return TryEliminateGate(gate); case OpCode::DEPEND_SELECTOR: return TryEliminateDependSelector(gate); @@ -113,7 +112,7 @@ GateRef LaterElimination::TryEliminateDependSelector(GateRef gate) // all depend done. auto depend = acc_.GetDep(gate); auto dependChain = GetDependChain(depend); - DependChainNodes* copy = new (chunk_) DependChainNodes(chunk_); + DependChains* copy = new (chunk_) DependChains(chunk_); copy->CopyFrom(dependChain); for (size_t i = 1; i < dependCount; ++i) { // 1: second in auto dependIn = acc_.GetDep(gate, i); @@ -123,7 +122,7 @@ GateRef LaterElimination::TryEliminateDependSelector(GateRef gate) return UpdateDependChain(gate, copy); } -GateRef LaterElimination::UpdateDependChain(GateRef gate, DependChainNodes* dependChain) +GateRef LaterElimination::UpdateDependChain(GateRef gate, DependChains* dependChain) { ASSERT(dependChain != nullptr); auto oldDependChain = GetDependChain(gate); @@ -148,77 +147,27 @@ bool LaterElimination::CheckReplacement(GateRef lhs, GateRef rhs) } } auto opcode = acc_.GetOpCode(lhs); - if (opcode == OpCode::GET_GLOBAL_ENV_OBJ_HCLASS || - opcode == OpCode::GET_GLOBAL_CONSTANT_VALUE) { - if (acc_.GetIndex(lhs) != acc_.GetIndex(rhs)) { - return false; + switch (opcode) { + case OpCode::GET_GLOBAL_ENV_OBJ: + case OpCode::GET_GLOBAL_ENV_OBJ_HCLASS: + case OpCode::GET_GLOBAL_CONSTANT_VALUE: { + if (acc_.GetIndex(lhs) != acc_.GetIndex(rhs)) { + return false; + } + break; } - } - return true; -} - -void DependChainNodes::Merge(DependChainNodes* that) -{ - // find common sub list - while (size_ > that->size_) { - head_ = head_->next; - size_--; - } - - auto lhs = this->head_; - auto rhs = that->head_; - size_t rhsSize = that->size_; - while (rhsSize > size_) { - rhs = rhs->next; - rhsSize--; - } - while (lhs != rhs) { - ASSERT(lhs != nullptr); - lhs = lhs->next; - rhs = rhs->next; - size_--; - } - head_ = lhs; -} - -bool DependChainNodes::Equals(DependChainNodes* that) -{ - if (that == nullptr) { - return false; - } - if (size_ != that->size_) { - return false; - } - auto lhs = this->head_; - auto rhs = that->head_; - while (lhs != rhs) { - if (lhs->gate != rhs->gate) { - return false; + case OpCode::CHECK_AND_CONVERT: { + if (acc_.GetSrcType(lhs) != acc_.GetSrcType(rhs)) { + return false; + } + if (acc_.GetDstType(lhs) != acc_.GetDstType(rhs)) { + return false; + } + break; } - lhs = lhs->next; - rhs = rhs->next; + default: + break; } return true; } - -GateRef DependChainNodes::LookupNode(LaterElimination* elimination, GateRef gate) -{ - for (Node* node = head_; node != nullptr; node = node->next) { - if (elimination->CheckReplacement(node->gate, gate)) { - return node->gate; - } - } - return Circuit::NullGate(); -} - -DependChainNodes* DependChainNodes::UpdateNode(GateRef gate) -{ - // assign node->next to head - Node* node = chunk_->New(gate, head_); - DependChainNodes* that = new (chunk_) DependChainNodes(chunk_); - // assign head to node - that->head_ = node; - that->size_ = size_ + 1; - return that; -} } // namespace panda::ecmascript::kungfu diff --git a/ecmascript/compiler/later_elimination.h b/ecmascript/compiler/later_elimination.h index 0c08f8cbec8bce20ff952f6a709cef368d0969fe..ee99cf3db7ed89042c4545285cd2aaba4df01247 100644 --- a/ecmascript/compiler/later_elimination.h +++ b/ecmascript/compiler/later_elimination.h @@ -17,63 +17,26 @@ #define ECMASCRIPT_COMPILER_LATER_ELIMINATION_H #include "ecmascript/compiler/circuit_builder.h" +#include "ecmascript/compiler/combined_pass_visitor.h" #include "ecmascript/compiler/gate_accessor.h" -#include "ecmascript/compiler/graph_visitor.h" +#include "ecmascript/compiler/base/depend_chain_helper.h" #include "ecmascript/mem/chunk_containers.h" namespace panda::ecmascript::kungfu { -class LaterElimination; - -class DependChainNodes : public ChunkObject { +class DependChains; +class LaterElimination : public PassVisitor { public: - DependChainNodes(Chunk* chunk) : chunk_(chunk) {} - ~DependChainNodes() = default; - - GateRef LookupNode(LaterElimination* elimination, GateRef gate); - DependChainNodes* UpdateNode(GateRef gate); - bool Equals(DependChainNodes* that); - void Merge(DependChainNodes* that); - void CopyFrom(DependChainNodes *other) - { - head_ = other->head_; - size_ = other->size_; - } -private: - struct Node { - Node(GateRef gate, Node* next) : gate(gate), next(next) {} - GateRef gate; - Node *next; - }; - - Node *head_{nullptr}; - size_t size_ {0}; - Chunk* chunk_; -}; - -class LaterElimination : public GraphVisitor { -public: - LaterElimination(Circuit *circuit, bool enableLog, const std::string& name, Chunk* chunk) - : GraphVisitor(circuit, chunk), enableLog_(enableLog), - methodName_(name), dependChains_(chunk) {} + LaterElimination(Circuit* circuit, RPOVisitor* visitor, Chunk* chunk) + : PassVisitor(circuit, chunk, visitor), dependChains_(chunk) {} ~LaterElimination() = default; - void Run(); - + void Initialize() override; GateRef VisitGate(GateRef gate) override; bool CheckReplacement(GateRef lhs, GateRef rhs); private: - bool IsLogEnabled() const - { - return enableLog_; - } - - const std::string& GetMethodName() const - { - return methodName_; - } - DependChainNodes* GetDependChain(GateRef dependIn) + DependChains* GetDependChain(GateRef dependIn) { size_t idx = acc_.GetId(dependIn); ASSERT(idx <= circuit_->GetMaxGateId()); @@ -81,14 +44,12 @@ private: } GateRef VisitDependEntry(GateRef gate); - GateRef UpdateDependChain(GateRef gate, DependChainNodes* dependInfo); + GateRef UpdateDependChain(GateRef gate, DependChains* dependInfo); GateRef TryEliminateGate(GateRef gate); GateRef TryEliminateOther(GateRef gate); GateRef TryEliminateDependSelector(GateRef gate); - bool enableLog_ {false}; - std::string methodName_; - ChunkVector dependChains_; + ChunkVector dependChains_; }; } // panda::ecmascript::kungfu #endif // ECMASCRIPT_COMPILER_LATER_ELIMINATION_H \ No newline at end of file diff --git a/ecmascript/compiler/lcr_gate_meta_data.cpp b/ecmascript/compiler/lcr_gate_meta_data.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4b9de96fa20406a503f1c475f8d7c9707cab656d --- /dev/null +++ b/ecmascript/compiler/lcr_gate_meta_data.cpp @@ -0,0 +1,48 @@ +/* + * 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/compiler/gate.h" +#include "ecmascript/compiler/lcr_gate_meta_data.h" +#include "ecmascript/compiler/gate_meta_data_builder.h" + +namespace panda::ecmascript::kungfu { + +std::string MachineTypeToStr(MachineType machineType) +{ + switch (machineType) { + case NOVALUE: + return "NOVALUE"; + case ANYVALUE: + return "ANYVALUE"; + case I1: + return "I1"; + case I8: + return "I8"; + case I16: + return "I16"; + case I32: + return "I32"; + case I64: + return "I64"; + case F32: + return "F32"; + case F64: + return "F64"; + default: + return "???"; + } +} + +} \ No newline at end of file diff --git a/ecmascript/compiler/lcr_gate_meta_data.h b/ecmascript/compiler/lcr_gate_meta_data.h new file mode 100644 index 0000000000000000000000000000000000000000..39c0b2f29446cca7715017f7d6b87ba4c47a31a4 --- /dev/null +++ b/ecmascript/compiler/lcr_gate_meta_data.h @@ -0,0 +1,149 @@ +/* + * 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_COMPILER_LCR_GATE_META_DATA_H +#define ECMASCRIPT_COMPILER_LCR_GATE_META_DATA_H + +#include + +#include "ecmascript/compiler/bytecodes.h" +#include "ecmascript/compiler/type.h" +#include "ecmascript/mem/chunk.h" +#include "ecmascript/mem/chunk_containers.h" + +#include "ecmascript/elements.h" +#include "ecmascript/pgo_profiler/types/pgo_profiler_type.h" +#include "libpandabase/macros.h" + +#include "ecmascript/compiler/share_gate_meta_data.h" + +namespace panda::ecmascript::kungfu { + +#define LCR_BINARY_GATE_META_DATA_CACHE_LIST(V) \ + V(Add, ADD, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Sub, SUB, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Mul, MUL, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Exp, EXP, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Sdiv, SDIV, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Smod, SMOD, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Udiv, UDIV, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Umod, UMOD, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Fdiv, FDIV, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Fmod, FMOD, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(And, AND, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Xor, XOR, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Or, OR, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Lsl, LSL, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Lsr, LSR, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Asr, ASR, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Sqrt, SQRT, GateFlags::NO_WRITE, 0, 0, 1) \ + V(AddWithOverflow, ADD_WITH_OVERFLOW, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(SubWithOverflow, SUB_WITH_OVERFLOW, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(MulWithOverflow, MUL_WITH_OVERFLOW, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(ExtractValue, EXTRACT_VALUE, GateFlags::NONE_FLAG, 0, 0, 2) + +#define LCR_UNARY_GATE_META_DATA_CACHE_LIST(V) \ + V(Zext, ZEXT, GateFlags::NONE_FLAG, 0, 0, 1) \ + V(Sext, SEXT, GateFlags::NONE_FLAG, 0, 0, 1) \ + V(Trunc, TRUNC, GateFlags::NONE_FLAG, 0, 0, 1) \ + V(Fext, FEXT, GateFlags::NONE_FLAG, 0, 0, 1) \ + V(Ftrunc, FTRUNC, GateFlags::NONE_FLAG, 0, 0, 1) \ + V(Rev, REV, GateFlags::NONE_FLAG, 0, 0, 1) \ + V(TaggedToInt64, TAGGED_TO_INT64, GateFlags::NONE_FLAG, 0, 0, 1) \ + V(Int64ToTagged, INT64_TO_TAGGED, GateFlags::NONE_FLAG, 0, 0, 1) \ + V(SignedIntToFloat, SIGNED_INT_TO_FLOAT, GateFlags::NONE_FLAG, 0, 0, 1) \ + V(UnsignedIntToFloat, UNSIGNED_INT_TO_FLOAT, GateFlags::NONE_FLAG, 0, 0, 1) \ + V(FloatToSignedInt, FLOAT_TO_SIGNED_INT, GateFlags::NONE_FLAG, 0, 0, 1) \ + V(UnsignedFloatToInt, UNSIGNED_FLOAT_TO_INT, GateFlags::NONE_FLAG, 0, 0, 1) \ + V(TruncFloatToInt64, TRUNC_FLOAT_TO_INT64, GateFlags::NONE_FLAG, 0, 0, 1) \ + V(TruncFloatToInt32, TRUNC_FLOAT_TO_INT32, GateFlags::NONE_FLAG, 0, 0, 1) \ + V(Bitcast, BITCAST, GateFlags::NONE_FLAG, 0, 0, 1) + +#define LCR_IMMUTABLE_META_DATA_CACHE_LIST(V) \ + V(ReadSp, READSP, GateFlags::NONE_FLAG, 0, 0, 0) \ + V(Load, LOAD, GateFlags::NO_WRITE, 0, 1, 1) \ + V(Store, STORE, GateFlags::NONE_FLAG, 0, 1, 2) \ + LCR_BINARY_GATE_META_DATA_CACHE_LIST(V) \ + LCR_UNARY_GATE_META_DATA_CACHE_LIST(V) + +#define LCR_GATE_META_DATA_LIST_WITH_BOOL_VALUE_IN(V) \ + V(CallOptimized, CALL_OPTIMIZED, GateFlags::NONE_FLAG, 0, 1, value) \ + V(FastCallOptimized, FAST_CALL_OPTIMIZED, GateFlags::NONE_FLAG, 0, 1, value) + +#define LCR_GATE_META_DATA_LIST_WITH_VALUE(V) \ + V(Icmp, ICMP, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Fcmp, FCMP, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Alloca, ALLOCA, GateFlags::NONE_FLAG, 0, 0, 0) + +enum MachineType : uint8_t { // Bit width + NOVALUE = 0, + ANYVALUE, + ARCH, + FLEX, + I1, + I8, + I16, + I32, + I64, + F32, + F64, +}; + +enum class ICmpCondition : uint8_t { + EQ = 1, + UGT, + UGE, + ULT, + ULE, + NE, + SGT, + SGE, + SLT, + SLE, +}; + +enum class FCmpCondition : uint8_t { + ALW_FALSE = 0, + OEQ, + OGT, + OGE, + OLT, + OLE, + ONE, + ORD, + UNO, + UEQ, + UGT, + UGE, + ULT, + ULE, + UNE, + ALW_TRUE, +}; + +std::string MachineTypeToStr(MachineType machineType); + +class BranchWeight { +public: + static constexpr uint32_t ZERO_WEIGHT = 0; + static constexpr uint32_t ONE_WEIGHT = 1; + static constexpr uint32_t WEAK_WEIGHT = 10; + static constexpr uint32_t STRONG_WEIGHT = 1000; + static constexpr uint32_t DEOPT_WEIGHT = 2000; +}; + +} + +#endif // ECMASCRIPT_COMPILER_LCR_GATE_META_DATA_H \ No newline at end of file diff --git a/ecmascript/compiler/lcr_lowering.cpp b/ecmascript/compiler/lcr_lowering.cpp index c7187bba9406496e17c0b19ddf8c4e93c77f1dd2..7ac4a410460c6d39f55be8df3a4448eda2e40a95 100644 --- a/ecmascript/compiler/lcr_lowering.cpp +++ b/ecmascript/compiler/lcr_lowering.cpp @@ -14,92 +14,87 @@ */ #include "ecmascript/compiler/lcr_lowering.h" #include "ecmascript/compiler/bytecodes.h" +#include "ecmascript/compiler/share_gate_meta_data.h" #include "ecmascript/global_env.h" #include "ecmascript/js_thread.h" #include "ecmascript/js_function.h" namespace panda::ecmascript::kungfu { -void LCRLowering::Run() -{ - std::vector gateList; - circuit_->GetAllGates(gateList); - for (const auto &gate : gateList) { - auto op = acc_.GetOpCode(gate); - switch (op) { - case OpCode::GET_CONSTPOOL: - LowerGetConstPool(gate); - break; - case OpCode::STATE_SPLIT: - DeleteStateSplit(gate); - break; - case OpCode::ARRAY_GUARDIAN_CHECK: - LowerArrayGuardianCheck(gate); - break; - case OpCode::HCLASS_STABLE_ARRAY_CHECK: - LowerHClassStableArrayCheck(gate); - break; - case OpCode::HEAP_OBJECT_CHECK: - LowerHeapObjectCheck(gate); - break; - case OpCode::LOAD_CONST_OFFSET: - LowerLoadConstOffset(gate); - break; - case OpCode::STORE_CONST_OFFSET: - LowerStoreConstOffset(gate); - break; - case OpCode::CONVERT_HOLE_AS_UNDEFINED: - LowerConvertHoleAsUndefined(gate); - break; - case OpCode::GET_GLOBAL_ENV: - LowerGetGlobalEnv(gate); - break; - case OpCode::GET_GLOBAL_ENV_OBJ_HCLASS: - LowerGetGlobalEnvObjHClass(gate); - break; - case OpCode::GET_GLOBAL_CONSTANT_VALUE: - LowerGetGlobalConstantValue(gate); - break; - case OpCode::HEAP_ALLOC: - LowerHeapAllocate(gate); - break; - case OpCode::INT32_CHECK_RIGHT_IS_ZERO: - LowerInt32CheckRightIsZero(gate); - break; - case OpCode::FLOAT64_CHECK_RIGHT_IS_ZERO: - LowerFloat64CheckRightIsZero(gate); - break; - case OpCode::VALUE_CHECK_NEG_OVERFLOW: - LowerValueCheckNegOverflow(gate); - break; - case OpCode::NEGATIVE_INDEX_CHECK: - LowerNegativeIndexCheck(gate); - break; - case OpCode::LARGE_INDEX_CHECK: - LowerLargeIndexCheck(gate); - break; - case OpCode::OVERFLOW_CHECK: - LowerOverflowCheck(gate); - break; - case OpCode::INT32_UNSIGNED_UPPER_BOUND_CHECK: - LowerInt32UnsignedUpperBoundCheck(gate); - break; - case OpCode::INT32_DIV_WITH_CHECK: - LowerInt32DivWithCheck(gate); - break; - default: - break; - } - } - if (IsLogEnabled()) { - LOG_COMPILER(INFO) << " "; - LOG_COMPILER(INFO) << "\033[34m" << "=================" - << " After LCRLowering " - << "[" << GetMethodName() << "] " - << "=================" << "\033[0m"; - circuit_->PrintAllGatesWithBytecode(); - LOG_COMPILER(INFO) << "\033[34m" << "=========================== End ===========================" << "\033[0m"; +GateRef LCRLowering::VisitGate(GateRef gate) +{ + auto op = acc_.GetOpCode(gate); + switch (op) { + case OpCode::GET_CONSTPOOL: + LowerGetConstPool(gate); + break; + case OpCode::STATE_SPLIT: + DeleteStateSplit(gate); + break; + case OpCode::ARRAY_GUARDIAN_CHECK: + LowerArrayGuardianCheck(gate); + break; + case OpCode::HCLASS_STABLE_ARRAY_CHECK: + LowerHClassStableArrayCheck(gate); + break; + case OpCode::HEAP_OBJECT_CHECK: + LowerHeapObjectCheck(gate); + break; + case OpCode::LOAD_CONST_OFFSET: + LowerLoadConstOffset(gate); + break; + case OpCode::STORE_CONST_OFFSET: + LowerStoreConstOffset(gate); + break; + case OpCode::CONVERT_HOLE_AS_UNDEFINED: + LowerConvertHoleAsUndefined(gate); + break; + case OpCode::GET_GLOBAL_ENV: + LowerGetGlobalEnv(gate); + break; + case OpCode::GET_GLOBAL_ENV_OBJ: + LowerGetGlobalEnvObj(gate); + break; + case OpCode::GET_GLOBAL_ENV_OBJ_HCLASS: + LowerGetGlobalEnvObjHClass(gate); + break; + case OpCode::GET_GLOBAL_CONSTANT_VALUE: + LowerGetGlobalConstantValue(gate); + break; + case OpCode::HEAP_ALLOC: + LowerHeapAllocate(gate); + break; + case OpCode::INT32_CHECK_RIGHT_IS_ZERO: + LowerInt32CheckRightIsZero(gate); + break; + case OpCode::FLOAT64_CHECK_RIGHT_IS_ZERO: + LowerFloat64CheckRightIsZero(gate); + break; + case OpCode::VALUE_CHECK_NEG_OVERFLOW: + LowerValueCheckNegOverflow(gate); + break; + case OpCode::OVERFLOW_CHECK: + LowerOverflowCheck(gate); + break; + case OpCode::INT32_UNSIGNED_UPPER_BOUND_CHECK: + LowerInt32UnsignedUpperBoundCheck(gate); + break; + case OpCode::INT32_DIV_WITH_CHECK: + LowerInt32DivWithCheck(gate); + break; + case OpCode::LEX_VAR_IS_HOLE_CHECK: + LowerLexVarIsHoleCheck(gate); + break; + case OpCode::STORE_MEMORY: + LowerStoreMemory(gate); + break; + case OpCode::CHECK_AND_CONVERT: + LowerCheckAndConvert(gate); + break; + default: + break; } + return Circuit::NullGate(); } void LCRLowering::LowerConvertHoleAsUndefined(GateRef gate) @@ -111,7 +106,7 @@ void LCRLowering::LowerConvertHoleAsUndefined(GateRef gate) GateRef receiver = acc_.GetValueIn(gate, 0); DEFVAlUE(result, (&builder_), VariableType::JS_ANY(), receiver); - builder_.Branch(builder_.TaggedIsHole(*result), &returnUndefined, &exit); + builder_.Branch(builder_.TaggedIsHole(*result), &returnUndefined, &exit, 1, BranchWeight::DEOPT_WEIGHT); builder_.Bind(&returnUndefined); { result = builder_.UndefineConstant(); @@ -193,7 +188,23 @@ void LCRLowering::LowerHClassStableArrayCheck(GateRef gate) GateRef frameState = acc_.GetFrameState(gate); GateRef hclass = acc_.GetValueIn(gate, 0); - GateRef check = builder_.IsIsStableElementsByHClass(hclass); + GateRef check = Circuit::NullGate(); + GateRef stableCheck = builder_.IsStableElements(hclass); + ArrayMetaDataAccessor accessor = acc_.GetArrayMetaDataAccessor(gate); + ElementsKind kind = accessor.GetElementsKind(); + if (accessor.IsLoadElement() && !Elements::IsHole(kind)) { + if (Elements::IsComplex(kind)) { + GateRef elementsKindCheck = builder_.Int32GreaterThanOrEqual(builder_.Int32(static_cast(kind)), + builder_.GetElementsKindByHClass(hclass)); + check = builder_.BoolAnd(stableCheck, elementsKindCheck); + } else { + GateRef elementsKindCheck = builder_.Equal(builder_.Int32(static_cast(kind)), + builder_.GetElementsKindByHClass(hclass)); + check = builder_.BoolAnd(stableCheck, elementsKindCheck); + } + } else { + check = stableCheck; + } builder_.DeoptCheck(check, frameState, DeoptType::NOTSARRAY); acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), Circuit::NullGate()); @@ -221,6 +232,16 @@ StateDepend LCRLowering::LowerConvert(StateDepend stateDepend, GateRef gate) result = builder_.NotEqual(value, builder_.Int32(0)); } break; + case ValueType::UINT32: + if (dstType == ValueType::TAGGED_NUMBER) { + result = ConvertUInt32ToTaggedNumber(value, &exit); + } else if (dstType == ValueType::FLOAT64) { + result = ConvertUInt32ToFloat64(value); + } else { + ASSERT(dstType == ValueType::BOOL); + result = builder_.NotEqual(value, builder_.Int32(0)); + } + break; case ValueType::FLOAT64: if (dstType == ValueType::TAGGED_DOUBLE) { result = ConvertFloat64ToTaggedDouble(value); @@ -243,6 +264,12 @@ StateDepend LCRLowering::LowerConvert(StateDepend stateDepend, GateRef gate) ASSERT((dstType == ValueType::FLOAT64)); result = ConvertTaggedDoubleToFloat64(value); break; + case ValueType::CHAR: { + ASSERT((dstType == ValueType::ECMA_STRING)); + GateRef glue = acc_.GetGlueFromArgList(); + result = builder_.CallStub(glue, gate, CommonStubCSigns::CreateStringBySingleCharCode, { glue, value }); + break; + } default: LOG_COMPILER(FATAL) << "this branch is unreachable"; break; @@ -307,12 +334,16 @@ GateRef LCRLowering::ConvertTaggedNumberToFloat64(GateRef gate, Label *exit) return *result; } -StateDepend LCRLowering::LowerCheckAndConvert(StateDepend stateDepend, GateRef gate, GateRef frameState) +void LCRLowering::LowerCheckAndConvert(GateRef gate) { - Environment env(stateDepend.State(), stateDepend.Depend(), {}, circuit_, &builder_); + Environment env(gate, circuit_, &builder_); + GateRef frameState = acc_.GetFrameState(gate); ValueType srcType = acc_.GetSrcType(gate); Label exit(&builder_); switch (srcType) { + case ValueType::UINT32: + LowerCheckUInt32AndConvert(gate, frameState); + break; case ValueType::TAGGED_INT: LowerCheckTaggedIntAndConvert(gate, frameState); break; @@ -325,10 +356,27 @@ StateDepend LCRLowering::LowerCheckAndConvert(StateDepend stateDepend, GateRef g case ValueType::TAGGED_NUMBER: LowerCheckTaggedNumberAndConvert(gate, frameState, &exit); break; + case ValueType::BOOL: + LowerCheckSupportAndConvert(gate, frameState); + break; + case ValueType::TAGGED_NULL: + LowerCheckNullAndConvert(gate, frameState); + break; + case ValueType::UNDEFINED: + LowerUndefinedAndConvert(gate, frameState); + break; default: UNREACHABLE(); } - return builder_.GetStateDepend(); +} + +void LCRLowering::LowerCheckUInt32AndConvert(GateRef gate, GateRef frameState) +{ + GateRef value = acc_.GetValueIn(gate, 0); + GateRef upperBound = builder_.Int32(INT32_MAX); + GateRef check = builder_.Int32UnsignedLessThanOrEqual(value, upperBound); + builder_.DeoptCheck(check, frameState, DeoptType::INT32OVERFLOW); + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), value); } void LCRLowering::LowerCheckTaggedIntAndConvert(GateRef gate, GateRef frameState) @@ -344,7 +392,7 @@ void LCRLowering::LowerCheckTaggedIntAndConvert(GateRef gate, GateRef frameState } else { result = ConvertTaggedIntToFloat64(value); } - acc_.ReplaceGate(gate, Circuit::NullGate(), Circuit::NullGate(), result); + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result); } void LCRLowering::LowerCheckTaggedDoubleAndConvert(GateRef gate, GateRef frameState, Label *exit) @@ -360,7 +408,7 @@ void LCRLowering::LowerCheckTaggedDoubleAndConvert(GateRef gate, GateRef frameSt } else { result = ConvertTaggedDoubleToFloat64(value); } - acc_.ReplaceGate(gate, Circuit::NullGate(), Circuit::NullGate(), result); + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result); } void LCRLowering::LowerCheckTaggedNumberAndConvert(GateRef gate, GateRef frameState, Label *exit) @@ -378,7 +426,25 @@ void LCRLowering::LowerCheckTaggedNumberAndConvert(GateRef gate, GateRef frameSt ASSERT(dst == ValueType::BOOL); result = ConvertTaggedNumberToBool(value, exit); } - acc_.ReplaceGate(gate, Circuit::NullGate(), Circuit::NullGate(), result); + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result); +} + +void LCRLowering::LowerCheckSupportAndConvert(GateRef gate, GateRef frameState) +{ + ValueType dstType = acc_.GetDstType(gate); + ASSERT(dstType == ValueType::INT32 || dstType == ValueType::FLOAT64); + bool support = acc_.IsConvertSupport(gate); + GateRef value = acc_.GetValueIn(gate, 0); + + GateRef result = Circuit::NullGate(); + if (dstType == ValueType::INT32) { + builder_.DeoptCheck(builder_.Boolean(support), frameState, DeoptType::NOTINT); + result = builder_.BooleanToInt32(value); + } else { + builder_.DeoptCheck(builder_.Boolean(support), frameState, DeoptType::NOTDOUBLE); + result = builder_.BooleanToFloat64(value); + } + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result); } void LCRLowering::LowerCheckTaggedBoolAndConvert(GateRef gate, GateRef frameState) @@ -387,9 +453,53 @@ void LCRLowering::LowerCheckTaggedBoolAndConvert(GateRef gate, GateRef frameStat GateRef typeCheck = builder_.TaggedIsBoolean(value); builder_.DeoptCheck(typeCheck, frameState, DeoptType::NOTBOOL); GateRef result = Circuit::NullGate(); - ASSERT(acc_.GetDstType(gate) == ValueType::BOOL); - result = ConvertTaggedBooleanToBool(value); - acc_.ReplaceGate(gate, Circuit::NullGate(), Circuit::NullGate(), result); + GateRef boolValue = ConvertTaggedBooleanToBool(value); + if (acc_.GetDstType(gate) == ValueType::BOOL) { + result = boolValue; + } else if (acc_.GetDstType(gate) == ValueType::INT32) { + result = builder_.ZExtInt1ToInt32(boolValue); + } else if (acc_.GetDstType(gate) == ValueType::FLOAT64) { + result = builder_.BooleanToFloat64(boolValue); + } else { + UNREACHABLE(); + } + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result); +} + +void LCRLowering::LowerCheckNullAndConvert(GateRef gate, GateRef frameState) +{ + GateRef value = acc_.GetValueIn(gate, 0); + GateRef typeCheck = builder_.TaggedIsNull(value); + builder_.DeoptCheck(typeCheck, frameState, DeoptType::NOTNULL); + GateRef result = Circuit::NullGate(); + if (acc_.GetDstType(gate) == ValueType::INT32) { + result = builder_.Int32(0); + } else if (acc_.GetDstType(gate) == ValueType::FLOAT64) { + result = builder_.Double(0); + } else if (acc_.GetDstType(gate) == ValueType::BOOL) { + result = builder_.False(); + } else { + UNREACHABLE(); + } + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result); +} + +void LCRLowering::LowerUndefinedAndConvert(GateRef gate, GateRef frameState) +{ + GateRef value = acc_.GetValueIn(gate, 0); + GateRef typeCheck = builder_.TaggedIsUndefined(value); + builder_.DeoptCheck(typeCheck, frameState, DeoptType::NOTNULL); + GateRef result = Circuit::NullGate(); + if (acc_.GetDstType(gate) == ValueType::FLOAT64) { + result = builder_.NanValue(); + } else if (acc_.GetDstType(gate) == ValueType::BOOL) { + result = builder_.False(); + } else if (acc_.GetDstType(gate) == ValueType::INT32) { + result = builder_.Int32(0); + } else { + UNREACHABLE(); + } + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result); } GateRef LCRLowering::ConvertTaggedBooleanToBool(GateRef value) @@ -407,11 +517,33 @@ GateRef LCRLowering::ConvertInt32ToFloat64(GateRef gate) return builder_.ChangeInt32ToFloat64(gate); } +GateRef LCRLowering::ConvertUInt32ToFloat64(GateRef gate) +{ + return builder_.ChangeUInt32ToFloat64(gate); +} + GateRef LCRLowering::ConvertInt32ToTaggedInt(GateRef gate) { return builder_.Int32ToTaggedPtr(gate); } +GateRef LCRLowering::ConvertUInt32ToTaggedNumber(GateRef gate, Label *exit) +{ + Label isOverFlow(&builder_); + Label notOverFlow(&builder_); + GateRef upperBound = builder_.Int32(INT32_MAX); + DEFVAlUE(taggedVal, (&builder_), VariableType::JS_ANY(), builder_.HoleConstant()); + builder_.Branch(builder_.Int32UnsignedLessThanOrEqual(gate, upperBound), ¬OverFlow, &isOverFlow); + builder_.Bind(¬OverFlow); + taggedVal = builder_.Int32ToTaggedPtr(gate); + builder_.Jump(exit); + builder_.Bind(&isOverFlow); + taggedVal = builder_.DoubleToTaggedDoublePtr(builder_.ChangeUInt32ToFloat64(gate)); + builder_.Jump(exit); + builder_.Bind(exit); + return *taggedVal; +} + GateRef LCRLowering::ConvertFloat64ToInt32(GateRef gate, Label *exit) { return builder_.DoubleToInt(gate, exit); @@ -453,10 +585,20 @@ void LCRLowering::LowerGetGlobalEnv(GateRef gate) { Environment env(gate, circuit_, &builder_); GateRef glueGlobalEnvOffset = builder_.IntPtr(JSThread::GlueData::GetGlueGlobalEnvOffset(false)); - GateRef glueGlobalEnv = builder_.Load(VariableType::NATIVE_POINTER(), glue_, glueGlobalEnvOffset); + GateRef glueGlobalEnv = builder_.Load(VariableType::JS_POINTER(), glue_, glueGlobalEnvOffset); acc_.ReplaceGate(gate, Circuit::NullGate(), builder_.GetDepend(), glueGlobalEnv); } +void LCRLowering::LowerGetGlobalEnvObj(GateRef gate) +{ + Environment env(gate, circuit_, &builder_); + GateRef globalEnv = acc_.GetValueIn(gate, 0); + size_t index = acc_.GetIndex(gate); + GateRef offset = builder_.IntPtr(GlobalEnv::HEADER_SIZE + JSTaggedValue::TaggedTypeSize() * index); + GateRef object = builder_.Load(VariableType::JS_ANY(), globalEnv, offset); + acc_.ReplaceGate(gate, Circuit::NullGate(), builder_.GetDepend(), object); +} + void LCRLowering::LowerGetGlobalEnvObjHClass(GateRef gate) { Environment env(gate, circuit_, &builder_); @@ -473,7 +615,7 @@ void LCRLowering::LowerGetGlobalConstantValue(GateRef gate) { Environment env(gate, circuit_, &builder_); size_t index = acc_.GetIndex(gate); - GateRef gConstAddr = builder_.PtrAdd(glue_, + GateRef gConstAddr = builder_.Load(VariableType::JS_POINTER(), glue_, builder_.IntPtr(JSThread::GlueData::GetGlobalConstOffset(false))); GateRef constantIndex = builder_.IntPtr(JSTaggedValue::TaggedTypeSize() * index); GateRef result = builder_.Load(VariableType::JS_POINTER(), gConstAddr, constantIndex); @@ -496,10 +638,12 @@ void LCRLowering::LowerHeapAllocate(GateRef gate) void LCRLowering::HeapAllocateInYoung(GateRef gate) { - Label success(&builder_); - Label callRuntime(&builder_); Label exit(&builder_); GateRef size = acc_.GetValueIn(gate, 0); + DEFVAlUE(result, (&builder_), VariableType::JS_ANY(), builder_.HoleConstant()); +#ifndef ARK_ASAN_ON + Label success(&builder_); + Label callRuntime(&builder_); size_t topOffset = JSThread::GlueData::GetNewSpaceAllocationTopAddressOffset(false); size_t endOffset = JSThread::GlueData::GetNewSpaceAllocationEndAddressOffset(false); GateRef topAddress = builder_.Load(VariableType::NATIVE_POINTER(), glue_, builder_.IntPtr(topOffset)); @@ -507,7 +651,6 @@ void LCRLowering::HeapAllocateInYoung(GateRef gate) GateRef top = builder_.Load(VariableType::JS_POINTER(), topAddress, builder_.IntPtr(0)); GateRef end = builder_.Load(VariableType::JS_POINTER(), endAddress, builder_.IntPtr(0)); - DEFVAlUE(result, (&builder_), VariableType::JS_ANY(), builder_.HoleConstant()); GateRef newTop = builder_.PtrAdd(top, size); builder_.Branch(builder_.IntPtrGreaterThan(newTop, end), &callRuntime, &success); builder_.Bind(&success); @@ -517,6 +660,7 @@ void LCRLowering::HeapAllocateInYoung(GateRef gate) builder_.Jump(&exit); } builder_.Bind(&callRuntime); +#endif { result = builder_.CallRuntime(glue_, RTSTUB_ID(AllocateInYoung), Gate::InvalidGateRef, {builder_.ToTaggedInt(size)}, gate); @@ -547,34 +691,23 @@ void LCRLowering::LowerFloat64CheckRightIsZero(GateRef gate) acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), Circuit::NullGate()); } -void LCRLowering::LowerValueCheckNegOverflow(GateRef gate) +void LCRLowering::LowerLexVarIsHoleCheck(GateRef gate) { Environment env(gate, circuit_, &builder_); GateRef frameState = acc_.GetFrameState(gate); GateRef value = acc_.GetValueIn(gate, 0); - GateRef valueNotZero = builder_.NotEqual(value, builder_.Int32(0)); - builder_.DeoptCheck(valueNotZero, frameState, DeoptType::NOTNEGOV); + GateRef valueIsNotHole = builder_.TaggedIsNotHole(value); + builder_.DeoptCheck(valueIsNotHole, frameState, DeoptType::LEXVARISHOLE); acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), Circuit::NullGate()); } -void LCRLowering::LowerNegativeIndexCheck(GateRef gate) -{ - Environment env(gate, circuit_, &builder_); - GateRef frameState = acc_.GetFrameState(gate); - GateRef index = acc_.GetValueIn(gate, 0); - GateRef condition = builder_.Int32LessThanOrEqual(builder_.Int32(0), index); - builder_.DeoptCheck(condition, frameState, DeoptType::NEGTIVEINDEX); - acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), Circuit::NullGate()); -} - -void LCRLowering::LowerLargeIndexCheck(GateRef gate) +void LCRLowering::LowerValueCheckNegOverflow(GateRef gate) { Environment env(gate, circuit_, &builder_); GateRef frameState = acc_.GetFrameState(gate); - GateRef index = acc_.GetValueIn(gate, 0); - GateRef length = acc_.GetValueIn(gate, 1); - GateRef condition = builder_.Int32LessThan(index, length); - builder_.DeoptCheck(condition, frameState, DeoptType::LARGEINDEX); + GateRef value = acc_.GetValueIn(gate, 0); + GateRef valueNotZero = builder_.NotEqual(value, builder_.Int32(0)); + builder_.DeoptCheck(valueNotZero, frameState, DeoptType::NOTNEGOV); acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), Circuit::NullGate()); } @@ -612,12 +745,23 @@ void LCRLowering::LowerInt32DivWithCheck(GateRef gate) GateRef condition = builder_.BoolOr(rightGreaterZero, builder_.BoolAnd(rightLessZero, leftNotZero)); builder_.DeoptCheck(condition, frameState, DeoptType::DIVZERO); result = builder_.BinaryArithmetic(circuit_->Sdiv(), MachineType::I32, left, right, GateType::NJSValue()); - GateRef truncated = builder_.BinaryArithmetic(circuit_->Mul(), MachineType::I32, result, right); + GateRef truncated = builder_.BinaryArithmetic(circuit_->Mul(), + MachineType::I32, result, right, GateType::NJSValue()); GateRef overCheck = builder_.Int32Equal(truncated, left); builder_.DeoptCheck(overCheck, frameState, DeoptType::NOTINT); acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result); } +void LCRLowering::LowerStoreMemory(GateRef gate) +{ + Environment env(gate, circuit_, &builder_); + GateRef receiver = acc_.GetValueIn(gate, 0); + GateRef index = acc_.GetValueIn(gate, 1); + GateRef value = acc_.GetValueIn(gate, 2); + builder_.Store(VariableType::VOID(), glue_, receiver, index, value); + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), Circuit::NullGate()); +} + void LCRLowering::InitializeWithSpeicalValue(Label *exit, GateRef object, GateRef glue, GateRef value, GateRef start, GateRef end) { diff --git a/ecmascript/compiler/lcr_lowering.h b/ecmascript/compiler/lcr_lowering.h index cd9ae449e1f58b531c8cb06010e547675e876856..9f8ca8686f3872d0d9f2c2338dfd3c032cbccad0 100644 --- a/ecmascript/compiler/lcr_lowering.h +++ b/ecmascript/compiler/lcr_lowering.h @@ -18,31 +18,23 @@ #include "ecmascript/compiler/circuit.h" #include "ecmascript/compiler/circuit_builder-inl.h" +#include "ecmascript/compiler/combined_pass_visitor.h" #include "ecmascript/compiler/gate_accessor.h" + namespace panda::ecmascript::kungfu { -class LCRLowering { +class LCRLowering : public PassVisitor { public: - LCRLowering(Circuit *circuit, CompilationConfig *cmpCfg, - bool enableLog, const std::string& name) - : circuit_(circuit), acc_(circuit), builder_(circuit, cmpCfg), - enableLog_(enableLog), methodName_(name), glue_(acc_.GetGlueFromArgList()) + LCRLowering(Circuit *circuit, RPOVisitor *visitor, CompilationConfig *cmpCfg, Chunk *chunk) + : PassVisitor(circuit, chunk, visitor), circuit_(circuit), acc_(circuit), + builder_(circuit, cmpCfg), glue_(acc_.GetGlueFromArgList()) { } ~LCRLowering() = default; - bool IsLogEnabled() const - { - return enableLog_; - } - void Run(); - StateDepend LowerCheckAndConvert(StateDepend stateDepend, GateRef gate, GateRef frameState); + GateRef VisitGate(GateRef gate) override; StateDepend LowerConvert(StateDepend stateDepend, GateRef gate); private: - const std::string& GetMethodName() const - { - return methodName_; - } void DeleteStateSplit(GateRef gate); void LowerArrayGuardianCheck(GateRef gate); @@ -52,26 +44,34 @@ private: void LowerLoadConstOffset(GateRef gate); void LowerStoreConstOffset(GateRef gate); void LowerConvertHoleAsUndefined(GateRef gate); + void LowerCheckAndConvert(GateRef gate); + void LowerCheckUInt32AndConvert(GateRef gate, GateRef frameState); void LowerCheckTaggedIntAndConvert(GateRef gate, GateRef frameState); void LowerCheckTaggedDoubleAndConvert(GateRef gate, GateRef frameState, Label *exit); void LowerCheckTaggedNumberAndConvert(GateRef gate, GateRef frameState, Label *exit); void LowerCheckTaggedBoolAndConvert(GateRef gate, GateRef frameState); + void LowerCheckSupportAndConvert(GateRef gate, GateRef frameState); void LowerGetGlobalEnv(GateRef gate); + void LowerGetGlobalEnvObj(GateRef gate); void LowerGetGlobalEnvObjHClass(GateRef gate); void LowerGetGlobalConstantValue(GateRef gate); void LowerHeapAllocate(GateRef gate); void LowerInt32CheckRightIsZero(GateRef gate); void LowerFloat64CheckRightIsZero(GateRef gate); void LowerValueCheckNegOverflow(GateRef gate); - void LowerNegativeIndexCheck(GateRef gate); - void LowerLargeIndexCheck(GateRef gate); void LowerOverflowCheck(GateRef gate); void LowerInt32UnsignedUpperBoundCheck(GateRef gate); void LowerInt32DivWithCheck(GateRef gate); + void LowerLexVarIsHoleCheck(GateRef gate); + void LowerStoreMemory(GateRef gate); + void LowerCheckNullAndConvert(GateRef gate, GateRef frameState); + void LowerUndefinedAndConvert(GateRef gate, GateRef frameState); GateRef ConvertBoolToTaggedBoolean(GateRef gate); GateRef ConvertInt32ToFloat64(GateRef gate); + GateRef ConvertUInt32ToFloat64(GateRef gate); GateRef ConvertInt32ToTaggedInt(GateRef gate); + GateRef ConvertUInt32ToTaggedNumber(GateRef gate, Label *exit); GateRef ConvertFloat64ToBool(GateRef gate); GateRef ConvertFloat64ToInt32(GateRef gate, Label *exit); GateRef ConvertFloat64ToTaggedDouble(GateRef gate); @@ -90,8 +90,6 @@ private: Circuit *circuit_; GateAccessor acc_; CircuitBuilder builder_; - bool enableLog_ {false}; - std::string methodName_; GateRef glue_ {Circuit::NullGate()}; }; } // panda::ecmascript::kungfu diff --git a/ecmascript/compiler/lcr_opcodes.h b/ecmascript/compiler/lcr_opcodes.h new file mode 100644 index 0000000000000000000000000000000000000000..ff80299acb54d57d15ba96a84b9d8ad58e3c7ab0 --- /dev/null +++ b/ecmascript/compiler/lcr_opcodes.h @@ -0,0 +1,82 @@ +/* + * 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_COMPILER_LCR_OPCODE_H +#define ECMASCRIPT_COMPILER_LCR_OPCODE_H + +namespace panda::ecmascript::kungfu { + +#define LCR_BINARY_GATE_META_DATA_CACHE_LIST(V) \ + V(Add, ADD, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Sub, SUB, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Mul, MUL, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Exp, EXP, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Sdiv, SDIV, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Smod, SMOD, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Udiv, UDIV, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Umod, UMOD, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Fdiv, FDIV, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Fmod, FMOD, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(And, AND, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Xor, XOR, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Or, OR, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Lsl, LSL, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Lsr, LSR, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Asr, ASR, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Sqrt, SQRT, GateFlags::NO_WRITE, 0, 0, 1) \ + V(AddWithOverflow, ADD_WITH_OVERFLOW, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(SubWithOverflow, SUB_WITH_OVERFLOW, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(MulWithOverflow, MUL_WITH_OVERFLOW, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(ExtractValue, EXTRACT_VALUE, GateFlags::NONE_FLAG, 0, 0, 2) + +#define LCR_UNARY_GATE_META_DATA_CACHE_LIST(V) \ + V(Zext, ZEXT, GateFlags::NONE_FLAG, 0, 0, 1) \ + V(Sext, SEXT, GateFlags::NONE_FLAG, 0, 0, 1) \ + V(Trunc, TRUNC, GateFlags::NONE_FLAG, 0, 0, 1) \ + V(Fext, FEXT, GateFlags::NONE_FLAG, 0, 0, 1) \ + V(Ftrunc, FTRUNC, GateFlags::NONE_FLAG, 0, 0, 1) \ + V(Rev, REV, GateFlags::NONE_FLAG, 0, 0, 1) \ + V(TaggedToInt64, TAGGED_TO_INT64, GateFlags::NONE_FLAG, 0, 0, 1) \ + V(Int64ToTagged, INT64_TO_TAGGED, GateFlags::NONE_FLAG, 0, 0, 1) \ + V(SignedIntToFloat, SIGNED_INT_TO_FLOAT, GateFlags::NONE_FLAG, 0, 0, 1) \ + V(UnsignedIntToFloat, UNSIGNED_INT_TO_FLOAT, GateFlags::NONE_FLAG, 0, 0, 1) \ + V(FloatToSignedInt, FLOAT_TO_SIGNED_INT, GateFlags::NONE_FLAG, 0, 0, 1) \ + V(UnsignedFloatToInt, UNSIGNED_FLOAT_TO_INT, GateFlags::NONE_FLAG, 0, 0, 1) \ + V(TruncFloatToInt64, TRUNC_FLOAT_TO_INT64, GateFlags::NONE_FLAG, 0, 0, 1) \ + V(TruncFloatToInt32, TRUNC_FLOAT_TO_INT32, GateFlags::NONE_FLAG, 0, 0, 1) \ + V(Bitcast, BITCAST, GateFlags::NONE_FLAG, 0, 0, 1) + +#define LCR_IMMUTABLE_META_DATA_CACHE_LIST(V) \ + V(ReadSp, READSP, GateFlags::NONE_FLAG, 0, 0, 0) \ + V(Load, LOAD, GateFlags::NO_WRITE, 0, 1, 1) \ + V(Store, STORE, GateFlags::NONE_FLAG, 0, 1, 2) \ + LCR_BINARY_GATE_META_DATA_CACHE_LIST(V) \ + LCR_UNARY_GATE_META_DATA_CACHE_LIST(V) + +#define LCR_GATE_META_DATA_LIST_WITH_BOOL_VALUE_IN(V) \ + V(CallOptimized, CALL_OPTIMIZED, GateFlags::NONE_FLAG, 0, 1, value) \ + V(FastCallOptimized, FAST_CALL_OPTIMIZED, GateFlags::NONE_FLAG, 0, 1, value) + +#define LCR_GATE_META_DATA_LIST_WITH_VALUE(V) \ + V(Icmp, ICMP, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Fcmp, FCMP, GateFlags::NONE_FLAG, 0, 0, 2) \ + V(Alloca, ALLOCA, GateFlags::NONE_FLAG, 0, 0, 0) + +#define LCR_GATE_META_DATA_LIST_WITH_ONE_PARAMETER(V) \ + LCR_GATE_META_DATA_LIST_WITH_VALUE(V) + +} + +#endif // ECMASCRIPT_COMPILER_LCR_OPCODE_H \ No newline at end of file diff --git a/ecmascript/compiler/llvm_codegen.cpp b/ecmascript/compiler/llvm_codegen.cpp index 19f61359585e117f130c23dffe7fbb6a1a293068..a7b55f3b11d2d8a84c79ef9c330a3ad3fa4e23a9 100644 --- a/ecmascript/compiler/llvm_codegen.cpp +++ b/ecmascript/compiler/llvm_codegen.cpp @@ -39,7 +39,11 @@ #include "llvm-c/DisassemblerTypes.h" #include "llvm-c/Target.h" #include "llvm-c/Transforms/PassManagerBuilder.h" +#if defined(PANDA_TARGET_MACOS) #include "llvm/CodeGen/BuiltinGCs.h" +#else +#include "llvm/IR/BuiltinGCs.h" +#endif #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DIContext.h" #include "llvm/ExecutionEngine/ExecutionEngine.h" @@ -112,12 +116,8 @@ CodeInfo::CodeSpace::~CodeSpace() unreqSecs_ = nullptr; } -uint8_t *CodeInfo::CodeSpace::Alloca(uintptr_t size, bool isReq, bool alignFlag) +uint8_t *CodeInfo::CodeSpace::Alloca(uintptr_t size, bool isReq, size_t alignSize) { - // align up for rodata section - if (alignFlag) { - size = AlignUp(size, static_cast(MemAlignment::MEM_ALIGN_REGION)); - } uint8_t *addr = nullptr; auto bufBegin = isReq ? reqSecs_ : unreqSecs_; auto &curPos = isReq ? reqBufPos_ : unreqBufPos_; @@ -127,26 +127,38 @@ uint8_t *CodeInfo::CodeSpace::Alloca(uintptr_t size, bool isReq, bool alignFlag) << " plus size:" << size << "exceed limit:" << limit; return nullptr; } + if (alignSize > 0) { + curPos = AlignUp(curPos, alignSize); + } addr = bufBegin + curPos; curPos += size; return addr; } -uint8_t *CodeInfo::AllocaInReqSecBuffer(uintptr_t size, bool alignFlag) +uint8_t *CodeInfo::AllocaInReqSecBuffer(uintptr_t size, size_t alignSize) { - return CodeSpace::GetInstance()->Alloca(size, true, alignFlag); + return CodeSpace::GetInstance()->Alloca(size, true, alignSize); } -uint8_t *CodeInfo::AllocaInNotReqSecBuffer(uintptr_t size) +uint8_t *CodeInfo::AllocaInNotReqSecBuffer(uintptr_t size, size_t alignSize) { - return CodeSpace::GetInstance()->Alloca(size, false); + return CodeSpace::GetInstance()->Alloca(size, false, alignSize); } uint8_t *CodeInfo::AllocaCodeSection(uintptr_t size, const char *sectionName) { - // if have got section, don't use align. - uint8_t *addr = AllocaInReqSecBuffer(size, false); + uint8_t *addr = nullptr; auto curSec = ElfSection(sectionName); + if (curSec.isValidAOTSec()) { + if (!alreadyPageAlign_) { + addr = AllocaInReqSecBuffer(size, AOTFileInfo::PAGE_ALIGN); + alreadyPageAlign_ = true; + } else { + addr = AllocaInReqSecBuffer(size, AOTFileInfo::TEXT_SEC_ALIGN); + } + } else { + addr = AllocaInReqSecBuffer(size); + } codeInfo_.push_back({addr, size}); if (curSec.isValidAOTSec()) { secInfos_[curSec.GetIntIndex()] = std::make_pair(addr, size); @@ -161,8 +173,17 @@ uint8_t *CodeInfo::AllocaDataSection(uintptr_t size, const char *sectionName) // rodata section needs 16 bytes alignment if (curSec.InRodataSection()) { size = AlignUp(size, static_cast(MemAlignment::MEM_ALIGN_REGION)); + if (!alreadyPageAlign_) { + addr = curSec.isSequentialAOTSec() ? AllocaInReqSecBuffer(size, AOTFileInfo::PAGE_ALIGN) + : AllocaInNotReqSecBuffer(size, AOTFileInfo::PAGE_ALIGN); + alreadyPageAlign_ = true; + } else { + addr = curSec.isSequentialAOTSec() ? AllocaInReqSecBuffer(size, AOTFileInfo::DATA_SEC_ALIGN) + : AllocaInNotReqSecBuffer(size, AOTFileInfo::DATA_SEC_ALIGN); + } + } else { + addr = curSec.isSequentialAOTSec() ? AllocaInReqSecBuffer(size) : AllocaInNotReqSecBuffer(size); } - addr = curSec.isSequentialAOTSec() ? AllocaInReqSecBuffer(size) : AllocaInNotReqSecBuffer(size); if (curSec.isValidAOTSec()) { secInfos_[curSec.GetIntIndex()] = std::make_pair(addr, size); } @@ -204,7 +225,8 @@ void LLVMIRGeneratorImpl::GenerateCodeForStub(Circuit *circuit, const ControlFlo void LLVMIRGeneratorImpl::GenerateCode(Circuit *circuit, const ControlFlowGraph &graph, const CompilationConfig *cfg, const panda::ecmascript::MethodLiteral *methodLiteral, - const JSPandaFile *jsPandaFile, const std::string &methodName) + const JSPandaFile *jsPandaFile, const std::string &methodName, + bool enableOptInlining) { auto function = module_->AddFunc(methodLiteral, jsPandaFile); circuit->SetFrameType(FrameType::OPTIMIZED_JS_FUNCTION_FRAME); @@ -215,7 +237,7 @@ void LLVMIRGeneratorImpl::GenerateCode(Circuit *circuit, const ControlFlowGraph conv = CallSignature::CallConv::WebKitJSCallConv; } LLVMIRBuilder builder(&graph, circuit, module_, function, cfg, conv, - enableLog_, methodLiteral->IsFastCall(), methodName); + enableLog_, methodLiteral->IsFastCall(), methodName, enableOptInlining); builder.Build(); } @@ -264,6 +286,37 @@ bool LLVMAssembler::BuildMCJITEngine() } void LLVMAssembler::BuildAndRunPasses() +{ + LLVMPassManagerBuilderRef pmBuilder = LLVMPassManagerBuilderCreate(); + LLVMPassManagerBuilderSetOptLevel(pmBuilder, options_.OptLevel); // using O3 optimization level + LLVMPassManagerBuilderSetSizeLevel(pmBuilder, 0); + LLVMPassManagerBuilderSetDisableUnrollLoops(pmBuilder, 0); + + // pass manager creation:rs4gc pass is the only pass in modPass, other opt module-based pass are in modPass1 + LLVMPassManagerRef funcPass = LLVMCreateFunctionPassManagerForModule(module_); + LLVMPassManagerRef modPass = LLVMCreatePassManager(); + LLVMPassManagerRef modPass1 = LLVMCreatePassManager(); + + // add pass into pass managers + LLVMPassManagerBuilderPopulateFunctionPassManager(pmBuilder, funcPass); + llvm::unwrap(modPass)->add(llvm::createRewriteStatepointsForGCLegacyPass()); // rs4gc pass added + LLVMPassManagerBuilderPopulateModulePassManager(pmBuilder, modPass1); + + LLVMRunPassManager(modPass, module_); + LLVMInitializeFunctionPassManager(funcPass); + for (LLVMValueRef fn = LLVMGetFirstFunction(module_); fn; fn = LLVMGetNextFunction(fn)) { + LLVMRunFunctionPassManager(funcPass, fn); + } + LLVMFinalizeFunctionPassManager(funcPass); + LLVMRunPassManager(modPass1, module_); + + LLVMPassManagerBuilderDispose(pmBuilder); + LLVMDisposePassManager(funcPass); + LLVMDisposePassManager(modPass); + LLVMDisposePassManager(modPass1); +} + +void LLVMAssembler::BuildAndRunPassesFastMode() { LLVMPassManagerBuilderRef pmBuilder = LLVMPassManagerBuilderCreate(); LLVMPassManagerBuilderSetOptLevel(pmBuilder, options_.OptLevel); // using O3 optimization level @@ -314,7 +367,7 @@ LLVMAssembler::~LLVMAssembler() error_ = nullptr; } -void LLVMAssembler::Run(const CompilerLog &log) +void LLVMAssembler::Run(const CompilerLog &log, bool fastCompileMode) { char *error = nullptr; std::string originName = llvm::unwrap(module_)->getModuleIdentifier() + ".ll"; @@ -331,7 +384,11 @@ void LLVMAssembler::Run(const CompilerLog &log) return; } llvm::unwrap(engine_)->setProcessAllSections(true); - BuildAndRunPasses(); + if (fastCompileMode) { + BuildAndRunPassesFastMode(); + } else { + BuildAndRunPasses(); + } if (log.OutputLLIR()) { error = nullptr; LLVMPrintModuleToFile(module_, optName.c_str(), &error); @@ -360,15 +417,6 @@ void LLVMAssembler::Initialize(LOptions option) LLVMInitializeAArch64AsmPrinter(); LLVMInitializeAArch64AsmParser(); LLVMInitializeAArch64Target(); - } else if (triple.compare(TARGET_ARM32) == 0) { -#if defined(PANDA_TARGET_MACOS) || !defined(PANDA_TARGET_ARM64) - LLVMInitializeARMTargetInfo(); - LLVMInitializeARMTargetMC(); - LLVMInitializeARMDisassembler(); - LLVMInitializeARMAsmPrinter(); - LLVMInitializeARMAsmParser(); - LLVMInitializeARMTarget(); -#endif } else { LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); @@ -395,7 +443,11 @@ kungfu::CalleeRegAndOffsetVec LLVMAssembler::GetCalleeReg2Offset(LLVMValueRef fn { kungfu::CalleeRegAndOffsetVec info; llvm::Function* func = llvm::unwrap(fn); +#if defined(PANDA_TARGET_MACOS) for (const auto &Attr : func->getAttributes().getFnAttributes()) { +#else + for (const auto &Attr : func->getAttributes().getFnAttrs()) { +#endif if (Attr.isStringAttribute()) { std::string str = std::string(Attr.getKindAsString().data()); std::string expectedKey = "DwarfReg"; diff --git a/ecmascript/compiler/llvm_codegen.h b/ecmascript/compiler/llvm_codegen.h index 080abd9a2e7ea58cbe515af85dda7ecec5dec640..17364b3833d8bf639385fbf0ba9d2686114f1ec9 100644 --- a/ecmascript/compiler/llvm_codegen.h +++ b/ecmascript/compiler/llvm_codegen.h @@ -56,7 +56,7 @@ struct CodeInfo { public: static CodeSpace *GetInstance(); - uint8_t *Alloca(uintptr_t size, bool isReq, bool alignFlag = true); + uint8_t *Alloca(uintptr_t size, bool isReq, size_t alignSize); private: CodeSpace(); @@ -73,9 +73,9 @@ struct CodeInfo { size_t unreqBufPos_ {0}; }; - uint8_t *AllocaInReqSecBuffer(uintptr_t size, bool alignFlag = true); + uint8_t *AllocaInReqSecBuffer(uintptr_t size, size_t alignSize = 0); - uint8_t *AllocaInNotReqSecBuffer(uintptr_t size); + uint8_t *AllocaInNotReqSecBuffer(uintptr_t size, size_t alignSize = 0); uint8_t *AllocaCodeSection(uintptr_t size, const char *sectionName); @@ -103,6 +103,7 @@ struct CodeInfo { private: std::array(ElfSecName::SIZE)> secInfos_; std::vector> codeInfo_ {}; // info for disasssembler, planed to be deprecated + bool alreadyPageAlign_ {false}; }; enum class FPFlag : uint32_t { @@ -124,7 +125,7 @@ class LLVMAssembler { public: explicit LLVMAssembler(LLVMModule *lm, LOptions option = LOptions()); virtual ~LLVMAssembler(); - void Run(const CompilerLog &log); + void Run(const CompilerLog &log, bool fastCompileMode); const LLVMExecutionEngineRef &GetEngine() { return engine_; @@ -184,6 +185,7 @@ private: void UseRoundTripSectionMemoryManager(); bool BuildMCJITEngine(); void BuildAndRunPasses(); + void BuildAndRunPassesFastMode(); void Initialize(LOptions option); static void PrintInstAndStep(uint64_t &pc, uint8_t **byteSp, uintptr_t &numBytes, size_t instSize, uint64_t textOffset, char *outString, std::ostringstream &codeStream, @@ -208,7 +210,8 @@ public: void GenerateCodeForStub(Circuit *circuit, const ControlFlowGraph &graph, size_t index, const CompilationConfig *cfg) override; void GenerateCode(Circuit *circuit, const ControlFlowGraph &graph, const CompilationConfig *cfg, - const MethodLiteral *methodLiteral, const JSPandaFile *jsPandaFile, const std::string &methodName) override; + const MethodLiteral *methodLiteral, const JSPandaFile *jsPandaFile, const std::string &methodName, + bool enableOptInlining) override; bool IsLogEnabled() const { diff --git a/ecmascript/compiler/llvm_ir_builder.cpp b/ecmascript/compiler/llvm_ir_builder.cpp index 0c10672336a63dd5530ce7106745aadbc6df3760..4f752624bf6a3971477020b89a804fb8c0bbea8c 100644 --- a/ecmascript/compiler/llvm_ir_builder.cpp +++ b/ecmascript/compiler/llvm_ir_builder.cpp @@ -24,6 +24,7 @@ #include "ecmascript/compiler/common_stubs.h" #include "ecmascript/compiler/debug_info.h" #include "ecmascript/compiler/gate.h" +#include "ecmascript/compiler/share_gate_meta_data.h" #include "ecmascript/compiler/rt_call_signature.h" #include "ecmascript/deoptimizer/deoptimizer.h" #include "ecmascript/frames.h" @@ -58,11 +59,12 @@ namespace panda::ecmascript::kungfu { LLVMIRBuilder::LLVMIRBuilder(const std::vector> *schedule, Circuit *circuit, LLVMModule *module, LLVMValueRef function, const CompilationConfig *cfg, CallSignature::CallConv callConv, bool enableLog, bool isFastCallAot, - const std::string &funcName) + const std::string &funcName, bool enableOptInlining) : compCfg_(cfg), scheduledGates_(schedule), circuit_(circuit), acc_(circuit), module_(module->GetModule()), function_(function), llvmModule_(module), callConv_(callConv), enableLog_(enableLog), - isFastCallAot_(isFastCallAot) + isFastCallAot_(isFastCallAot), enableOptInlining_(enableOptInlining) { + ASSERT(compCfg_->Is64Bit()); context_ = module->GetContext(); builder_ = LLVMCreateBuilderInContext(context_); bbID2BB_.clear(); @@ -70,17 +72,8 @@ LLVMIRBuilder::LLVMIRBuilder(const std::vector> *schedule, InitializeHandlers(); LLVMSetGC(function_, "statepoint-example"); - if (compCfg_->Is32Bit()) { - slotSize_ = sizeof(uint32_t); - slotType_ = LLVMInt32TypeInContext(context_); - } else { - slotSize_ = sizeof(uint64_t); - slotType_ = LLVMInt64TypeInContext(context_); - } - if (compCfg_->Is32Bit()) { - // hard float instruction - LLVMAddTargetDependentFunctionAttr(function_, "target-features", "+armv8-a"); - } + slotSize_ = sizeof(uint64_t); + slotType_ = GetInt64T(); LLVMMetadataRef dFile = llvmModule_->GetDFileMD(); LLVMMetadataRef funcTyMD = GetFunctionTypeMD(dFile); @@ -115,11 +108,7 @@ void LLVMIRBuilder::SetFunctionCallConv() LLVMSetFunctionCallConv(function_, LLVMGHCCallConv); break; case CallSignature::CallConv::WebKitJSCallConv: { - if (!compCfg_->Is32Bit()) { - LLVMSetFunctionCallConv(function_, LLVMWebKitJSCallConv); - } else { - LLVMSetFunctionCallConv(function_, LLVMCCallConv); - } + LLVMSetFunctionCallConv(function_, LLVMWebKitJSCallConv); break; } default: { @@ -214,6 +203,7 @@ void LLVMIRBuilder::InitializeHandlers() {OpCode::MUL_WITH_OVERFLOW, &LLVMIRBuilder::HandleMulWithOverflow}, {OpCode::EXTRACT_VALUE, &LLVMIRBuilder::HandleExtractValue}, {OpCode::SQRT, &LLVMIRBuilder::HandleSqrt}, + {OpCode::READSP, &LLVMIRBuilder::HandleReadSp}, }; illegalOpHandlers_ = { OpCode::NOP, OpCode::CIRCUIT_ROOT, OpCode::DEPEND_ENTRY, @@ -305,12 +295,13 @@ void LLVMIRBuilder::ProcessPhiWorkList() for (BasicBlock *bb : phiRebuildWorklist_) { auto impl = bb->GetImpl(); for (auto &e : impl->unmergedPhis_) { - BasicBlock *pred = e.pred; - if (impl->started == 0) { + ASSERT(bbID2BB_.count(e.predBBId) > 0); + BasicBlock *pred = bbID2BB_[e.predBBId].get(); + if (!impl->started) { OPTIONAL_LOG_COMPILER(ERROR) << " ProcessPhiWorkList error hav't start "; return; } - LLVMValueRef value = gate2LValue_[e.operand]; + LLVMValueRef value = GetLValue(e.operand); if (LLVMTypeOf(value) != LLVMTypeOf(e.phi)) { OPTIONAL_LOG_COMPILER(ERROR) << " ProcessPhiWorkList LLVMTypeOf don't match error "; } @@ -351,9 +342,6 @@ BasicBlockImpl *LLVMIRBuilder::EnsureBBImpl(BasicBlock *bb) const void LLVMIRBuilder::GenPrologue() { - if (compCfg_->Is32Bit()) { - return; - } auto frameType = circuit_->GetFrameType(); if (IsInterpreted()) { return; @@ -410,14 +398,12 @@ LLVMValueRef LLVMIRBuilder::CallingFp(LLVMModuleRef &module, LLVMBuilderRef &bui return LLVMGetParam(function_, static_cast(InterpreterHandlerInputs::SP)); } /* 0:calling 1:its caller */ - std::vector args = {LLVMConstInt(LLVMInt32TypeInContext(context_), 0, isCaller)}; + std::vector args = {LLVMConstInt(GetInt32T(), 0, isCaller)}; auto fn = LLVMGetNamedFunction(module, "llvm.frameaddress.p0i8"); if (!fn) { /* init instrinsic function declare */ - LLVMTypeRef paramTys1[] = { - LLVMInt32TypeInContext(context_), - }; - auto fnTy = LLVMFunctionType(LLVMPointerType(LLVMInt8TypeInContext(context_), 0), paramTys1, 1, 0); + LLVMTypeRef paramTys1[] = { GetInt32T() }; + auto fnTy = LLVMFunctionType(GetRawPtrT(), paramTys1, 1, 0); fn = LLVMAddFunction(module, "llvm.frameaddress.p0i8", fnTy); } LLVMValueRef fAddrRet = LLVMBuildCall(builder, fn, args.data(), 1, ""); @@ -434,7 +420,7 @@ LLVMValueRef LLVMIRBuilder::ReadRegister(LLVMModuleRef &module, [[maybe_unused]] LLVMTypeRef paramTys1[] = { GetMachineRepType(MachineRep::K_META), }; - auto fnTy = LLVMFunctionType(LLVMInt64TypeInContext(context_), paramTys1, 1, 0); + auto fnTy = LLVMFunctionType(GetInt64T(), paramTys1, 1, 0); fn = LLVMAddFunction(module, "llvm.read_register.i64", fnTy); } LLVMValueRef fAddrRet = LLVMBuildCall(builder_, fn, args.data(), 1, ""); @@ -461,30 +447,25 @@ LLVMTypeRef LLVMIRBuilder::GetMachineRepType(MachineRep rep) const LLVMTypeRef dstType; switch (rep) { case MachineRep::K_BIT: - dstType = LLVMInt1TypeInContext(context_); + dstType = GetInt1T(); break; case MachineRep::K_WORD8: - dstType = LLVMInt8TypeInContext(context_); + dstType = GetInt8T(); break; case MachineRep::K_WORD16: - dstType = LLVMInt16TypeInContext(context_); + dstType = GetInt16T(); break; case MachineRep::K_WORD32: - dstType = LLVMInt32TypeInContext(context_); + dstType = GetInt32T(); break; case MachineRep::K_FLOAT64: - dstType = LLVMDoubleTypeInContext(context_); + dstType = GetDoubleT(); break; case MachineRep::K_WORD64: - dstType = LLVMInt64TypeInContext(context_); + dstType = GetInt64T(); break; case MachineRep::K_PTR_1: - if (compCfg_->Is32Bit()) { - dstType = - LLVMVectorType(LLVMPointerType(LLVMInt8TypeInContext(context_), 1), 2); // 2: packed vector type - } else { - dstType = LLVMPointerType(LLVMInt64TypeInContext(context_), 1); - } + dstType = GetTaggedHPtrT(); break; case MachineRep::K_META: dstType = LLVMMetadataTypeInContext(context_); @@ -497,6 +478,12 @@ LLVMTypeRef LLVMIRBuilder::GetMachineRepType(MachineRep rep) const return dstType; } +void LLVMIRBuilder::HandleReadSp(GateRef gate) +{ + ASSERT(acc_.GetOpCode(gate) == OpCode::READSP); + VisitReadSp(gate); +} + void LLVMIRBuilder::HandleCall(GateRef gate) { std::vector ins; @@ -580,18 +567,19 @@ void LLVMIRBuilder::VisitRuntimeCall(GateRef gate, const std::vector &i auto kind = GetCallExceptionKind(stubIndex, OpCode::RUNTIME_CALL); size_t actualNumArgs = 0; - LLVMValueRef pcOffset = LLVMConstInt(LLVMInt32TypeInContext(context_), 0, 0); - ComputeArgCountAndPCOffset(actualNumArgs, pcOffset, inList, kind); + LLVMValueRef pcOffset = LLVMConstInt(GetInt32T(), 0, 0); + GateRef frameArgs = Circuit::NullGate(); + ComputeArgCountAndExtraInfo(actualNumArgs, pcOffset, frameArgs, inList, kind); std::vector params; params.push_back(glue); // glue const int index = static_cast(acc_.GetConstantValue(inList[static_cast(CallInputs::TARGET)])); - params.push_back(LLVMConstInt(LLVMInt64TypeInContext(context_), index, 0)); // target - params.push_back(LLVMConstInt(LLVMInt64TypeInContext(context_), + params.push_back(LLVMConstInt(GetInt64T(), index, 0)); // target + params.push_back(LLVMConstInt(GetInt64T(), actualNumArgs - static_cast(CallInputs::FIRST_PARAMETER), 0)); // argc for (size_t paraIdx = static_cast(CallInputs::FIRST_PARAMETER); paraIdx < actualNumArgs; ++paraIdx) { GateRef gateTmp = inList[paraIdx]; - params.push_back(gate2LValue_[gateTmp]); + params.push_back(GetLValue(gateTmp)); } LLVMTypeRef funcType = llvmModule_->GenerateFuncType(params, signature); @@ -601,20 +589,14 @@ void LLVMIRBuilder::VisitRuntimeCall(GateRef gate, const std::vector &i LLVMValueRef runtimeCall = nullptr; if (kind == CallExceptionKind::HAS_PC_OFFSET) { std::vector values; - auto pcIndex = - LLVMConstInt(LLVMInt64TypeInContext(context_), static_cast(SpecVregIndex::PC_OFFSET_INDEX), 1); - values.push_back(pcIndex); - values.push_back(pcOffset); + CollectExraCallSiteInfo(values, pcOffset, frameArgs); runtimeCall = LLVMBuildCall3(builder_, funcType, callee, params.data(), actualNumArgs, "", values.data(), values.size()); } else { runtimeCall = LLVMBuildCall2(builder_, funcType, callee, params.data(), actualNumArgs, ""); } - - if (!compCfg_->Is32Bit()) { // Arm32 not support webkit jscc calling convention - LLVMSetInstructionCallConv(runtimeCall, LLVMWebKitJSCallConv); - } - gate2LValue_[gate] = runtimeCall; + LLVMSetInstructionCallConv(runtimeCall, LLVMWebKitJSCallConv); + Bind(gate, runtimeCall); if (IsLogEnabled()) { SetDebugInfo(gate, runtimeCall); @@ -624,13 +606,15 @@ void LLVMIRBuilder::VisitRuntimeCall(GateRef gate, const std::vector &i bool LLVMIRBuilder::SetDebugInfo(GateRef g, LLVMValueRef r) { size_t index = 0; - if (circuit_->GetDebugInfo(g, index)) { - LLVMMetadataRef loc = LLVMDIBuilderCreateDebugLocation(context_, index + 1, 0, dFuncMD_, NULL); - LLVMInstructionSetDebugLoc(r, loc); - return true; - } else { - return false; + if (r != nullptr && circuit_->GetDebugInfo(g, index)) { + LLVMValueKind k = LLVMGetValueKind(r); + if (k == LLVMInstructionValueKind) { + LLVMMetadataRef loc = LLVMDIBuilderCreateDebugLocation(context_, index + 1, 0, dFuncMD_, NULL); + LLVMInstructionSetDebugLoc(r, loc); + return true; + } } + return false; } void LLVMIRBuilder::HandleRuntimeCallWithArgv(GateRef gate) @@ -655,17 +639,17 @@ void LLVMIRBuilder::VisitRuntimeCallWithArgv(GateRef gate, const std::vector(CallInputs::TARGET)]); - auto targetId = LLVMConstInt(LLVMInt64TypeInContext(context_), index, 0); + auto targetId = LLVMConstInt(GetInt64T(), index, 0); params.push_back(targetId); // target for (size_t paraIdx = static_cast(CallInputs::FIRST_PARAMETER); paraIdx < inList.size(); ++paraIdx) { GateRef gateTmp = inList[paraIdx]; - params.push_back(gate2LValue_[gateTmp]); + params.push_back(GetLValue(gateTmp)); } LLVMTypeRef funcType = llvmModule_->GenerateFuncType(params, signature); callee = LLVMBuildPointerCast(builder_, callee, LLVMPointerType(funcType, 0), ""); LLVMValueRef runtimeCall = LLVMBuildCall2(builder_, funcType, callee, params.data(), inList.size() - 1, ""); - gate2LValue_[gate] = runtimeCall; + Bind(gate, runtimeCall); if (IsLogEnabled()) { SetDebugInfo(gate, runtimeCall); @@ -689,7 +673,7 @@ LLVMValueRef LLVMIRBuilder::GetCurrentFrameType(LLVMValueRef currentSpFrameAddr) { LLVMValueRef tmp = LLVMBuildSub(builder_, currentSpFrameAddr, LLVMConstInt(slotType_, slotSize_, 1), ""); LLVMValueRef frameTypeAddr = - LLVMBuildIntToPtr(builder_, tmp, LLVMPointerType(LLVMInt64TypeInContext(context_), 0), ""); + LLVMBuildIntToPtr(builder_, tmp, LLVMPointerType(GetInt64T(), 0), ""); LLVMValueRef frameType = LLVMBuildLoad(builder_, frameTypeAddr, ""); return frameType; } @@ -728,7 +712,8 @@ bool LLVMIRBuilder::IsHeapPointerType(LLVMTypeRef valueType) LLVMValueRef LLVMIRBuilder::GetGlue(const std::vector &inList) { - return gate2LValue_[inList[static_cast(CallInputs::GLUE)]]; + auto g = inList.at(static_cast(CallInputs::GLUE)); + return GetLValue(g); } LLVMValueRef LLVMIRBuilder::GetLeaveFrameOffset(LLVMValueRef glue) @@ -770,13 +755,14 @@ LLVMValueRef LLVMIRBuilder::GetBuiltinsStubOffset(LLVMValueRef glue) return LLVMConstInt(glueType, JSThread::GlueData::GetBuiltinsStubEntriesOffset(compCfg_->Is32Bit()), 0); } -void LLVMIRBuilder::ComputeArgCountAndPCOffset(size_t &actualNumArgs, LLVMValueRef &pcOffset, - const std::vector &inList, CallExceptionKind kind) +void LLVMIRBuilder::ComputeArgCountAndExtraInfo(size_t &actualNumArgs, LLVMValueRef &pcOffset, GateRef &frameArgs, + const std::vector &inList, CallExceptionKind kind) { if (kind == CallExceptionKind::HAS_PC_OFFSET) { - actualNumArgs = inList.size() - 1; - pcOffset = gate2LValue_[inList[actualNumArgs]]; - ASSERT(acc_.GetOpCode(inList[actualNumArgs]) == OpCode::CONSTANT); + actualNumArgs = inList.size() - 2; // 2: pcOffset and frameArgs + pcOffset = GetLValue(inList.at(actualNumArgs + 1)); + frameArgs = inList.at(actualNumArgs); + ASSERT(acc_.GetOpCode(inList.at(actualNumArgs + 1)) == OpCode::CONSTANT); } else { actualNumArgs = inList.size(); } @@ -798,7 +784,7 @@ void LLVMIRBuilder::UpdateLeaveFrame(LLVMValueRef glue) LLVMTypeRef glueType = LLVMTypeOf(glue); LLVMValueRef leaveFrameAddr = LLVMBuildIntToPtr(builder_, leaveFrameValue, LLVMPointerType(glueType, 0), ""); LLVMValueRef llvmFpAddr = CallingFp(module_, builder_, true); - LLVMValueRef fp = LLVMBuildPtrToInt(builder_, llvmFpAddr, LLVMInt64TypeInContext(context_), "cast_int64_t"); + LLVMValueRef fp = LLVMBuildPtrToInt(builder_, llvmFpAddr, GetInt64T(), "cast_int64_t"); LLVMBuildStore(builder_, fp, leaveFrameAddr); } @@ -811,12 +797,59 @@ LLVMValueRef LLVMIRBuilder::GetCallee(const std::vector &inList, const std::string name = realName.empty() ? signature->GetName() : realName; - LLVMValueRef code = gate2LValue_[inList[static_cast(CallInputs::TARGET)]]; + LLVMValueRef code = GetLValue(inList.at(static_cast(CallInputs::TARGET))); LLVMValueRef callee = LLVMBuildIntToPtr(builder_, code, rtfuncTypePtr, (name + "-cast").c_str()); ASSERT(callee != nullptr); return callee; } +void LLVMIRBuilder::VisitReadSp(GateRef gate) +{ + LLVMValueRef spValue = GetCurrentSP(); + Bind(gate, spValue); +} + +void LLVMIRBuilder::CollectExraCallSiteInfo(std::vector &values, LLVMValueRef pcOffset, + GateRef frameArgs) +{ + // pc offset + auto pcIndex = LLVMConstInt(GetInt64T(), static_cast(SpecVregIndex::PC_OFFSET_INDEX), 1); + values.push_back(pcIndex); + values.push_back(pcOffset); + + if (!enableOptInlining_) { + return; + } + + if (frameArgs == Circuit::NullGate()) { + return; + } + if (acc_.GetOpCode(frameArgs) != OpCode::FRAME_ARGS) { + return; + } + uint32_t maxDepth = acc_.GetFrameDepth(frameArgs, OpCode::FRAME_ARGS); + if (maxDepth == 0) { + return; + } + + maxDepth = std::min(maxDepth, MAX_METHOD_OFFSET_NUM); + size_t shift = Deoptimizier::ComputeShift(MAX_METHOD_OFFSET_NUM); + ArgumentAccessor argAcc(const_cast(circuit_)); + for (int32_t curDepth = static_cast(maxDepth - 1); curDepth >= 0; curDepth--) { + ASSERT(acc_.GetOpCode(frameArgs) == OpCode::FRAME_ARGS); + // method id + uint32_t methodOffset = acc_.TryGetMethodOffset(frameArgs); + frameArgs = acc_.GetFrameState(frameArgs); + if (methodOffset == FrameStateOutput::INVALID_INDEX) { + methodOffset = 0; + } + int32_t specCallTargetIndex = static_cast(SpecVregIndex::FIRST_METHOD_OFFSET_INDEX) - curDepth; + int32_t encodeIndex = Deoptimizier::EncodeDeoptVregIndex(specCallTargetIndex, curDepth, shift); + values.emplace_back(LLVMConstInt(GetInt32T(), encodeIndex, false)); + values.emplace_back(LLVMConstInt(GetInt32T(), methodOffset, false)); + } +} + void LLVMIRBuilder::VisitCall(GateRef gate, const std::vector &inList, OpCode op) { size_t targetIndex = static_cast(CallInputs::TARGET); @@ -827,6 +860,7 @@ void LLVMIRBuilder::VisitCall(GateRef gate, const std::vector &inList, LLVMValueRef rtbaseoffset; LLVMValueRef callee; CallExceptionKind kind = CallExceptionKind::NO_PC_OFFSET; + bool isNoGC = false; if (op == OpCode::CALL) { const size_t index = acc_.GetConstantValue(inList[targetIndex]); calleeDescriptor = CommonStubCSigns::Get(index); @@ -850,6 +884,7 @@ void LLVMIRBuilder::VisitCall(GateRef gate, const std::vector &inList, } else { kind = CallExceptionKind::NO_PC_OFFSET; } + isNoGC = acc_.IsNoGC(gate); } else if (op == OpCode::FAST_CALL_OPTIMIZED) { calleeDescriptor = RuntimeStubCSigns::GetOptimizedFastCallSign(); callee = GetCallee(inList, calleeDescriptor); @@ -858,9 +893,10 @@ void LLVMIRBuilder::VisitCall(GateRef gate, const std::vector &inList, } else { kind = CallExceptionKind::NO_PC_OFFSET; } + isNoGC = acc_.IsNoGC(gate); } else { ASSERT(op == OpCode::BUILTINS_CALL || op == OpCode::BUILTINS_CALL_WITH_ARGV); - LLVMValueRef opcodeOffset = gate2LValue_[inList[targetIndex]]; + LLVMValueRef opcodeOffset = GetLValue(inList.at(targetIndex)); rtoffset = GetBuiltinsStubOffset(glue); rtbaseoffset = LLVMBuildAdd( builder_, glue, LLVMBuildAdd(builder_, rtoffset, opcodeOffset, ""), ""); @@ -875,7 +911,7 @@ void LLVMIRBuilder::VisitCall(GateRef gate, const std::vector &inList, std::vector params; const size_t firstArg = static_cast(CallInputs::FIRST_PARAMETER); GateRef glueGate = inList[firstArg]; - params.push_back(gate2LValue_[glueGate]); + params.push_back(GetLValue(glueGate)); // get parameter types LLVMTypeRef calleeFuncType = LLVMGetElementType(LLVMTypeOf(callee)); @@ -884,25 +920,26 @@ void LLVMIRBuilder::VisitCall(GateRef gate, const std::vector &inList, int extraParameterCnt = 0; size_t actualNumArgs = 0; - LLVMValueRef pcOffset = LLVMConstInt(LLVMInt32TypeInContext(context_), 0, 0); - ComputeArgCountAndPCOffset(actualNumArgs, pcOffset, inList, kind); + LLVMValueRef pcOffset = LLVMConstInt(GetInt32T(), 0, 0); + GateRef frameArgs = Circuit::NullGate(); + ComputeArgCountAndExtraInfo(actualNumArgs, pcOffset, frameArgs, inList, kind); // then push the actual parameter for js function call for (size_t paraIdx = firstArg + 1; paraIdx < actualNumArgs; ++paraIdx) { GateRef gateTmp = inList[paraIdx]; - const auto gateTmpType = LLVMTypeOf(gate2LValue_[gateTmp]); + const auto gateTmpType = LLVMTypeOf(GetLValue(gateTmp)); if (params.size() < paramTypes.size()) { // this condition will be false for variadic arguments const auto paramType = paramTypes.at(params.size()); // match parameter types and function signature types if (IsHeapPointerType(paramType) && !IsHeapPointerType(gateTmpType)) { params.push_back(LLVMBuildIntToPtr(builder_, - LLVMBuildBitCast(builder_, gate2LValue_[gateTmp], LLVMInt64TypeInContext(context_), ""), - paramType, "")); + LLVMBuildBitCast(builder_, GetLValue(gateTmp), GetInt64T(), ""), + paramType, "")); } else { - params.push_back(LLVMBuildBitCast(builder_, gate2LValue_[gateTmp], paramType, "")); + params.push_back(LLVMBuildBitCast(builder_, GetLValue(gateTmp), paramType, "")); } } else { - params.push_back(gate2LValue_[gateTmp]); + params.push_back(GetLValue(gateTmp)); } } @@ -911,10 +948,7 @@ void LLVMIRBuilder::VisitCall(GateRef gate, const std::vector &inList, callee = LLVMBuildPointerCast(builder_, callee, LLVMPointerType(funcType, 0), ""); if (kind == CallExceptionKind::HAS_PC_OFFSET) { std::vector values; - auto pcIndex = - LLVMConstInt(LLVMInt64TypeInContext(context_), static_cast(SpecVregIndex::PC_OFFSET_INDEX), 1); - values.push_back(pcIndex); - values.push_back(pcOffset); + CollectExraCallSiteInfo(values, pcOffset, frameArgs); call = LLVMBuildCall3(builder_, funcType, callee, params.data(), actualNumArgs - firstArg + extraParameterCnt, "", values.data(), values.size()); } else { @@ -922,7 +956,10 @@ void LLVMIRBuilder::VisitCall(GateRef gate, const std::vector &inList, ""); } SetCallConvAttr(calleeDescriptor, call); - gate2LValue_[gate] = call; + if (isNoGC) { + SetGCLeafFunction(call); + } + Bind(gate, call); if (IsLogEnabled()) { SetDebugInfo(gate, call); @@ -934,11 +971,11 @@ void LLVMIRBuilder::VisitBytecodeCall(GateRef gate, const std::vector & size_t paraStartIndex = static_cast(CallInputs::FIRST_PARAMETER); size_t targetIndex = static_cast(CallInputs::TARGET); size_t glueIndex = static_cast(CallInputs::GLUE); - LLVMValueRef opcodeOffset = gate2LValue_[inList[targetIndex]]; + LLVMValueRef opcodeOffset = GetLValue(inList.at(targetIndex)); ASSERT(llvmModule_ != nullptr); // start index of bytecode handler csign in llvmModule - LLVMValueRef glue = gate2LValue_[inList[glueIndex]]; + LLVMValueRef glue = GetLValue(inList.at(glueIndex)); LLVMValueRef baseOffset = GetBaseOffset(gate, glue); LLVMValueRef rtbaseoffset = LLVMBuildAdd( builder_, glue, LLVMBuildAdd(builder_, baseOffset, opcodeOffset, ""), ""); @@ -948,7 +985,7 @@ void LLVMIRBuilder::VisitBytecodeCall(GateRef gate, const std::vector & std::vector params; for (size_t paraIdx = paraStartIndex; paraIdx < inList.size(); ++paraIdx) { GateRef gateTmp = inList[paraIdx]; - params.push_back(gate2LValue_[gateTmp]); + params.push_back(GetLValue(gateTmp)); } LLVMTypeRef funcType = llvmModule_->GenerateFuncType(params, signature); @@ -957,7 +994,7 @@ void LLVMIRBuilder::VisitBytecodeCall(GateRef gate, const std::vector & SetGCLeafFunction(call); LLVMSetTailCall(call, true); LLVMSetInstructionCallConv(call, LLVMGHCCallConv); - gate2LValue_[gate] = call; + Bind(gate, call); if (IsLogEnabled()) { SetDebugInfo(gate, call); @@ -986,9 +1023,10 @@ void LLVMIRBuilder::VisitAlloca(GateRef gate) { uint64_t machineRep = acc_.TryGetValue(gate); LLVMTypeRef dataType = GetMachineRepType(static_cast(machineRep)); - gate2LValue_[gate] = LLVMBuildPtrToInt(builder_, - LLVMBuildAlloca(builder_, dataType, ""), - ConvertLLVMTypeFromGate(gate), ""); + auto lv = LLVMBuildPtrToInt(builder_, + LLVMBuildAlloca(builder_, dataType, ""), + ConvertLLVMTypeFromGate(gate), ""); + Bind(gate, lv); } void LLVMIRBuilder::HandlePhi(GateRef gate) @@ -998,16 +1036,36 @@ void LLVMIRBuilder::HandlePhi(GateRef gate) VisitPhi(gate, ins); } -void LLVMIRBuilder::VisitPhi(GateRef gate, const std::vector &srcGates) +int LLVMIRBuilder::LookupPredBB(GateRef start, int bbID) +{ + GateId gateId = acc_.GetId(start); + int owner = instID2bbID_[gateId]; + if (owner != bbID) { + return owner; + } + GateRef pred = start; + while (owner == bbID) { + pred = acc_.GetState(pred); + auto id = acc_.GetId(pred); + owner = instID2bbID_[id]; + } + return owner; +} + +void LLVMIRBuilder::VisitPhi(GateRef gate, const std::vector &phiIns) { LLVMTypeRef type = ConvertLLVMTypeFromGate(gate); LLVMValueRef phi = LLVMBuildPhi(builder_, type, ""); - std::vector relMergeIns; - acc_.GetIns(srcGates[0], relMergeIns); - bool addToPhiRebuildList = false; - for (int i = 1; i < static_cast(srcGates.size()); i++) { - GateId gateId = acc_.GetId(relMergeIns[i - 1]); - int bbIdx = instID2bbID_[gateId]; + if (phiIns.size() > 1) { + Bind(gate, phi); + } + // Collect the states merges of this phi and note the 1-in is the merged states. + std::vector phiStates; + acc_.GetIns(phiIns.at(0), phiStates); + ASSERT(phiStates.size() + 1 == phiIns.size()); + for (int i = 1; i < static_cast(phiIns.size()); i++) { + int bbIdx = LookupPredBB(phiStates.at(i - 1), currentBb_->GetId()); + int cnt = static_cast(bbID2BB_.count(bbIdx)); // if cnt = 0 means bb with current bbIdx hasn't been created if (cnt > 0) { @@ -1022,26 +1080,22 @@ void LLVMIRBuilder::VisitPhi(GateRef gate, const std::vector &srcGates) return; } LLVMBasicBlockRef llvmBB = EnsureLBB(bb); // The llvm bb - LLVMValueRef value = gate2LValue_[srcGates[i]]; + LLVMValueRef value = GetLValue(phiIns.at(i)); if (impl->started) { LLVMAddIncoming(phi, &value, &llvmBB, 1); } else { - addToPhiRebuildList = true; impl = currentBb_->GetImpl(); - impl->unmergedPhis_.emplace_back(); - auto ¬_merged_phi = impl->unmergedPhis_.back(); - not_merged_phi.phi = phi; - not_merged_phi.pred = bb; - not_merged_phi.operand = srcGates[i]; + NotMergedPhiDesc d = { bbIdx, phiIns.at(i), phi }; + impl->unmergedPhis_.emplace_back(d); + phiRebuildWorklist_.push_back(currentBb_); } } else { - addToPhiRebuildList = true; - } - if (addToPhiRebuildList) { + BasicBlockImpl* impl = currentBb_->GetImpl(); + NotMergedPhiDesc d = { bbIdx, phiIns.at(i), phi }; + impl->unmergedPhis_.emplace_back(d); phiRebuildWorklist_.push_back(currentBb_); } - gate2LValue_[gate] = phi; } } @@ -1050,8 +1104,12 @@ void LLVMIRBuilder::VisitReturn([[maybe_unused]] GateRef gate, [[maybe_unused]] { // [STATE] [DEPEND] [VALUE] [RETURN_LIST] GateRef operand = operands[2]; // 2: skip 2 in gate that are not data gate - LLVMValueRef returnValue = gate2LValue_[operand]; + LLVMValueRef returnValue = GetLValue(operand); LLVMBuildRet(builder_, returnValue); + + if (IsLogEnabled()) { + SetDebugInfo(gate, returnValue); + } } void LLVMIRBuilder::HandleReturn(GateRef gate) @@ -1091,7 +1149,7 @@ void LLVMIRBuilder::LinkToLLVMCfg(int bbId, const OperandsVector &predecessors) LLVMBasicBlockRef preLBB = EnsureLBB(pre); LLVMMoveBasicBlockBefore(preLBB, lBB); } - if (isPrologue(bbId)) { + if (IsPrologue(bbId)) { GenPrologue(); } } @@ -1145,26 +1203,16 @@ void LLVMIRBuilder::VisitConstant(GateRef gate, std::bitset<64> value) // 64: bi LLVMValueRef llvmValue = nullptr; auto machineType = acc_.GetMachineType(gate); if (machineType == MachineType::ARCH) { - machineType = compCfg_->Is32Bit() ? MachineType::I32 : MachineType::I64; + ASSERT(compCfg_->Is64Bit()); + machineType = MachineType::I64; } if (machineType == MachineType::I32) { - llvmValue = LLVMConstInt(LLVMInt32TypeInContext(context_), value.to_ulong(), 0); + llvmValue = LLVMConstInt(GetInt32T(), value.to_ulong(), 0); } else if (machineType == MachineType::I64) { - llvmValue = LLVMConstInt(LLVMInt64TypeInContext(context_), value.to_ullong(), 0); + llvmValue = LLVMConstInt(GetInt64T(), value.to_ullong(), 0); LLVMTypeRef type = ConvertLLVMTypeFromGate(gate); if (LLVMGetTypeKind(type) == LLVMPointerTypeKind) { llvmValue = LLVMBuildIntToPtr(builder_, llvmValue, type, ""); - } else if (LLVMGetTypeKind(type) == LLVMVectorTypeKind) { - LLVMValueRef tmp1Value = LLVMBuildLShr( - builder_, llvmValue, LLVMConstInt(LLVMInt64TypeInContext(context_), 32, 0), ""); // 32: offset - LLVMValueRef tmp2Value = LLVMBuildIntCast(builder_, llvmValue, LLVMInt32TypeInContext(context_), ""); // low - LLVMValueRef emptyValue = LLVMGetUndef(type); - tmp1Value = LLVMBuildIntToPtr(builder_, tmp1Value, LLVMPointerType(LLVMInt8TypeInContext(context_), 1), ""); - tmp2Value = LLVMBuildIntToPtr(builder_, tmp2Value, LLVMPointerType(LLVMInt8TypeInContext(context_), 1), ""); - llvmValue = LLVMBuildInsertElement( - builder_, emptyValue, tmp2Value, LLVMConstInt(LLVMInt32TypeInContext(context_), 0, 0), ""); - llvmValue = LLVMBuildInsertElement( - builder_, llvmValue, tmp1Value, LLVMConstInt(LLVMInt32TypeInContext(context_), 1, 0), ""); } else if (LLVMGetTypeKind(type) == LLVMIntegerTypeKind) { // do nothing } else { @@ -1173,18 +1221,18 @@ void LLVMIRBuilder::VisitConstant(GateRef gate, std::bitset<64> value) // 64: bi } } else if (machineType == MachineType::F64) { auto doubleValue = base::bit_cast(value.to_ullong()); // actual double value - llvmValue = LLVMConstReal(LLVMDoubleTypeInContext(context_), doubleValue); + llvmValue = LLVMConstReal(GetDoubleT(), doubleValue); } else if (machineType == MachineType::I8) { - llvmValue = LLVMConstInt(LLVMInt8TypeInContext(context_), value.to_ulong(), 0); + llvmValue = LLVMConstInt(GetInt8T(), value.to_ulong(), 0); } else if (machineType == MachineType::I16) { - llvmValue = LLVMConstInt(LLVMInt16TypeInContext(context_), value.to_ulong(), 0); + llvmValue = LLVMConstInt(GetInt16T(), value.to_ulong(), 0); } else if (machineType == MachineType::I1) { - llvmValue = LLVMConstInt(LLVMInt1TypeInContext(context_), value.to_ulong(), 0); + llvmValue = LLVMConstInt(GetInt1T(), value.to_ulong(), 0); } else { LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); } - gate2LValue_[gate] = llvmValue; + Bind(gate, llvmValue); } void LLVMIRBuilder::HandleConstString(GateRef gate) @@ -1199,7 +1247,7 @@ void LLVMIRBuilder::VisitConstString(GateRef gate, const ChunkVector &str) LLVMValueRef llvmValue1 = LLVMConstStringInContext(context_, str.data(), str.size(), 0); LLVMValueRef addr = LLVMBuildAlloca(builder_, LLVMTypeOf(llvmValue1), ""); LLVMBuildStore(builder_, llvmValue1, addr); - gate2LValue_[gate] = addr; + Bind(gate, addr); } void LLVMIRBuilder::HandleRelocatableData(GateRef gate) @@ -1210,9 +1258,9 @@ void LLVMIRBuilder::HandleRelocatableData(GateRef gate) void LLVMIRBuilder::VisitRelocatableData(GateRef gate, uint64_t value) { - LLVMValueRef globalValue = LLVMAddGlobal(module_, LLVMInt64TypeInContext(context_), "G"); - LLVMSetInitializer(globalValue, LLVMConstInt(LLVMInt64TypeInContext(context_), value, 0)); - gate2LValue_[gate] = globalValue; + LLVMValueRef globalValue = LLVMAddGlobal(module_, GetInt64T(), "G"); + LLVMSetInitializer(globalValue, LLVMConstInt(GetInt64T(), value, 0)); + Bind(gate, globalValue); } void LLVMIRBuilder::HandleZExtInt(GateRef gate) @@ -1239,7 +1287,7 @@ void LLVMIRBuilder::VisitParameter(GateRef gate) int argth = static_cast(acc_.TryGetValue(gate)); LLVMValueRef value = LLVMGetParam(function_, argth); ASSERT(LLVMTypeOf(value) == ConvertLLVMTypeFromGate(gate)); - gate2LValue_[gate] = value; + Bind(gate, value); // NOTE: caller put args, otherwise crash ASSERT(value != nullptr); } @@ -1280,8 +1328,8 @@ void LLVMIRBuilder::HandleMod(GateRef gate) void LLVMIRBuilder::VisitMod(GateRef gate, GateRef e1, GateRef e2) { - LLVMValueRef e1Value = gate2LValue_[e1]; - LLVMValueRef e2Value = gate2LValue_[e2]; + LLVMValueRef e1Value = GetLValue(e1); + LLVMValueRef e2Value = GetLValue(e2); LLVMValueRef result = nullptr; ASSERT(ConvertLLVMTypeFromGate(gate) == ConvertLLVMTypeFromGate(e1)); ASSERT(ConvertLLVMTypeFromGate(gate) == ConvertLLVMTypeFromGate(e2)); @@ -1294,7 +1342,11 @@ void LLVMIRBuilder::VisitMod(GateRef gate, GateRef e1, GateRef e2) LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); } - gate2LValue_[gate] = result; + Bind(gate, result); + + if (IsLogEnabled()) { + SetDebugInfo(gate, result); + } } void LLVMIRBuilder::VisitBranch(GateRef gate, GateRef cmp, int btrue, int bfalse) @@ -1303,7 +1355,7 @@ void LLVMIRBuilder::VisitBranch(GateRef gate, GateRef cmp, int btrue, int bfalse OPTIONAL_LOG_COMPILER(ERROR) << "Branch condition gate is nullptr!"; return; } - LLVMValueRef cond = gate2LValue_[cmp]; + LLVMValueRef cond = GetLValue(cmp); BasicBlock *trueBB = EnsureBB(btrue); BasicBlock *falseBB = EnsureBB(bfalse); @@ -1314,7 +1366,23 @@ void LLVMIRBuilder::VisitBranch(GateRef gate, GateRef cmp, int btrue, int bfalse LLVMBasicBlockRef llvmFalseBB = falseBB->GetImpl()->lBB_; LLVMValueRef result = LLVMBuildCondBr(builder_, cond, llvmTrueBB, llvmFalseBB); EndCurrentBlock(); - gate2LValue_[gate] = result; + + if (acc_.HasBranchWeight(gate)) { + auto trueWeight = acc_.GetTrueWeight(gate); + auto falseWeight = acc_.GetFalseWeight(gate); + LLVMMetadataRef branch_weights = LLVMMDStringInContext2(context_, "branch_weights", 14); + LLVMMetadataRef weight1 = LLVMValueAsMetadata(LLVMConstInt(LLVMIntType(32), trueWeight, 0)); + LLVMMetadataRef weight2 = LLVMValueAsMetadata(LLVMConstInt(LLVMIntType(32), falseWeight, 0)); + LLVMMetadataRef mds[] = {branch_weights, weight1, weight2}; + LLVMMetadataRef metadata = LLVMMDNodeInContext2(context_, mds, 3); + LLVMValueRef metadata_value = LLVMMetadataAsValue(context_, metadata); + LLVMSetMetadata(result, LLVMGetMDKindID("prof", 4), metadata_value); // 4: length of "prof" + } + Bind(gate, result); + + if (IsLogEnabled()) { + SetDebugInfo(gate, result); + } } void LLVMIRBuilder::HandleSwitch(GateRef gate) @@ -1328,7 +1396,7 @@ void LLVMIRBuilder::HandleSwitch(GateRef gate) void LLVMIRBuilder::VisitSwitch(GateRef gate, GateRef input, const std::vector &outList) { - LLVMValueRef cond = gate2LValue_[input]; + LLVMValueRef cond = GetLValue(input); int caseNum = static_cast(outList.size()); BasicBlock *curOutBB = nullptr; LLVMBasicBlockRef llvmDefaultOutBB = nullptr; @@ -1351,47 +1419,59 @@ void LLVMIRBuilder::VisitSwitch(GateRef gate, GateRef input, const std::vectorIs32Bit()) { - return LLVMVectorType(LLVMPointerType(LLVMInt8TypeInContext(context_), 1), 2); // 2: packed vector type - } else { - return LLVMPointerType(LLVMInt64TypeInContext(context_), 1); - } + return GetTaggedHPtrT(); } MachineType t = acc_.GetMachineType(gate); switch (t) { case MachineType::NOVALUE: - return LLVMVoidTypeInContext(context_); + return GetVoidT(); case MachineType::I1: - return LLVMInt1TypeInContext(context_); + return GetInt1T(); case MachineType::I8: - return LLVMInt8TypeInContext(context_); + return GetInt8T(); case MachineType::I16: - return LLVMInt16TypeInContext(context_); + return GetInt16T(); case MachineType::I32: - return LLVMInt32TypeInContext(context_); + return GetInt32T(); case MachineType::I64: - return LLVMInt64TypeInContext(context_); + return GetInt64T(); case MachineType::F32: - return LLVMFloatTypeInContext(context_); + return GetFloatT(); case MachineType::F64: - return LLVMDoubleTypeInContext(context_); + return GetDoubleT(); case MachineType::ARCH: { - if (compCfg_->Is32Bit()) { - return LLVMInt32TypeInContext(context_); - } else { - return LLVMInt64TypeInContext(context_); - } + return GetInt64T(); } default: LOG_ECMA(FATAL) << "this branch is unreachable"; @@ -1543,7 +1636,7 @@ void LLVMIRBuilder::HandleTruncFloatToInt(GateRef gate) void LLVMIRBuilder::VisitTruncFloatToInt(GateRef gate, GateRef e1) { - LLVMValueRef e1Value = gate2LValue_[e1]; + LLVMValueRef e1Value = GetLValue(e1); auto machineType = acc_.GetMachineType(e1); LLVMValueRef result = nullptr; if (machineType <= MachineType::F64 && machineType >= MachineType::F32) { @@ -1552,7 +1645,11 @@ void LLVMIRBuilder::VisitTruncFloatToInt(GateRef gate, GateRef e1) LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); } - gate2LValue_[gate] = result; + Bind(gate, result); + + if (IsLogEnabled()) { + SetDebugInfo(gate, result); + } } bool IsAddIntergerType(MachineType machineType) @@ -1571,22 +1668,15 @@ bool IsAddIntergerType(MachineType machineType) void LLVMIRBuilder::VisitAdd(GateRef gate, GateRef e1, GateRef e2) { - LLVMValueRef e1Value = gate2LValue_[e1]; - LLVMValueRef e2Value = gate2LValue_[e2]; + LLVMValueRef e1Value = GetLValue(e1); + LLVMValueRef e2Value = GetLValue(e2); LLVMValueRef result = nullptr; - /* - * If the first operand is pointer, special treatment is needed - * 1) add, pointer, int - * 2) add, vector{i8* x 2}, int - */ - LLVMTypeRef returnType = ConvertLLVMTypeFromGate(gate); + LLVMTypeRef returnType = ConvertLLVMTypeFromGate(gate); auto machineType = acc_.GetMachineType(gate); if (IsAddIntergerType(machineType)) { auto e1Type = LLVMGetTypeKind(ConvertLLVMTypeFromGate(e1)); - if (e1Type == LLVMVectorTypeKind) { - result = VectorAdd(e1Value, e2Value, returnType); - } else if (e1Type == LLVMPointerTypeKind) { + if (e1Type == LLVMPointerTypeKind) { result = PointerAdd(e1Value, e2Value, returnType); } else { LLVMValueRef tmp1Value = LLVMBuildIntCast2(builder_, e1Value, returnType, 0, ""); @@ -1602,7 +1692,11 @@ void LLVMIRBuilder::VisitAdd(GateRef gate, GateRef e1, GateRef e2) LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); } - gate2LValue_[gate] = result; + Bind(gate, result); + + if (IsLogEnabled()) { + SetDebugInfo(gate, result); + } } void LLVMIRBuilder::HandleSub(GateRef gate) @@ -1614,8 +1708,8 @@ void LLVMIRBuilder::HandleSub(GateRef gate) void LLVMIRBuilder::VisitSub(GateRef gate, GateRef e1, GateRef e2) { - LLVMValueRef e1Value = gate2LValue_[e1]; - LLVMValueRef e2Value = gate2LValue_[e2]; + LLVMValueRef e1Value = GetLValue(e1); + LLVMValueRef e2Value = GetLValue(e2); LLVMValueRef result = nullptr; auto machineType = acc_.GetMachineType(gate); if (machineType == MachineType::I16 || machineType == MachineType::I32 || @@ -1627,7 +1721,11 @@ void LLVMIRBuilder::VisitSub(GateRef gate, GateRef e1, GateRef e2) LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); } - gate2LValue_[gate] = result; + Bind(gate, result); + + if (IsLogEnabled()) { + SetDebugInfo(gate, result); + } } void LLVMIRBuilder::HandleMul(GateRef gate) @@ -1651,8 +1749,8 @@ bool IsMulIntergerType(MachineType machineType) void LLVMIRBuilder::VisitMul(GateRef gate, GateRef e1, GateRef e2) { - LLVMValueRef e1Value = gate2LValue_[e1]; - LLVMValueRef e2Value = gate2LValue_[e2]; + LLVMValueRef e1Value = GetLValue(e1); + LLVMValueRef e2Value = GetLValue(e2); LLVMValueRef result = nullptr; auto machineType = acc_.GetMachineType(gate); if (IsMulIntergerType(machineType)) { @@ -1663,7 +1761,11 @@ void LLVMIRBuilder::VisitMul(GateRef gate, GateRef e1, GateRef e2) LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); } - gate2LValue_[gate] = result; + Bind(gate, result); + + if (IsLogEnabled()) { + SetDebugInfo(gate, result); + } } void LLVMIRBuilder::HandleFloatDiv(GateRef gate) @@ -1733,26 +1835,24 @@ void LLVMIRBuilder::HandleAddWithOverflow(GateRef gate) void LLVMIRBuilder::VisitAddWithOverflow(GateRef gate, GateRef e1, GateRef e2) { - LLVMValueRef e1Value = gate2LValue_[e1]; - LLVMValueRef e2Value = gate2LValue_[e2]; + LLVMValueRef e1Value = GetLValue(e1); + LLVMValueRef e2Value = GetLValue(e2); std::vector args = { e1Value, e2Value }; auto fn = LLVMGetNamedFunction(module_, "llvm.sadd.with.overflow.i32"); if (!fn) { /* init instrinsic function declare */ - LLVMTypeRef paramTys1[] = { - LLVMInt32TypeInContext(context_), - LLVMInt32TypeInContext(context_), - }; - LLVMTypeRef structTys[] = { - LLVMInt32TypeInContext(context_), - LLVMInt1TypeInContext(context_), - }; + LLVMTypeRef paramTys1[] = { GetInt32T(), GetInt32T() }; + LLVMTypeRef structTys[] = { GetInt32T(), GetInt1T() }; LLVMTypeRef returnType = LLVMStructTypeInContext(context_, structTys, 2, 0); auto fnTy = LLVMFunctionType(returnType, paramTys1, 2, 0); fn = LLVMAddFunction(module_, "llvm.sadd.with.overflow.i32", fnTy); } LLVMValueRef result = LLVMBuildCall(builder_, fn, args.data(), 2, ""); - gate2LValue_[gate] = result; + Bind(gate, result); + + if (IsLogEnabled()) { + SetDebugInfo(gate, result); + } } void LLVMIRBuilder::HandleSubWithOverflow(GateRef gate) @@ -1766,26 +1866,24 @@ void LLVMIRBuilder::HandleSubWithOverflow(GateRef gate) void LLVMIRBuilder::VisitSubWithOverflow(GateRef gate, GateRef e1, GateRef e2) { - LLVMValueRef e1Value = gate2LValue_[e1]; - LLVMValueRef e2Value = gate2LValue_[e2]; + LLVMValueRef e1Value = GetLValue(e1); + LLVMValueRef e2Value = GetLValue(e2); std::vector args = { e1Value, e2Value }; auto fn = LLVMGetNamedFunction(module_, "llvm.ssub.with.overflow.i32"); if (!fn) { /* init instrinsic function declare */ - LLVMTypeRef paramTys1[] = { - LLVMInt32TypeInContext(context_), - LLVMInt32TypeInContext(context_), - }; - LLVMTypeRef structTys[] = { - LLVMInt32TypeInContext(context_), - LLVMInt1TypeInContext(context_), - }; + LLVMTypeRef paramTys1[] = { GetInt32T(), GetInt32T() }; + LLVMTypeRef structTys[] = { GetInt32T(), GetInt1T() }; LLVMTypeRef returnType = LLVMStructTypeInContext(context_, structTys, 2, 0); auto fnTy = LLVMFunctionType(returnType, paramTys1, 2, 0); fn = LLVMAddFunction(module_, "llvm.ssub.with.overflow.i32", fnTy); } LLVMValueRef result = LLVMBuildCall(builder_, fn, args.data(), 2, ""); - gate2LValue_[gate] = result; + Bind(gate, result); + + if (IsLogEnabled()) { + SetDebugInfo(gate, result); + } } void LLVMIRBuilder::HandleMulWithOverflow(GateRef gate) @@ -1799,26 +1897,24 @@ void LLVMIRBuilder::HandleMulWithOverflow(GateRef gate) void LLVMIRBuilder::VisitMulWithOverflow(GateRef gate, GateRef e1, GateRef e2) { - LLVMValueRef e1Value = gate2LValue_[e1]; - LLVMValueRef e2Value = gate2LValue_[e2]; + LLVMValueRef e1Value = GetLValue(e1); + LLVMValueRef e2Value = GetLValue(e2); std::vector args = { e1Value, e2Value }; auto fn = LLVMGetNamedFunction(module_, "llvm.smul.with.overflow.i32"); if (!fn) { /* init instrinsic function declare */ - LLVMTypeRef paramTys1[] = { - LLVMInt32TypeInContext(context_), - LLVMInt32TypeInContext(context_), - }; - LLVMTypeRef structTys[] = { - LLVMInt32TypeInContext(context_), - LLVMInt1TypeInContext(context_), - }; + LLVMTypeRef paramTys1[] = { GetInt32T(), GetInt32T() }; + LLVMTypeRef structTys[] = { GetInt32T(), GetInt1T() }; LLVMTypeRef returnType = LLVMStructTypeInContext(context_, structTys, 2, 0); auto fnTy = LLVMFunctionType(returnType, paramTys1, 2, 0); fn = LLVMAddFunction(module_, "llvm.smul.with.overflow.i32", fnTy); } LLVMValueRef result = LLVMBuildCall(builder_, fn, args.data(), 2, ""); - gate2LValue_[gate] = result; + Bind(gate, result); + + if (IsLogEnabled()) { + SetDebugInfo(gate, result); + } } void LLVMIRBuilder::HandleExtractValue(GateRef gate) @@ -1830,11 +1926,15 @@ void LLVMIRBuilder::HandleExtractValue(GateRef gate) void LLVMIRBuilder::VisitExtractValue(GateRef gate, GateRef e1, GateRef e2) { - LLVMValueRef e1Value = gate2LValue_[e1]; + LLVMValueRef e1Value = GetLValue(e1); ASSERT((acc_.GetOpCode(e2) == OpCode::CONSTANT) && acc_.GetMachineType(e2) == MachineType::I32); uint32_t index = static_cast(acc_.GetConstantValue(e2)); LLVMValueRef result = LLVMBuildExtractValue(builder_, e1Value, index, ""); - gate2LValue_[gate] = result; + Bind(gate, result); + + if (IsLogEnabled()) { + SetDebugInfo(gate, result); + } } void LLVMIRBuilder::HandleSqrt(GateRef gate) @@ -1845,19 +1945,21 @@ void LLVMIRBuilder::HandleSqrt(GateRef gate) void LLVMIRBuilder::VisitSqrt(GateRef gate, GateRef e1) { - LLVMValueRef e1Value = gate2LValue_[e1]; + LLVMValueRef e1Value = GetLValue(e1); std::vector args = { e1Value }; auto fn = LLVMGetNamedFunction(module_, "llvm.sqrt.f64"); if (!fn) { /* init instrinsic function declare */ - LLVMTypeRef paramTys1[] = { - LLVMDoubleTypeInContext(context_), - }; - auto fnTy = LLVMFunctionType(LLVMDoubleTypeInContext(context_), paramTys1, 1, 0); + LLVMTypeRef paramTys1[] = { GetDoubleT() }; + auto fnTy = LLVMFunctionType(GetDoubleT(), paramTys1, 1, 0); fn = LLVMAddFunction(module_, "llvm.sqrt.f64", fnTy); } LLVMValueRef result = LLVMBuildCall(builder_, fn, args.data(), 1, ""); - gate2LValue_[gate] = result; + Bind(gate, result); + + if (IsLogEnabled()) { + SetDebugInfo(gate, result); + } } LLVMIntPredicate LLVMIRBuilder::ConvertLLVMPredicateFromICMP(ICmpCondition cond) @@ -1914,15 +2016,13 @@ LLVMRealPredicate LLVMIRBuilder::ConvertLLVMPredicateFromFCMP(FCmpCondition cond void LLVMIRBuilder::VisitCmp(GateRef gate, GateRef e1, GateRef e2) { - LLVMValueRef e1Value = gate2LValue_[e1]; - LLVMValueRef e2Value = gate2LValue_[e2]; + LLVMValueRef e1Value = GetLValue(e1); + LLVMValueRef e2Value = GetLValue(e2); LLVMValueRef result = nullptr; [[maybe_unused]] auto e1ValCode = acc_.GetMachineType(e1); [[maybe_unused]] auto e2ValCode = acc_.GetMachineType(e2); ASSERT((e1ValCode == e2ValCode) || - (compCfg_->Is32Bit() && (e1ValCode == MachineType::ARCH) && (e2ValCode == MachineType::I32)) || (compCfg_->Is64Bit() && (e1ValCode == MachineType::ARCH) && (e2ValCode == MachineType::I64)) || - (compCfg_->Is32Bit() && (e2ValCode == MachineType::ARCH) && (e1ValCode == MachineType::I32)) || (compCfg_->Is64Bit() && (e2ValCode == MachineType::ARCH) && (e1ValCode == MachineType::I64))); LLVMIntPredicate intOpcode = LLVMIntEQ; LLVMRealPredicate realOpcode = LLVMRealPredicateFalse; @@ -1939,8 +2039,11 @@ void LLVMIRBuilder::VisitCmp(GateRef gate, GateRef e1, GateRef e2) LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); } + Bind(gate, result); - gate2LValue_[gate] = result; + if (IsLogEnabled()) { + SetDebugInfo(gate, result); + } } void LLVMIRBuilder::HandleLoad(GateRef gate) @@ -1980,35 +2083,51 @@ void LLVMIRBuilder::HandleChangeInt64ToTagged(GateRef gate) void LLVMIRBuilder::VisitIntDiv(GateRef gate, GateRef e1, GateRef e2) { - LLVMValueRef e1Value = gate2LValue_[e1]; - LLVMValueRef e2Value = gate2LValue_[e2]; + LLVMValueRef e1Value = GetLValue(e1); + LLVMValueRef e2Value = GetLValue(e2); LLVMValueRef result = LLVMBuildSDiv(builder_, e1Value, e2Value, ""); - gate2LValue_[gate] = result; + Bind(gate, result); + + if (IsLogEnabled()) { + SetDebugInfo(gate, result); + } } void LLVMIRBuilder::VisitUDiv(GateRef gate, GateRef e1, GateRef e2) { - LLVMValueRef e1Value = gate2LValue_[e1]; - LLVMValueRef e2Value = gate2LValue_[e2]; + LLVMValueRef e1Value = GetLValue(e1); + LLVMValueRef e2Value = GetLValue(e2); LLVMValueRef result = LLVMBuildUDiv(builder_, e1Value, e2Value, ""); - gate2LValue_[gate] = result; + Bind(gate, result); + + if (IsLogEnabled()) { + SetDebugInfo(gate, result); + } } void LLVMIRBuilder::VisitFloatDiv(GateRef gate, GateRef e1, GateRef e2) { - LLVMValueRef e1Value = gate2LValue_[e1]; - LLVMValueRef e2Value = gate2LValue_[e2]; + LLVMValueRef e1Value = GetLValue(e1); + LLVMValueRef e2Value = GetLValue(e2); LLVMValueRef result = LLVMBuildFDiv(builder_, e1Value, e2Value, ""); - gate2LValue_[gate] = result; + Bind(gate, result); + + if (IsLogEnabled()) { + SetDebugInfo(gate, result); + } } void LLVMIRBuilder::VisitIntOr(GateRef gate, GateRef e1, GateRef e2) { - LLVMValueRef e1Value = gate2LValue_[e1]; - LLVMValueRef e2Value = gate2LValue_[e2]; + LLVMValueRef e1Value = GetLValue(e1); + LLVMValueRef e2Value = GetLValue(e2); LLVMValueRef result = LLVMBuildOr(builder_, e1Value, e2Value, ""); - gate2LValue_[gate] = result; + Bind(gate, result); + + if (IsLogEnabled()) { + SetDebugInfo(gate, result); + } } void LLVMIRBuilder::HandleIntAnd(GateRef gate) @@ -2020,34 +2139,50 @@ void LLVMIRBuilder::HandleIntAnd(GateRef gate) void LLVMIRBuilder::VisitIntAnd(GateRef gate, GateRef e1, GateRef e2) { - LLVMValueRef e1Value = gate2LValue_[e1]; - LLVMValueRef e2Value = gate2LValue_[e2]; + LLVMValueRef e1Value = GetLValue(e1); + LLVMValueRef e2Value = GetLValue(e2); LLVMValueRef result = LLVMBuildAnd(builder_, e1Value, e2Value, ""); - gate2LValue_[gate] = result; + Bind(gate, result); + + if (IsLogEnabled()) { + SetDebugInfo(gate, result); + } } void LLVMIRBuilder::VisitIntXor(GateRef gate, GateRef e1, GateRef e2) { - LLVMValueRef e1Value = gate2LValue_[e1]; - LLVMValueRef e2Value = gate2LValue_[e2]; + LLVMValueRef e1Value = GetLValue(e1); + LLVMValueRef e2Value = GetLValue(e2); LLVMValueRef result = LLVMBuildXor(builder_, e1Value, e2Value, ""); - gate2LValue_[gate] = result; + Bind(gate, result); + + if (IsLogEnabled()) { + SetDebugInfo(gate, result); + } } void LLVMIRBuilder::VisitIntLsr(GateRef gate, GateRef e1, GateRef e2) { - LLVMValueRef e1Value = gate2LValue_[e1]; - LLVMValueRef e2Value = gate2LValue_[e2]; + LLVMValueRef e1Value = GetLValue(e1); + LLVMValueRef e2Value = GetLValue(e2); LLVMValueRef result = LLVMBuildLShr(builder_, e1Value, e2Value, ""); - gate2LValue_[gate] = result; + Bind(gate, result); + + if (IsLogEnabled()) { + SetDebugInfo(gate, result); + } } void LLVMIRBuilder::VisitIntAsr(GateRef gate, GateRef e1, GateRef e2) { - LLVMValueRef e1Value = gate2LValue_[e1]; - LLVMValueRef e2Value = gate2LValue_[e2]; + LLVMValueRef e1Value = GetLValue(e1); + LLVMValueRef e2Value = GetLValue(e2); LLVMValueRef result = LLVMBuildAShr(builder_, e1Value, e2Value, ""); - gate2LValue_[gate] = result; + Bind(gate, result); + + if (IsLogEnabled()) { + SetDebugInfo(gate, result); + } } void LLVMIRBuilder::HandleIntLsl(GateRef gate) @@ -2059,26 +2194,38 @@ void LLVMIRBuilder::HandleIntLsl(GateRef gate) void LLVMIRBuilder::VisitIntLsl(GateRef gate, GateRef e1, GateRef e2) { - LLVMValueRef e1Value = gate2LValue_[e1]; - LLVMValueRef e2Value = gate2LValue_[e2]; + LLVMValueRef e1Value = GetLValue(e1); + LLVMValueRef e2Value = GetLValue(e2); LLVMValueRef result = LLVMBuildShl(builder_, e1Value, e2Value, ""); - gate2LValue_[gate] = result; + Bind(gate, result); + + if (IsLogEnabled()) { + SetDebugInfo(gate, result); + } } void LLVMIRBuilder::VisitZExtInt(GateRef gate, GateRef e1) { - LLVMValueRef e1Value = gate2LValue_[e1]; + LLVMValueRef e1Value = GetLValue(e1); ASSERT(GetBitWidthFromMachineType(acc_.GetMachineType(e1)) <= GetBitWidthFromMachineType(acc_.GetMachineType(gate))); LLVMValueRef result = LLVMBuildZExt(builder_, e1Value, ConvertLLVMTypeFromGate(gate), ""); - gate2LValue_[gate] = result; + Bind(gate, result); + + if (IsLogEnabled()) { + SetDebugInfo(gate, result); + } } void LLVMIRBuilder::VisitSExtInt(GateRef gate, GateRef e1) { - LLVMValueRef e1Value = gate2LValue_[e1]; + LLVMValueRef e1Value = GetLValue(e1); LLVMValueRef result = LLVMBuildSExt(builder_, e1Value, ConvertLLVMTypeFromGate(gate), ""); - gate2LValue_[gate] = result; + Bind(gate, result); + + if (IsLogEnabled()) { + SetDebugInfo(gate, result); + } } void LLVMIRBuilder::HandleCastIntXToIntY(GateRef gate) @@ -2088,11 +2235,15 @@ void LLVMIRBuilder::HandleCastIntXToIntY(GateRef gate) void LLVMIRBuilder::VisitCastIntXToIntY(GateRef gate, GateRef e1) { - LLVMValueRef e1Value = gate2LValue_[e1]; + LLVMValueRef e1Value = GetLValue(e1); ASSERT(GetBitWidthFromMachineType(acc_.GetMachineType(e1)) >= GetBitWidthFromMachineType(acc_.GetMachineType(gate))); LLVMValueRef result = LLVMBuildIntCast2(builder_, e1Value, ConvertLLVMTypeFromGate(gate), 1, ""); - gate2LValue_[gate] = result; + Bind(gate, result); + + if (IsLogEnabled()) { + SetDebugInfo(gate, result); + } } void LLVMIRBuilder::HandleFPExt(GateRef gate) @@ -2102,11 +2253,15 @@ void LLVMIRBuilder::HandleFPExt(GateRef gate) void LLVMIRBuilder::VisitFPExt(GateRef gate, GateRef e1) { - LLVMValueRef e1Value = gate2LValue_[e1]; + LLVMValueRef e1Value = GetLValue(e1); ASSERT(GetBitWidthFromMachineType(acc_.GetMachineType(e1)) <= GetBitWidthFromMachineType(acc_.GetMachineType(gate))); LLVMValueRef result = LLVMBuildFPExt(builder_, e1Value, ConvertLLVMTypeFromGate(gate), ""); - gate2LValue_[gate] = result; + Bind(gate, result); + + if (IsLogEnabled()) { + SetDebugInfo(gate, result); + } } void LLVMIRBuilder::HandleFPTrunc(GateRef gate) @@ -2116,63 +2271,71 @@ void LLVMIRBuilder::HandleFPTrunc(GateRef gate) void LLVMIRBuilder::VisitFPTrunc(GateRef gate, GateRef e1) { - LLVMValueRef e1Value = gate2LValue_[e1]; + LLVMValueRef e1Value = GetLValue(e1); ASSERT(GetBitWidthFromMachineType(acc_.GetMachineType(e1)) >= GetBitWidthFromMachineType(acc_.GetMachineType(gate))); LLVMValueRef result = LLVMBuildFPTrunc(builder_, e1Value, ConvertLLVMTypeFromGate(gate), ""); - gate2LValue_[gate] = result; + Bind(gate, result); + + if (IsLogEnabled()) { + SetDebugInfo(gate, result); + } } void LLVMIRBuilder::VisitChangeInt32ToDouble(GateRef gate, GateRef e1) { - LLVMValueRef e1Value = gate2LValue_[e1]; + LLVMValueRef e1Value = GetLValue(e1); LLVMValueRef result = LLVMBuildSIToFP(builder_, e1Value, ConvertLLVMTypeFromGate(gate), ""); - gate2LValue_[gate] = result; + Bind(gate, result); + + if (IsLogEnabled()) { + SetDebugInfo(gate, result); + } } void LLVMIRBuilder::VisitChangeUInt32ToDouble(GateRef gate, GateRef e1) { - LLVMValueRef e1Value = gate2LValue_[e1]; - LLVMValueRef result = LLVMBuildUIToFP(builder_, e1Value, LLVMDoubleTypeInContext(context_), ""); - gate2LValue_[gate] = result; + LLVMValueRef e1Value = GetLValue(e1); + LLVMValueRef result = LLVMBuildUIToFP(builder_, e1Value, GetDoubleT(), ""); + Bind(gate, result); + + if (IsLogEnabled()) { + SetDebugInfo(gate, result); + } } void LLVMIRBuilder::VisitChangeDoubleToInt32(GateRef gate, GateRef e1) { - LLVMValueRef e1Value = gate2LValue_[e1]; - LLVMValueRef result = LLVMBuildFPToSI(builder_, e1Value, LLVMInt32TypeInContext(context_), ""); - gate2LValue_[gate] = result; + LLVMValueRef e1Value = GetLValue(e1); + LLVMValueRef result = LLVMBuildFPToSI(builder_, e1Value, GetInt32T(), ""); + Bind(gate, result); + + if (IsLogEnabled()) { + SetDebugInfo(gate, result); + } } void LLVMIRBuilder::VisitChangeTaggedPointerToInt64(GateRef gate, GateRef e1) { - LLVMValueRef e1Value = gate2LValue_[e1]; + LLVMValueRef e1Value = GetLValue(e1); LLVMValueRef result = CanonicalizeToInt(e1Value); - gate2LValue_[gate] = result; + Bind(gate, result); + + if (IsLogEnabled()) { + SetDebugInfo(gate, result); + } } void LLVMIRBuilder::VisitChangeInt64ToTagged(GateRef gate, GateRef e1) { - LLVMValueRef e1Value = gate2LValue_[e1]; + LLVMValueRef e1Value = GetLValue(e1); ASSERT(LLVMGetTypeKind(LLVMTypeOf(e1Value)) == LLVMIntegerTypeKind); - LLVMValueRef result; - if (compCfg_->Is32Bit()) { - LLVMValueRef tmp1Value = - LLVMBuildLShr(builder_, e1Value, LLVMConstInt(LLVMInt64TypeInContext(context_), 32, 0), ""); // 32: offset - LLVMValueRef tmp2Value = LLVMBuildIntCast(builder_, e1Value, LLVMInt32TypeInContext(context_), ""); // low - LLVMTypeRef vectorType = LLVMVectorType( - LLVMPointerType(LLVMInt8TypeInContext(context_), 1), 2); // 2: packed vector type - LLVMValueRef emptyValue = LLVMGetUndef(vectorType); - tmp1Value = LLVMBuildIntToPtr(builder_, tmp1Value, LLVMPointerType(LLVMInt8TypeInContext(context_), 1), ""); - tmp2Value = LLVMBuildIntToPtr(builder_, tmp2Value, LLVMPointerType(LLVMInt8TypeInContext(context_), 1), ""); - result = LLVMBuildInsertElement( - builder_, emptyValue, tmp2Value, LLVMConstInt(LLVMInt32TypeInContext(context_), 0, 0), ""); - result = LLVMBuildInsertElement( - builder_, result, tmp1Value, LLVMConstInt(LLVMInt32TypeInContext(context_), 1, 0), ""); - } else { - result = LLVMBuildIntToPtr(builder_, e1Value, LLVMPointerType(LLVMInt64TypeInContext(context_), 1), ""); + LLVMValueRef result = LLVMBuildIntToPtr(builder_, e1Value, GetTaggedHPtrT(), ""); + Bind(gate, result); + + if (IsLogEnabled()) { + SetDebugInfo(gate, result); } - gate2LValue_[gate] = result; } void LLVMIRBuilder::HandleBitCast(GateRef gate) @@ -2182,12 +2345,16 @@ void LLVMIRBuilder::HandleBitCast(GateRef gate) void LLVMIRBuilder::VisitBitCast(GateRef gate, GateRef e1) { - LLVMValueRef e1Value = gate2LValue_[e1]; + LLVMValueRef e1Value = GetLValue(e1); ASSERT(GetBitWidthFromMachineType(acc_.GetMachineType(gate)) == GetBitWidthFromMachineType(acc_.GetMachineType(e1))); auto returnType = ConvertLLVMTypeFromGate(gate); LLVMValueRef result = LLVMBuildBitCast(builder_, e1Value, returnType, ""); - gate2LValue_[gate] = result; + Bind(gate, result); + + if (IsLogEnabled()) { + SetDebugInfo(gate, result); + } } void LLVMIRBuilder::HandleDeoptCheck(GateRef gate) @@ -2202,8 +2369,17 @@ void LLVMIRBuilder::HandleDeoptCheck(GateRef gate) std::string buf = "deopt if false B" + std::to_string(block); LLVMBasicBlockRef llvmFalseBB = LLVMAppendBasicBlock(function_, buf.c_str()); GateRef cmp = acc_.GetValueIn(gate, 0); // 0: cond - LLVMValueRef cond = gate2LValue_[cmp]; + LLVMValueRef cond = GetLValue(cmp); LLVMValueRef result = LLVMBuildCondBr(builder_, cond, llvmTrueBB, llvmFalseBB); + + LLVMMetadataRef branch_weights = LLVMMDStringInContext2(context_, "branch_weights", 14); + LLVMMetadataRef weight1 = LLVMValueAsMetadata(LLVMConstInt(LLVMIntType(32), BranchWeight::DEOPT_WEIGHT, 0)); + LLVMMetadataRef weight2 = LLVMValueAsMetadata(LLVMConstInt(LLVMIntType(32), BranchWeight::ONE_WEIGHT, 0)); + LLVMMetadataRef mds[] = {branch_weights, weight1, weight2}; + LLVMMetadataRef metadata = LLVMMDNodeInContext2(context_, mds, 3); // 3: size of mds + LLVMValueRef metadata_value = LLVMMetadataAsValue(context_, metadata); + LLVMSetMetadata(result, LLVMGetMDKindID("prof", 4), metadata_value); // 4: length of "prof" + EndCurrentBlock(); LLVMPositionBuilderAtEnd(builder_, llvmFalseBB); @@ -2211,17 +2387,17 @@ void LLVMIRBuilder::HandleDeoptCheck(GateRef gate) LLVMMoveBasicBlockBefore(preLBB, llvmFalseBB); VisitDeoptCheck(gate); - LLVMValueRef returnValue = gate2LValue_[gate]; + LLVMValueRef returnValue = GetLValue(gate); if (IsLogEnabled()) { SetDebugInfo(gate, returnValue); } LLVMBuildRet(builder_, returnValue); - gate2LValue_[gate] = result; + Bind(gate, result); } LLVMTypeRef LLVMIRBuilder::GetExperimentalDeoptTy() { - auto fnTy = LLVMFunctionType(LLVMPointerType(LLVMInt64TypeInContext(context_), 1), nullptr, 0, 1); + auto fnTy = LLVMFunctionType(GetTaggedHPtrT(), nullptr, 0, 1); return fnTy; } @@ -2234,9 +2410,8 @@ LLVMValueRef LLVMModule::GetDeoptFunction() void LLVMIRBuilder::GenDeoptEntry(LLVMModuleRef &module) { // glue type depth - std::vector paramTys = { - LLVMInt64TypeInContext(context_), LLVMInt64TypeInContext(context_), LLVMInt64TypeInContext(context_)}; - auto funcType = LLVMFunctionType(LLVMInt64TypeInContext(context_), paramTys.data(), paramTys.size(), 0); + std::vector paramTys = { GetInt64T(), GetInt64T(), GetInt64T() }; + auto funcType = LLVMFunctionType(GetInt64T(), paramTys.data(), paramTys.size(), 0); auto function = LLVMAddFunction(module, Deoptimizier::GetLLVMDeoptRelocateSymbol(), funcType); LLVMSetFunctionCallConv(function, LLVMCCallConv); llvmModule_->SetFunction(LLVMModule::kDeoptEntryOffset, function, false); @@ -2256,8 +2431,7 @@ void LLVMIRBuilder::GenDeoptEntry(LLVMModuleRef &module) StubIdType stubId = RTSTUB_ID(DeoptHandlerAsm); int stubIndex = static_cast(std::get(stubId)); LLVMValueRef rtoffset = LLVMBuildAdd(builder, glue, GetRTStubOffset(glue, stubIndex), ""); - LLVMValueRef patchAddr = LLVMBuildIntToPtr( - builder, rtoffset, LLVMPointerType(LLVMInt64TypeInContext(context_), 0), ""); + LLVMValueRef patchAddr = LLVMBuildIntToPtr(builder, rtoffset, GetTaggedPtrT(), ""); LLVMValueRef llvmAddr = LLVMBuildLoad(builder, patchAddr, ""); LLVMTypeRef rtfuncTypePtr = LLVMPointerType(funcType, 0); LLVMValueRef callee = LLVMBuildIntToPtr(builder, llvmAddr, rtfuncTypePtr, ""); @@ -2282,34 +2456,34 @@ LLVMValueRef LLVMIRBuilder::GetExperimentalDeopt(LLVMModuleRef &module) LLVMValueRef LLVMIRBuilder::ConvertBoolToTaggedBoolean(GateRef gate) { - LLVMValueRef value = gate2LValue_[gate]; - LLVMValueRef e1Value = LLVMBuildZExt(builder_, value, LLVMInt64TypeInContext(context_), ""); - auto tagMask = LLVMConstInt(LLVMInt64TypeInContext(context_), JSTaggedValue::TAG_BOOLEAN_MASK, 0); + LLVMValueRef value = GetLValue(gate); + LLVMValueRef e1Value = LLVMBuildZExt(builder_, value, GetInt64T(), ""); + auto tagMask = LLVMConstInt(GetInt64T(), JSTaggedValue::TAG_BOOLEAN_MASK, 0); LLVMValueRef result = LLVMBuildOr(builder_, e1Value, tagMask, ""); - return LLVMBuildIntToPtr(builder_, result, LLVMPointerType(LLVMInt64TypeInContext(context_), 1), ""); + return LLVMBuildIntToPtr(builder_, result, GetTaggedHPtrT(), ""); } LLVMValueRef LLVMIRBuilder::ConvertInt32ToTaggedInt(GateRef gate) { - LLVMValueRef value = gate2LValue_[gate]; + LLVMValueRef value = GetLValue(gate); return ConvertInt32ToTaggedInt(value); } LLVMValueRef LLVMIRBuilder::ConvertInt32ToTaggedInt(LLVMValueRef value) { - LLVMValueRef e1Value = LLVMBuildSExt(builder_, value, LLVMInt64TypeInContext(context_), ""); - auto tagMask = LLVMConstInt(LLVMInt64TypeInContext(context_), JSTaggedValue::TAG_INT, 0); + LLVMValueRef e1Value = LLVMBuildSExt(builder_, value, GetInt64T(), ""); + auto tagMask = LLVMConstInt(GetInt64T(), JSTaggedValue::TAG_INT, 0); LLVMValueRef result = LLVMBuildOr(builder_, e1Value, tagMask, ""); - return LLVMBuildIntToPtr(builder_, result, LLVMPointerType(LLVMInt64TypeInContext(context_), 1), ""); + return LLVMBuildIntToPtr(builder_, result, GetTaggedHPtrT(), ""); } LLVMValueRef LLVMIRBuilder::ConvertFloat64ToTaggedDouble(GateRef gate) { - LLVMValueRef value = gate2LValue_[gate]; - LLVMValueRef e1Value = LLVMBuildBitCast(builder_, value, LLVMInt64TypeInContext(context_), ""); - auto offset = LLVMConstInt(LLVMInt64TypeInContext(context_), JSTaggedValue::DOUBLE_ENCODE_OFFSET, 0); + LLVMValueRef value = GetLValue(gate); + LLVMValueRef e1Value = LLVMBuildBitCast(builder_, value, GetInt64T(), ""); + auto offset = LLVMConstInt(GetInt64T(), JSTaggedValue::DOUBLE_ENCODE_OFFSET, 0); LLVMValueRef result = LLVMBuildAdd(builder_, e1Value, offset, ""); - return LLVMBuildIntToPtr(builder_, result, LLVMPointerType(LLVMInt64TypeInContext(context_), 1), ""); + return LLVMBuildIntToPtr(builder_, result, GetTaggedHPtrT(), ""); } LLVMValueRef LLVMIRBuilder::ConvertToTagged(GateRef gate) @@ -2336,17 +2510,16 @@ void LLVMIRBuilder::SaveDeoptVregInfo(std::vector &values, int32_t GateRef gate) { int32_t encodeIndex = Deoptimizier::EncodeDeoptVregIndex(index, curDepth, shift); - values.emplace_back(LLVMConstInt(LLVMInt32TypeInContext(context_), encodeIndex, false)); + values.emplace_back(LLVMConstInt(GetInt32T(), encodeIndex, false)); values.emplace_back(ConvertToTagged(gate)); } void LLVMIRBuilder::SaveDeoptVregInfoWithI64(std::vector &values, int32_t index, size_t curDepth, size_t shift, GateRef gate) { - LLVMValueRef value = LLVMBuildIntCast2(builder_, gate2LValue_.at(gate), - LLVMInt32TypeInContext(context_), 1, ""); + LLVMValueRef value = LLVMBuildIntCast2(builder_, gate2LValue_.at(gate), GetInt32T(), 1, ""); int32_t encodeIndex = Deoptimizier::EncodeDeoptVregIndex(index, curDepth, shift); - values.emplace_back(LLVMConstInt(LLVMInt32TypeInContext(context_), encodeIndex, false)); + values.emplace_back(LLVMConstInt(GetInt32T(), encodeIndex, false)); values.emplace_back(ConvertInt32ToTaggedInt(value)); } @@ -2359,24 +2532,17 @@ void LLVMIRBuilder::VisitDeoptCheck(GateRef gate) params.push_back(glue); // glue GateRef deoptType = acc_.GetValueIn(gate, 2); // 2: deopt type uint64_t v = acc_.GetConstantValue(deoptType); - params.push_back(ConvertInt32ToTaggedInt(LLVMConstInt(LLVMInt32TypeInContext(context_), - static_cast(v), false))); // deoptType + params.push_back(ConvertInt32ToTaggedInt(LLVMConstInt(GetInt32T(), static_cast(v), false))); // deoptType LLVMValueRef callee = GetExperimentalDeopt(module_); LLVMTypeRef funcType = GetExperimentalDeoptTy(); std::vector values; - size_t maxDepth = 0; - GateRef frameState = acc_.GetFrameState(deoptFrameState); - while ((acc_.GetOpCode(frameState) == OpCode::FRAME_STATE)) { - maxDepth++; - frameState = acc_.GetFrameState(frameState); - } - params.push_back(ConvertInt32ToTaggedInt(LLVMConstInt(LLVMInt32TypeInContext(context_), - static_cast(maxDepth), false))); + size_t maxDepth = acc_.GetFrameDepth(deoptFrameState, OpCode::FRAME_STATE); + params.push_back(ConvertInt32ToTaggedInt(LLVMConstInt(GetInt32T(), static_cast(maxDepth), false))); size_t shift = Deoptimizier::ComputeShift(maxDepth); - frameState = deoptFrameState; + GateRef frameState = deoptFrameState; ArgumentAccessor argAcc(const_cast(circuit_)); - for (size_t curDepth = 0; curDepth <= maxDepth; curDepth++) { + for (int32_t curDepth = static_cast(maxDepth); curDepth >= 0; curDepth--) { ASSERT(acc_.GetOpCode(frameState) == OpCode::FRAME_STATE); GateRef frameValues = acc_.GetValueIn(frameState, 1); // 1: frame values const size_t numValueIn = acc_.GetNumValueIn(frameValues); @@ -2410,8 +2576,8 @@ void LLVMIRBuilder::VisitDeoptCheck(GateRef gate) // pc offset int32_t specPcOffsetIndex = static_cast(SpecVregIndex::PC_OFFSET_INDEX); int32_t encodeIndex = Deoptimizier::EncodeDeoptVregIndex(specPcOffsetIndex, curDepth, shift); - values.emplace_back(LLVMConstInt(LLVMInt32TypeInContext(context_), encodeIndex, false)); - values.emplace_back(LLVMConstInt(LLVMInt32TypeInContext(context_), pc, false)); + values.emplace_back(LLVMConstInt(GetInt32T(), encodeIndex, false)); + values.emplace_back(LLVMConstInt(GetInt32T(), pc, false)); // func int32_t specCallTargetIndex = static_cast(SpecVregIndex::FUNC_INDEX); SaveDeoptVregInfo(values, specCallTargetIndex, curDepth, shift, jsFunc); @@ -2427,7 +2593,7 @@ void LLVMIRBuilder::VisitDeoptCheck(GateRef gate) } LLVMValueRef runtimeCall = LLVMBuildCall3(builder_, funcType, callee, params.data(), params.size(), "", values.data(), values.size()); - gate2LValue_[gate] = runtimeCall; + Bind(gate, runtimeCall); } LLVMModule::LLVMModule(NativeAreaAllocator* allocator, const std::string &name, bool logDbg, const std::string &triple) @@ -2445,6 +2611,18 @@ LLVMModule::LLVMModule(NativeAreaAllocator* allocator, const std::string &name, 0, 0, NULL, 0, 0, NULL, 0, LLVMDWARFEmissionFull, 0, 0, 0, "/", 1, "", 0); debugInfo_ = new DebugInfo(allocator, logDbg); + + voidT_ = LLVMVoidTypeInContext(context_); + int1T_ = LLVMInt1TypeInContext(context_); + int8T_ = LLVMInt8TypeInContext(context_); + int16T_ = LLVMInt16TypeInContext(context_); + int32T_ = LLVMInt32TypeInContext(context_); + int64T_ = LLVMInt64TypeInContext(context_); + floatT_ = LLVMFloatTypeInContext(context_); + doubleT_ = LLVMDoubleTypeInContext(context_); + taggedHPtrT_ = LLVMPointerType(LLVMInt64TypeInContext(context_), 1); + taggedPtrT_ = LLVMPointerType(LLVMInt64TypeInContext(context_), 0); + rawPtrT_ = LLVMPointerType(LLVMInt8TypeInContext(context_), 0); } LLVMModule::~LLVMModule() @@ -2535,29 +2713,22 @@ LLVMTypeRef LLVMModule::GenerateFuncType(const std::vector ¶ms LLVMTypeRef LLVMModule::ConvertLLVMTypeFromVariableType(VariableType type) { std::map machineTypeMap = { - {VariableType::VOID(), LLVMVoidTypeInContext(context_)}, - {VariableType::BOOL(), LLVMInt1TypeInContext(context_)}, - {VariableType::INT8(), LLVMInt8TypeInContext(context_)}, - {VariableType::INT16(), LLVMInt16TypeInContext(context_)}, - {VariableType::INT32(), LLVMInt32TypeInContext(context_)}, - {VariableType::INT64(), LLVMInt64TypeInContext(context_)}, - {VariableType::INT8(), LLVMInt8TypeInContext(context_)}, - {VariableType::INT16(), LLVMInt16TypeInContext(context_)}, - {VariableType::INT32(), LLVMInt32TypeInContext(context_)}, - {VariableType::INT64(), LLVMInt64TypeInContext(context_)}, - {VariableType::FLOAT32(), LLVMFloatTypeInContext(context_)}, - {VariableType::FLOAT64(), LLVMDoubleTypeInContext(context_)}, - {VariableType::NATIVE_POINTER(), LLVMInt64TypeInContext(context_)}, - {VariableType::JS_POINTER(), LLVMPointerType(LLVMInt64TypeInContext(context_), 1)}, - {VariableType::JS_ANY(), LLVMPointerType(LLVMInt64TypeInContext(context_), 1)}, + {VariableType::VOID(), GetVoidT() }, + {VariableType::BOOL(), GetInt1T() }, + {VariableType::INT8(), GetInt8T() }, + {VariableType::INT16(), GetInt16T() }, + {VariableType::INT32(), GetInt32T() }, + {VariableType::INT64(), GetInt64T() }, + {VariableType::INT8(), GetInt8T() }, + {VariableType::INT16(), GetInt16T() }, + {VariableType::INT32(), GetInt32T() }, + {VariableType::INT64(), GetInt64T() }, + {VariableType::FLOAT32(), GetFloatT() }, + {VariableType::FLOAT64(), GetDoubleT() }, + {VariableType::NATIVE_POINTER(), GetInt64T() }, + {VariableType::JS_POINTER(), GetTaggedHPtrT() }, + {VariableType::JS_ANY(), GetTaggedHPtrT()}, }; - if (Is32Bit()) { - machineTypeMap[VariableType::NATIVE_POINTER()] = LLVMInt32TypeInContext(context_); - LLVMTypeRef vectorType = LLVMVectorType( - LLVMPointerType(LLVMInt8TypeInContext(context_), 1), 2); // 2: packed vector type - machineTypeMap[VariableType::JS_POINTER()] = vectorType; - machineTypeMap[VariableType::JS_ANY()] = vectorType; - } return machineTypeMap[type]; } @@ -2582,13 +2753,13 @@ LLVMValueRef LLVMModule::AddFunc(const panda::ecmascript::MethodLiteral *methodL paramTys.emplace_back(actualArgc); auto funcIndex = static_cast(CommonArgIdx::FUNC); auto numOfComArgs = static_cast(CommonArgIdx::NUM_OF_ARGS); - paramCount = methodLiteral->GetNumArgs() + numOfComArgs; + paramCount = methodLiteral->GetNumArgsWithCallField() + numOfComArgs; auto numOfRestArgs = paramCount - funcIndex; paramTys.insert(paramTys.end(), numOfRestArgs, NewLType(MachineType::I64, GateType::TaggedValue())); } else { auto funcIndex = static_cast(FastCallArgIdx::FUNC); auto numOfComArgs = static_cast(FastCallArgIdx::NUM_OF_ARGS); - paramCount = methodLiteral->GetNumArgs() + numOfComArgs; + paramCount = methodLiteral->GetNumArgsWithCallField() + numOfComArgs; auto numOfRestArgs = paramCount - funcIndex; paramTys.insert(paramTys.end(), numOfRestArgs, NewLType(MachineType::I64, GateType::TaggedValue())); } diff --git a/ecmascript/compiler/llvm_ir_builder.h b/ecmascript/compiler/llvm_ir_builder.h index cb1ae22e5cd67d65a6dc8fe31b7f0e586d541848..1eb221072aadd8347bc724e7e28c38caf618c6b7 100644 --- a/ecmascript/compiler/llvm_ir_builder.h +++ b/ecmascript/compiler/llvm_ir_builder.h @@ -98,7 +98,7 @@ private: }; struct NotMergedPhiDesc { - BasicBlock *pred; + int predBBId; GateRef operand; LLVMValueRef phi; }; @@ -213,6 +213,60 @@ public: static constexpr int kDeoptEntryOffset = 0; + LLVMTypeRef GetVoidT() const + { + return voidT_; + } + + LLVMTypeRef GetInt1T() const + { + return int1T_; + } + + LLVMTypeRef GetInt8T() const + { + return int8T_; + } + + LLVMTypeRef GetInt16T() const + { + return int16T_; + } + + LLVMTypeRef GetInt32T() const + { + return int32T_; + } + + LLVMTypeRef GetInt64T() const + { + return int64T_; + } + + LLVMTypeRef GetFloatT() const + { + return floatT_; + } + + LLVMTypeRef GetDoubleT() const + { + return doubleT_; + } + + LLVMTypeRef GetTaggedPtrT() const + { + return taggedPtrT_; + } + + LLVMTypeRef GetTaggedHPtrT() const + { + return taggedHPtrT_; + } + + LLVMTypeRef GetRawPtrT() const + { + return rawPtrT_; + } private: LLVMValueRef AddAndGetFunc(const CallSignature *stubDescriptor); void InitialLLVMFuncTypeAndFuncByModuleCSigns(); @@ -229,6 +283,19 @@ private: LLVMMetadataRef dUnitMD_ {nullptr}; LLVMDIBuilderRef dBuilder_ {nullptr}; DebugInfo* debugInfo_ {nullptr}; + + LLVMTypeRef voidT_ {nullptr}; + LLVMTypeRef int1T_ {nullptr}; + LLVMTypeRef int8T_ {nullptr}; + LLVMTypeRef int16T_ {nullptr}; + LLVMTypeRef int32T_ {nullptr}; + LLVMTypeRef int64T_ {nullptr}; + LLVMTypeRef floatT_ {nullptr}; + LLVMTypeRef doubleT_ {nullptr}; + LLVMTypeRef taggedHPtrT_ {nullptr}; + LLVMTypeRef taggedPtrT_ {nullptr}; + LLVMTypeRef rawPtrT_ {nullptr}; + std::string tripleStr_; bool is64Bit_ {false}; Triple triple_; @@ -289,7 +356,8 @@ private: V(SubWithOverflow, (GateRef gate, GateRef e1, GateRef e2)) \ V(MulWithOverflow, (GateRef gate, GateRef e1, GateRef e2)) \ V(ExtractValue, (GateRef gate, GateRef e1, GateRef e2)) \ - V(Sqrt, (GateRef gate, GateRef e1)) + V(Sqrt, (GateRef gate, GateRef e1)) \ + V(ReadSp, (GateRef gate)) // runtime/common stub ID, opcodeOffset for bc stub using StubIdType = std::variant; @@ -298,7 +366,8 @@ class LLVMIRBuilder { public: LLVMIRBuilder(const std::vector> *schedule, Circuit *circuit, LLVMModule *module, LLVMValueRef function, const CompilationConfig *cfg, - CallSignature::CallConv callConv, bool enableLog, bool isFastCallAot, const std::string &funcName); + CallSignature::CallConv callConv, bool enableLog, bool isFastCallAot, const std::string &funcName, + bool enableOptInlining = false); ~LLVMIRBuilder(); void Build(); @@ -310,7 +379,7 @@ private: OPCODES(DECLAREHANDLEOPCODE) #undef DECLAREHANDLEOPCODE - bool isPrologue(int bbId) const + bool IsPrologue(int bbId) const { return bbId == 0; } @@ -333,19 +402,12 @@ private: void InitializeHandlers(); std::string LLVMValueToString(LLVMValueRef val) const; - LLVMTypeRef GetIntPtr() const - { - if (compCfg_->Is32Bit()) { - return LLVMInt32TypeInContext(context_); - } - return LLVMInt64TypeInContext(context_); - } LLVMTypeRef ConvertLLVMTypeFromGate(GateRef gate) const; int64_t GetBitWidthFromMachineType(MachineType machineType) const; - LLVMValueRef PointerAdd(LLVMValueRef baseAddr, LLVMValueRef offset, LLVMTypeRef rep); - LLVMValueRef VectorAdd(LLVMValueRef e1Value, LLVMValueRef e2Value, LLVMTypeRef rep); - LLVMValueRef CanonicalizeToInt(LLVMValueRef value); - LLVMValueRef CanonicalizeToPtr(LLVMValueRef value); + LLVMValueRef PointerAdd(LLVMValueRef baseAddr, LLVMValueRef offsetInByte, LLVMTypeRef rep); + LLVMValueRef CanonicalizeToInt(LLVMValueRef value) const; + LLVMValueRef CanonicalizeToPtr(LLVMValueRef value) const; + LLVMValueRef CanonicalizeToPtr(LLVMValueRef value, LLVMTypeRef ptrType) const; LLVMValueRef GetCurrentFrameType(LLVMValueRef currentSpFrameAddr); void SetFunctionCallConv(); @@ -357,6 +419,8 @@ private: const std::string &realName = "") const; LLVMValueRef GetCallee(const std::vector &inList, const CallSignature *signature, const std::string &realName = ""); + void CollectExraCallSiteInfo(std::vector &values, LLVMValueRef pcOffset, + GateRef frameState); LLVMValueRef GetFunctionFromGlobalValue(LLVMValueRef glue, const CallSignature *signature, LLVMValueRef reloc) const; bool IsInterpreted() const; @@ -366,6 +430,61 @@ private: void SetCallConvAttr(const CallSignature *calleeDescriptor, LLVMValueRef call); bool IsHeapPointerType(LLVMTypeRef valueType); + LLVMTypeRef GetVoidT() const + { + return llvmModule_->GetVoidT(); + } + + LLVMTypeRef GetInt1T() const + { + return llvmModule_->GetInt1T(); + } + + LLVMTypeRef GetInt8T() const + { + return llvmModule_->GetInt8T(); + } + + LLVMTypeRef GetInt16T() const + { + return llvmModule_->GetInt16T(); + } + + LLVMTypeRef GetInt32T() const + { + return llvmModule_->GetInt32T(); + } + + LLVMTypeRef GetInt64T() const + { + return llvmModule_->GetInt64T(); + } + + LLVMTypeRef GetFloatT() const + { + return llvmModule_->GetFloatT(); + } + + LLVMTypeRef GetDoubleT() const + { + return llvmModule_->GetDoubleT(); + } + + LLVMTypeRef GetTaggedPtrT() const + { + return llvmModule_->GetTaggedPtrT(); + } + + LLVMTypeRef GetTaggedHPtrT() const + { + return llvmModule_->GetTaggedHPtrT(); + } + + LLVMTypeRef GetRawPtrT() const + { + return llvmModule_->GetRawPtrT(); + } + private: enum class CallInputs : size_t { DEPEND = 0, @@ -383,6 +502,9 @@ private: return llvmModule_ == nullptr ? nullptr : llvmModule_->GetDIBuilder(); } + unsigned GetPtrAddressSpace(LLVMValueRef v) const; + bool IsLInteger(LLVMValueRef v) const; + bool IsLPointer(LLVMValueRef v) const; LLVMRealPredicate ConvertLLVMPredicateFromFCMP(FCmpCondition cond); LLVMIntPredicate ConvertLLVMPredicateFromICMP(ICmpCondition cond); LLVMValueRef GetGlue(const std::vector &inList); @@ -394,7 +516,7 @@ private: LLVMValueRef GetBuiltinsStubOffset(LLVMValueRef glue); LLVMValueRef GetBaseOffset(GateRef gate, LLVMValueRef glue); CallExceptionKind GetCallExceptionKind(size_t index, OpCode op) const; - void ComputeArgCountAndPCOffset(size_t &actualNumArgs, LLVMValueRef &pcOffset, + void ComputeArgCountAndExtraInfo(size_t &actualNumArgs, LLVMValueRef &pcOffset, GateRef &frameArgs, const std::vector &inList, CallExceptionKind kind); void SaveLexicalEnvOnOptJSFuncFrame(LLVMValueRef value); void SaveJSFuncOnOptJSFuncFrame(LLVMValueRef value); @@ -414,6 +536,15 @@ private: GateRef gate); void SaveDeoptVregInfoWithI64(std::vector &values, int32_t index, size_t curDepth, size_t shift, GateRef gate); + int LookupPredBB(GateRef start, int bbID); + LLVMValueRef GetLValue(const GateRef g) + { + return gate2LValue_[g]; + } + void Bind(const GateRef g, const LLVMValueRef lv) + { + gate2LValue_[g] = lv; + } const CompilationConfig *compCfg_ {nullptr}; const std::vector> *scheduledGates_ {nullptr}; @@ -440,6 +571,7 @@ private: bool enableLog_ {false}; bool isFastCallAot_ {false}; LLVMMetadataRef dFuncMD_ {nullptr}; + bool enableOptInlining_ {false}; }; } // namespace panda::ecmascript::kungfu #endif // ECMASCRIPT_COMPILER_LLVM_IR_BUILDER_H diff --git a/ecmascript/compiler/loop_analysis.cpp b/ecmascript/compiler/loop_analysis.cpp index 814f1c921636721204496604b88a1c75c501f4dd..a50dc1ad612b2f844301a557e9ea42f019f722c2 100644 --- a/ecmascript/compiler/loop_analysis.cpp +++ b/ecmascript/compiler/loop_analysis.cpp @@ -16,7 +16,7 @@ #include "ecmascript/compiler/loop_analysis.h" #include "ecmascript/compiler/loop_peeling.h" #include "ecmascript/compiler/bytecodes.h" -#include "ecmascript/compiler/gate_meta_data.h" +#include "ecmascript/compiler/share_gate_meta_data.h" #include "ecmascript/log_wrapper.h" namespace panda::ecmascript::kungfu { @@ -79,8 +79,12 @@ void LoopAnalysis::CollectLoopBody(LoopInfo* loopInfo) // only calculate loop depth for state & depend edges, // since there is no phi of each value and each loop head. gateToDepth[nex] = ComputeLoopDepth(cur, nex, gateToDepth[cur]); - if (acc_.GetOpCode(nex) == OpCode::STATE_SPLIT) { - gateToDepth[acc_.GetFrameState(nex)] = gateToDepth[nex]; + if (acc_.HasFrameState(nex)) { + auto frameState = acc_.GetFrameState(nex); + if (acc_.GetOpCode(frameState) == OpCode::FRAME_STATE) { + gateToDepth[frameState] = gateToDepth[nex]; + gateToDepth[acc_.GetValueIn(frameState, 1)] = gateToDepth[nex]; + } } // state and depend edge should be visited first. firstList.push(nex); @@ -120,14 +124,17 @@ void LoopAnalysis::UpdateLoopInfo(LoopInfo* loopInfo, GateRef gate, size_t dep) } break; } - case OpCode::STATE_SPLIT: { - loopInfo->size++; - loopInfo->loopBodys.emplace_back(acc_.GetFrameState(gate)); - break; - } default: break; } + if (acc_.HasFrameState(gate)) { + auto frameState = acc_.GetFrameState(gate); + if (acc_.GetOpCode(frameState) == OpCode::FRAME_STATE) { + loopInfo->size += 2; // 2: framestate and framevalues + loopInfo->loopBodys.emplace_back(frameState); + loopInfo->loopBodys.emplace_back(acc_.GetValueIn(frameState, 1)); + } + } loopInfo->loopBodys.emplace_back(gate); } @@ -189,6 +196,5 @@ void LoopAnalysis::LoopExitElimination() break; } } - acc_.EliminateRedundantPhi(); } } // namespace panda::ecmascript::kungfu \ No newline at end of file diff --git a/ecmascript/compiler/loop_peeling.cpp b/ecmascript/compiler/loop_peeling.cpp index d7625444dd30dcf7f204264925d344d3002484dd..d004b43dc2a7b0960d6bcffc2bf73928e0c91831 100644 --- a/ecmascript/compiler/loop_peeling.cpp +++ b/ecmascript/compiler/loop_peeling.cpp @@ -15,7 +15,7 @@ #include "ecmascript/compiler/loop_peeling.h" #include "ecmascript/compiler/circuit.h" -#include "ecmascript/compiler/gate_meta_data.h" +#include "ecmascript/compiler/share_gate_meta_data.h" #include "ecmascript/compiler/number_gate_info.h" #include "ecmascript/compiler/type.h" @@ -76,10 +76,9 @@ void LoopPeeling::Peel() } else if (acc_.GetOpCode(*it) == OpCode::LOOP_EXIT_VALUE) { GateRef value = *it; GateRef copyValue = GetCopy(acc_.GetValueIn(value)); - ASSERT(acc_.GetMachineType(value) == MachineType::I64); - ASSERT(acc_.GetMachineType(copyValue) == MachineType::I64); - GateRef selector = circuit_->NewGate(circuit_->ValueSelector(numIns), MachineType::I64, - {merge, value, copyValue}, GateType::AnyType()); + ASSERT(acc_.GetMachineType(value) == acc_.GetMachineType(copyValue)); + GateRef selector = circuit_->NewGate(circuit_->ValueSelector(numIns), acc_.GetMachineType(value), + {merge, value, copyValue}, acc_.GetGateType(value)); acc_.UpdateAllUses(value, selector); acc_.ReplaceIn(selector, 1, value); // 0: index of exit depend ++it; @@ -90,18 +89,21 @@ void LoopPeeling::Peel() } } } - - auto asyncList = bcBuilder_->GetAsyncRelatedGates(); - ChunkVector list(chunk_); - for (auto gate : asyncList) { - auto copyAsync = TryGetCopy(gate); - if (copyAsync == Circuit::NullGate()) { - list.emplace_back(copyAsync); + if (bcBuilder_) { + auto asyncList = bcBuilder_->GetAsyncRelatedGates(); + ChunkVector list(chunk_); + for (auto gate : asyncList) { + auto copyAsync = TryGetCopy(gate); + if (copyAsync == Circuit::NullGate()) { + list.emplace_back(copyAsync); + } + } + for (auto gate : asyncList) { + bcBuilder_->UpdateAsyncRelatedGate(gate); } } - for (auto gate : asyncList) { - bcBuilder_->UpdateAsyncRelatedGate(gate); - } + + Print(); } void LoopPeeling::SetCopy(GateRef gate) @@ -130,7 +132,10 @@ void LoopPeeling::SetCopy(GateRef gate) GateRef LoopPeeling::GetCopy(GateRef gate) const { - return copies_.at(gate); + if (copies_.count(gate)) { + return copies_.at(gate); + } + return gate; } GateRef LoopPeeling::TryGetCopy(GateRef gate) const @@ -140,4 +145,19 @@ GateRef LoopPeeling::TryGetCopy(GateRef gate) const } return Circuit::NullGate(); } + +void LoopPeeling::Print() const +{ + if (IsLogEnabled()) { + LOG_COMPILER(INFO) << ""; + LOG_COMPILER(INFO) << "\033[34m" + << "====================" + << " After loop peeling " + << "[" << GetMethodName() << "]" + << "====================" + << "\033[0m"; + circuit_->PrintAllGatesWithBytecode(); + LOG_COMPILER(INFO) << "\033[34m" << "========================= End ==========================" << "\033[0m"; + } +} } // namespace panda::ecmascript::kungfu \ No newline at end of file diff --git a/ecmascript/compiler/loop_peeling.h b/ecmascript/compiler/loop_peeling.h index bd886928c6ef338e08d6c651f071dc8fb890d9ce..48a75274576441011b34a7d591065022e791e40e 100644 --- a/ecmascript/compiler/loop_peeling.h +++ b/ecmascript/compiler/loop_peeling.h @@ -20,16 +20,17 @@ #include "ecmascript/compiler/circuit_builder.h" #include "ecmascript/compiler/bytecode_circuit_builder.h" #include "ecmascript/compiler/gate.h" -#include "ecmascript/compiler/gate_meta_data.h" +#include "ecmascript/compiler/share_gate_meta_data.h" #include "ecmascript/compiler/loop_analysis.h" namespace panda::ecmascript::kungfu { class LoopPeeling { public: - LoopPeeling(BytecodeCircuitBuilder* bcBuilder, Circuit *circuit, Chunk* chunk, LoopInfo* loopInfo) - : bcBuilder_(bcBuilder), circuit_(circuit), acc_(circuit), - chunk_(chunk), loopInfo_(loopInfo), copies_(chunk_) {} + LoopPeeling(BytecodeCircuitBuilder* bcBuilder, Circuit *circuit, bool enableLog, + const std::string& name, Chunk* chunk, LoopInfo* loopInfo) + : bcBuilder_(bcBuilder), circuit_(circuit), acc_(circuit), enableLog_(enableLog), + methodName_(name), chunk_(chunk), loopInfo_(loopInfo), copies_(chunk_) {} ~LoopPeeling() = default; void Peel(); @@ -37,9 +38,20 @@ private: void SetCopy(GateRef gate); GateRef GetCopy(GateRef gate) const; GateRef TryGetCopy(GateRef gate) const; + void Print() const; + bool IsLogEnabled() const + { + return enableLog_; + } + std::string GetMethodName() const + { + return methodName_; + } BytecodeCircuitBuilder* bcBuilder_{nullptr}; Circuit* circuit_; GateAccessor acc_; + bool enableLog_{false}; + std::string methodName_; Chunk* chunk_{nullptr}; LoopInfo* loopInfo_{nullptr}; ChunkMap copies_; diff --git a/ecmascript/compiler/mcr_gate_meta_data.cpp b/ecmascript/compiler/mcr_gate_meta_data.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b4ee551e859aaa19374ef4e01eac0652b079a2a4 --- /dev/null +++ b/ecmascript/compiler/mcr_gate_meta_data.cpp @@ -0,0 +1,175 @@ +/* + * 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/compiler/gate.h" +#include "ecmascript/compiler/mcr_gate_meta_data.h" +#include "ecmascript/compiler/gate_meta_data_builder.h" + +namespace panda::ecmascript::kungfu { + +TypedBinOp TypedBinaryMetaData::GetRevCompareOp(TypedBinOp op) +{ + switch (op) { + case TypedBinOp::TYPED_LESS: + return TypedBinOp::TYPED_GREATEREQ; + case TypedBinOp::TYPED_LESSEQ: + return TypedBinOp::TYPED_GREATER; + case TypedBinOp::TYPED_GREATER: + return TypedBinOp::TYPED_LESSEQ; + case TypedBinOp::TYPED_GREATEREQ: + return TypedBinOp::TYPED_LESS; + case TypedBinOp::TYPED_EQ: + return TypedBinOp::TYPED_NOTEQ; + case TypedBinOp::TYPED_NOTEQ: + return TypedBinOp::TYPED_EQ; + default: + UNREACHABLE(); + return op; + } +} + +TypedBinOp TypedBinaryMetaData::GetSwapCompareOp(TypedBinOp op) +{ + switch (op) { + case TypedBinOp::TYPED_LESS: + return TypedBinOp::TYPED_GREATER; + case TypedBinOp::TYPED_LESSEQ: + return TypedBinOp::TYPED_GREATEREQ; + case TypedBinOp::TYPED_GREATER: + return TypedBinOp::TYPED_LESS; + case TypedBinOp::TYPED_GREATEREQ: + return TypedBinOp::TYPED_LESSEQ; + case TypedBinOp::TYPED_EQ: + return TypedBinOp::TYPED_EQ; + case TypedBinOp::TYPED_NOTEQ: + return TypedBinOp::TYPED_NOTEQ; + default: + UNREACHABLE(); + return op; + } +} + +bool GateMetaData::IsTypedOperator() const +{ + return (opcode_ == OpCode::TYPED_BINARY_OP) || (opcode_ == OpCode::TYPE_CONVERT) || + (opcode_ == OpCode::TYPED_UNARY_OP); +} + +bool GateMetaData::IsCheckWithTwoIns() const +{ + return (opcode_ == OpCode::OBJECT_TYPE_CHECK) || + (opcode_ == OpCode::INDEX_CHECK) || + (opcode_ == OpCode::TYPED_CALL_CHECK); +} + +bool GateMetaData::IsCheckWithOneIn() const +{ + return (opcode_ == OpCode::PRIMITIVE_TYPE_CHECK) || + (opcode_ == OpCode::HEAP_OBJECT_CHECK) || + (opcode_ == OpCode::STABLE_ARRAY_CHECK) || + (opcode_ == OpCode::TYPED_ARRAY_CHECK); +} + +std::string GateMetaData::Str(TypedBinOp op) +{ + const std::map strMap = { +#define TYPED_BIN_OP_NAME_MAP(OP) { TypedBinOp::OP, #OP }, + TYPED_BIN_OP_LIST(TYPED_BIN_OP_NAME_MAP) +#undef TYPED_BIN_OP_NAME_MAP + }; + if (strMap.count(op) > 0) { + return strMap.at(op); + } + return "UNKNOW"; +} + +std::string GateMetaData::Str(TypedUnOp op) +{ + const std::map strMap = { +#define TYPED_UN_OP_NAME_MAP(OP) { TypedUnOp::OP, #OP }, + TYPED_UN_OP_LIST(TYPED_UN_OP_NAME_MAP) +#undef TYPED_UN_OP_NAME_MAP + }; + if (strMap.count(op) > 0) { + return strMap.at(op); + } + return "UNKNOW"; +} + +std::string GateMetaData::Str(TypedJumpOp op) +{ + const std::map strMap = { +#define TYPED_JUMP_OP_NAME_MAP(OP) { TypedJumpOp::OP, #OP }, + TYPED_JUMP_OP_LIST(TYPED_JUMP_OP_NAME_MAP) +#undef TYPED_JUMP_OP_NAME_MAP + }; + if (strMap.count(op) > 0) { + return strMap.at(op); + } + return "UNKNOW"; +} + +std::string GateMetaData::Str(TypedLoadOp op) +{ + const std::map strMap = { +#define TYPED_LOAD_OP_NAME_MAP(OP) { TypedLoadOp::OP, #OP }, + TYPED_LOAD_OP_LIST(TYPED_LOAD_OP_NAME_MAP) +#undef TYPED_LOAD_OP_NAME_MAP + }; + if (strMap.count(op) > 0) { + return strMap.at(op); + } + return "UNKNOW"; +} + +std::string GateMetaData::Str(TypedStoreOp op) +{ + const std::map strMap = { +#define TYPED_STORE_OP_NAME_MAP(OP) { TypedStoreOp::OP, #OP }, + TYPED_STORE_OP_LIST(TYPED_STORE_OP_NAME_MAP) +#undef TYPED_STORE_OP_NAME_MAP + }; + if (strMap.count(op) > 0) { + return strMap.at(op); + } + return "UNKNOW"; +} + +std::string GateMetaData::Str(TypedCallTargetCheckOp op) +{ + const std::map strMap = { +#define TYPED_CALL_TARGET_CHECK_OP_NAME_MAP(OP) { TypedCallTargetCheckOp::OP, #OP }, + TYPED_CALL_TARGET_CHECK_OP_LIST(TYPED_CALL_TARGET_CHECK_OP_NAME_MAP) +#undef TYPED_CALL_TARGET_CHECK_OP_NAME_MAP + }; + if (strMap.count(op) > 0) { + return strMap.at(op); + } + return "UNKNOW"; +} + +std::string GateMetaData::Str(ValueType type) +{ + const std::map strMap = { +#define VALUE_TYPE_NAME_MAP(TYPE) { ValueType::TYPE, #TYPE }, + VALUE_TYPE_LIST(VALUE_TYPE_NAME_MAP) +#undef VALUE_TYPE_NAME_MAP + }; + if (strMap.count(type) > 0) { + return strMap.at(type); + } + return "UNKNOW"; +} +} \ No newline at end of file diff --git a/ecmascript/compiler/mcr_gate_meta_data.h b/ecmascript/compiler/mcr_gate_meta_data.h new file mode 100644 index 0000000000000000000000000000000000000000..76c8e4a0dce2135b612b748607c7124124398b64 --- /dev/null +++ b/ecmascript/compiler/mcr_gate_meta_data.h @@ -0,0 +1,381 @@ +/* + * 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_COMPILER_MCR_GATE_META_DATA_H +#define ECMASCRIPT_COMPILER_MCR_GATE_META_DATA_H + +#include + +#include "ecmascript/compiler/bytecodes.h" +#include "ecmascript/compiler/type.h" +#include "ecmascript/mem/chunk.h" +#include "ecmascript/mem/chunk_containers.h" + +#include "ecmascript/elements.h" +#include "ecmascript/pgo_profiler/types/pgo_profiler_type.h" +#include "libpandabase/macros.h" + +#include "ecmascript/compiler/share_gate_meta_data.h" + +namespace panda::ecmascript::kungfu { + +#define TYPED_BIN_OP_LIST(V) \ + V(TYPED_ADD) \ + V(TYPED_SUB) \ + V(TYPED_MUL) \ + V(TYPED_DIV) \ + V(TYPED_MOD) \ + V(TYPED_LESS) \ + V(TYPED_LESSEQ) \ + V(TYPED_GREATER) \ + V(TYPED_GREATEREQ) \ + V(TYPED_EQ) \ + V(TYPED_NOTEQ) \ + V(TYPED_STRICTEQ) \ + V(TYPED_SHL) \ + V(TYPED_SHR) \ + V(TYPED_ASHR) \ + V(TYPED_AND) \ + V(TYPED_OR) \ + V(TYPED_XOR) \ + V(TYPED_EXP) + +#define TYPED_UN_OP_LIST(V) \ + V(TYPED_NEG) \ + V(TYPED_NOT) \ + V(TYPED_INC) \ + V(TYPED_DEC) \ + V(TYPED_ISFALSE) \ + V(TYPED_ISTRUE) + +#define TYPED_JUMP_OP_LIST(V) \ + V(TYPED_JEQZ) \ + V(TYPED_JNEZ) + +#define TYPED_LOAD_OP_LIST(V) \ + V(ARRAY_LOAD_INT_ELEMENT) \ + V(ARRAY_LOAD_DOUBLE_ELEMENT) \ + V(ARRAY_LOAD_OBJECT_ELEMENT) \ + V(ARRAY_LOAD_TAGGED_ELEMENT) \ + V(ARRAY_LOAD_HOLE_TAGGED_ELEMENT) \ + V(INT8ARRAY_LOAD_ELEMENT) \ + V(UINT8ARRAY_LOAD_ELEMENT) \ + V(UINT8CLAMPEDARRAY_LOAD_ELEMENT) \ + V(INT16ARRAY_LOAD_ELEMENT) \ + V(UINT16ARRAY_LOAD_ELEMENT) \ + V(INT32ARRAY_LOAD_ELEMENT) \ + V(UINT32ARRAY_LOAD_ELEMENT) \ + V(FLOAT32ARRAY_LOAD_ELEMENT) \ + V(FLOAT64ARRAY_LOAD_ELEMENT) \ + V(STRING_LOAD_ELEMENT) + +#define TYPED_STORE_OP_LIST(V) \ + V(ARRAY_STORE_ELEMENT) \ + V(INT8ARRAY_STORE_ELEMENT) \ + V(UINT8ARRAY_STORE_ELEMENT) \ + V(UINT8CLAMPEDARRAY_STORE_ELEMENT) \ + V(INT16ARRAY_STORE_ELEMENT) \ + V(UINT16ARRAY_STORE_ELEMENT) \ + V(INT32ARRAY_STORE_ELEMENT) \ + V(UINT32ARRAY_STORE_ELEMENT) \ + V(FLOAT32ARRAY_STORE_ELEMENT) \ + V(FLOAT64ARRAY_STORE_ELEMENT) + +#define TYPED_CALL_TARGET_CHECK_OP_LIST(V) \ + V(JSCALL_IMMEDIATE_AFTER_FUNC_DEF) \ + V(JSCALL) \ + V(JSCALL_FAST) \ + V(JSCALLTHIS) \ + V(JSCALLTHIS_FAST) \ + V(JSCALLTHIS_NOGC) \ + V(JSCALLTHIS_FAST_NOGC) + +enum class TypedBinOp : uint8_t { +#define DECLARE_TYPED_BIN_OP(OP) OP, + TYPED_BIN_OP_LIST(DECLARE_TYPED_BIN_OP) +#undef DECLARE_TYPED_BIN_OP +}; + +enum class TypedUnOp : uint8_t { +#define DECLARE_TYPED_UN_OP(OP) OP, + TYPED_UN_OP_LIST(DECLARE_TYPED_UN_OP) +#undef DECLARE_TYPED_UN_OP +}; + +enum class TypedJumpOp : uint8_t { +#define DECLARE_TYPED_JUMP_OP(OP) OP, + TYPED_JUMP_OP_LIST(DECLARE_TYPED_JUMP_OP) +#undef DECLARE_TYPED_JUMP_OP +}; + +enum class TypedLoadOp : uint8_t { +#define DECLARE_TYPED_LOAD_OP(OP) OP, + TYPED_LOAD_OP_LIST(DECLARE_TYPED_LOAD_OP) +#undef DECLARE_TYPED_LOAD_OP + TYPED_ARRAY_FIRST = INT8ARRAY_LOAD_ELEMENT, + TYPED_ARRAY_LAST = FLOAT64ARRAY_LOAD_ELEMENT, +}; + +enum class TypedStoreOp : uint8_t { +#define DECLARE_TYPED_STORE_OP(OP) OP, + TYPED_STORE_OP_LIST(DECLARE_TYPED_STORE_OP) +#undef DECLARE_TYPED_STORE_OP + TYPED_ARRAY_FIRST = INT8ARRAY_STORE_ELEMENT, + TYPED_ARRAY_LAST = FLOAT64ARRAY_STORE_ELEMENT, +}; + +enum class TypedCallTargetCheckOp : uint8_t { +#define DECLARE_TYPED_CALL_TARGET_CHECK_OP(OP) OP, + TYPED_CALL_TARGET_CHECK_OP_LIST(DECLARE_TYPED_CALL_TARGET_CHECK_OP) +#undef DECLARE_TYPED_CALL_TARGET_CHECK_OP +}; + +enum class BranchKind : uint8_t { + NORMAL_BRANCH = 0, + TRUE_BRANCH, + FALSE_BRANCH, + STRONG_TRUE_BRANCH, + STRONG_FALSE_BRANCH, +}; + +enum class TypedOpKind : uint8_t { + TYPED_BIN_OP, + TYPED_CALL_TARGET_CHECK_OP, + TYPED_UN_OP, + TYPED_JUMP_OP, + TYPED_STORE_OP, + TYPED_LOAD_OP, +}; + +enum class MemoryType : uint8_t { + ELEMENT_TYPE = 0, +}; + +class TypedCallMetaData : public OneParameterMetaData { +public: + TypedCallMetaData(OpCode opcode, GateFlags flags, uint32_t statesIn, + uint16_t dependsIn, uint32_t valuesIn, uint64_t value, bool noGC) + : OneParameterMetaData(opcode, flags, statesIn, dependsIn, valuesIn, value), + noGC_(noGC) + { + SetKind(GateMetaData::Kind::TYPED_CALL); + } + + static const TypedCallMetaData* Cast(const GateMetaData* meta) + { + meta->AssertKind(GateMetaData::Kind::TYPED_CALL); + return static_cast(meta); + } + + bool IsNoGC() const + { + return noGC_; + } +private: + bool noGC_; +}; + +class TypedCallTargetCheckMetaData : public OneParameterMetaData { +public: + TypedCallTargetCheckMetaData(uint32_t valuesIn, uint64_t value, TypedCallTargetCheckOp checkOp) + : OneParameterMetaData(OpCode::TYPED_CALLTARGETCHECK_OP, GateFlags::CHECKABLE, 1, 1, valuesIn, value), + checkOp_(checkOp) + { + SetKind(GateMetaData::Kind::TYPED_CALLTARGETCHECK_OP); + } + + static const TypedCallTargetCheckMetaData* Cast(const GateMetaData* meta) + { + meta->AssertKind(GateMetaData::Kind::TYPED_CALLTARGETCHECK_OP); + return static_cast(meta); + } + + TypedCallTargetCheckOp GetTypedCallTargetCheckOp() const + { + return checkOp_; + } +private: + TypedCallTargetCheckOp checkOp_; +}; + + +class TypedBinaryMetaData : public OneParameterMetaData { +public: + TypedBinaryMetaData(uint64_t value, TypedBinOp binOp, PGOSampleType type) + : OneParameterMetaData(OpCode::TYPED_BINARY_OP, GateFlags::NO_WRITE, 1, 1, 2, value), // 2: valuesIn + binOp_(binOp), type_(type) + { + SetKind(GateMetaData::Kind::TYPED_BINARY_OP); + } + + static const TypedBinaryMetaData* Cast(const GateMetaData* meta) + { + meta->AssertKind(GateMetaData::Kind::TYPED_BINARY_OP); + return static_cast(meta); + } + + TypedBinOp GetTypedBinaryOp() const + { + return binOp_; + } + + PGOSampleType GetType() const + { + return type_; + } + + static TypedBinOp GetRevCompareOp(TypedBinOp op); + static TypedBinOp GetSwapCompareOp(TypedBinOp op); +private: + TypedBinOp binOp_; + PGOSampleType type_; +}; + +class TypedUnaryAccessor { +public: + // type bits shift + static constexpr int OPRAND_TYPE_BITS = 32; + explicit TypedUnaryAccessor(uint64_t value) : bitField_(value) {} + + GateType GetTypeValue() const + { + return GateType(TypedValueBits::Get(bitField_)); + } + + TypedUnOp GetTypedUnOp() const + { + return TypedUnOpBits::Get(bitField_); + } + + static uint64_t ToValue(GateType typeValue, TypedUnOp unaryOp) + { + return TypedValueBits::Encode(typeValue.Value()) + | TypedUnOpBits::Encode(unaryOp); + } + +private: + using TypedValueBits = panda::BitField; + using TypedUnOpBits = TypedValueBits::NextField; + + uint64_t bitField_; +}; + +class TypedBinaryAccessor { +public: + // type bits shift + static constexpr int OPRAND_TYPE_BITS = 32; + explicit TypedBinaryAccessor(uint64_t value) : bitField_(value) {} + explicit TypedBinaryAccessor(GateType gate, TypedBinOp binOp) + { + bitField_ = TypedValueBits::Encode(gate.Value()) | TypedBinOpBits::Encode(binOp); + } + + GateType GetTypeValue() const + { + return GateType(TypedValueBits::Get(bitField_)); + } + + TypedBinOp GetTypedBinOp() const + { + return TypedBinOpBits::Get(bitField_); + } + + uint64_t ToValue() const + { + return bitField_; + } + +private: + using TypedValueBits = panda::BitField; + using TypedBinOpBits = TypedValueBits::NextField; + + uint64_t bitField_; +}; + +class BranchAccessor { +public: + // type bits shift + static constexpr int OPRAND_TYPE_BITS = 32; + explicit BranchAccessor(uint64_t value) : bitField_(value) {} + + int32_t GetTrueWeight() const + { + return TrueWeightBits::Get(bitField_); + } + + int32_t GetFalseWeight() const + { + return FalseWeightBits::Get(bitField_); + } + + static uint64_t ToValue(uint32_t trueWeight, uint32_t falseWeight) + { + return TrueWeightBits::Encode(trueWeight) + | FalseWeightBits::Encode(falseWeight); + } +private: + using TrueWeightBits = panda::BitField; + using FalseWeightBits = TrueWeightBits::NextField; + + uint64_t bitField_; +}; + +class TypedJumpAccessor { +public: + // type bits shift + static constexpr int OPRAND_TYPE_BITS = 32; + static constexpr int JUMP_OP_BITS = 8; + explicit TypedJumpAccessor(uint64_t value) : bitField_(value) {} + + GateType GetTypeValue() const + { + return GateType(TypedValueBits::Get(bitField_)); + } + + TypedJumpOp GetTypedJumpOp() const + { + return TypedJumpOpBits::Get(bitField_); + } + + uint32_t GetTrueWeight() const + { + return TrueWeightBits::Get(bitField_); + } + + uint32_t GetFalseWeight() const + { + return FalseWeightBits::Get(bitField_); + } + + static uint64_t ToValue(GateType typeValue, TypedJumpOp jumpOp, uint32_t weight) + { + return TypedValueBits::Encode(typeValue.Value()) + | TypedJumpOpBits::Encode(jumpOp) + | WeightBits::Encode(weight); + } + +private: + using TypedValueBits = panda::BitField; + using TypedJumpOpBits = TypedValueBits::NextField; + using WeightBits = TypedJumpOpBits::NextField; + using FalseWeightBits = TypedJumpOpBits::NextField; + using TrueWeightBits = FalseWeightBits::NextField; + + uint64_t bitField_; +}; + +} + +#endif // ECMASCRIPT_COMPILER_MCR_GATE_META_DATA_H diff --git a/ecmascript/compiler/mcr_opcodes.h b/ecmascript/compiler/mcr_opcodes.h new file mode 100644 index 0000000000000000000000000000000000000000..a91013e58a21448a17412f88dad3029ccf8599aa --- /dev/null +++ b/ecmascript/compiler/mcr_opcodes.h @@ -0,0 +1,102 @@ +/* + * 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_COMPILER_MCR_OPCODE_H +#define ECMASCRIPT_COMPILER_MCR_OPCODE_H + +namespace panda::ecmascript::kungfu { + +#define MCR_BINARY_GATE_META_DATA_CACHE_LIST(V) \ + V(Int32CheckRightIsZero, INT32_CHECK_RIGHT_IS_ZERO, GateFlags::CHECKABLE, 1, 1, 1) \ + V(Float64CheckRightIsZero, FLOAT64_CHECK_RIGHT_IS_ZERO, GateFlags::CHECKABLE, 1, 1, 1) \ + V(ValueCheckNegOverflow, VALUE_CHECK_NEG_OVERFLOW, GateFlags::CHECKABLE, 1, 1, 1) \ + V(OverflowCheck, OVERFLOW_CHECK, GateFlags::CHECKABLE, 1, 1, 1) \ + V(Int32UnsignedUpperBoundCheck, INT32_UNSIGNED_UPPER_BOUND_CHECK, GateFlags::CHECKABLE, 1, 1, 2) \ + V(Int32DivWithCheck, INT32_DIV_WITH_CHECK, GateFlags::CHECKABLE, 1, 1, 2) \ + V(LexVarIsHoleCheck, LEX_VAR_IS_HOLE_CHECK, GateFlags::CHECKABLE, 1, 1, 1) \ + V(StringEqual, STRING_EQUAL, GateFlags::NO_WRITE, 1, 1, 2) + +#define MCR_IMMUTABLE_META_DATA_CACHE_LIST(V) \ + V(ArrayGuardianCheck, ARRAY_GUARDIAN_CHECK, GateFlags::CHECKABLE, 1, 1, 0) \ + V(COWArrayCheck, COW_ARRAY_CHECK, GateFlags::CHECKABLE, 1, 1, 1) \ + V(ConvertHoleAsUndefined, CONVERT_HOLE_AS_UNDEFINED, GateFlags::NO_WRITE, 1, 1, 1) \ + V(EcmaStringCheck, ECMA_STRING_CHECK, GateFlags::CHECKABLE, 1, 1, 1) \ + V(FinishAllocate, FINISH_ALLOCATE, GateFlags::NONE_FLAG, 0, 1, 0) \ + V(FlattenTreeStringCheck, FLATTEN_TREE_STRING_CHECK, GateFlags::CHECKABLE, 1, 1, 1) \ + V(HeapObjectCheck, HEAP_OBJECT_CHECK, GateFlags::CHECKABLE, 1, 1, 1) \ + V(LoadGetter, LOAD_GETTER, GateFlags::NO_WRITE, 0, 1, 2) \ + V(LoadSetter, LOAD_SETTER, GateFlags::NO_WRITE, 0, 1, 2) \ + V(LoadArrayLength, LOAD_ARRAY_LENGTH, GateFlags::NO_WRITE, 1, 1, 1) \ + V(LoadStringLength, LOAD_STRING_LENGTH, GateFlags::NO_WRITE, 1, 1, 1) \ + V(StartAllocate, START_ALLOCATE, GateFlags::NONE_FLAG, 0, 1, 0) \ + V(StoreProperty, STORE_PROPERTY, GateFlags::NONE_FLAG, 1, 1, 3) \ + V(StorePropertyNoBarrier, STORE_PROPERTY_NO_BARRIER, GateFlags::NONE_FLAG, 1, 1, 3) \ + V(TypedCallCheck, TYPED_CALL_CHECK, GateFlags::CHECKABLE, 1, 1, 3) \ + V(TypedNewAllocateThis, TYPED_NEW_ALLOCATE_THIS, GateFlags::CHECKABLE, 1, 1, 2) \ + V(TypedSuperAllocateThis, TYPED_SUPER_ALLOCATE_THIS, GateFlags::CHECKABLE, 1, 1, 2) \ + MCR_BINARY_GATE_META_DATA_CACHE_LIST(V) + +#define MCR_GATE_META_DATA_LIST_WITH_PC_OFFSET(V) \ + V(TypedCallBuiltin, TYPED_CALL_BUILTIN, GateFlags::CHECKABLE, 1, 1, value) + +#define MCR_GATE_META_DATA_LIST_FOR_CALL(V) \ + V(TypedCall, TYPEDCALL, GateFlags::HAS_FRAME_STATE, 1, 1, value) \ + V(TypedFastCall, TYPEDFASTCALL, GateFlags::HAS_FRAME_STATE, 1, 1, value) + +#define MCR_GATE_META_DATA_LIST_WITH_VALUE(V) \ + V(LoadConstOffset, LOAD_CONST_OFFSET, GateFlags::NO_WRITE, 0, 1, 1) \ + V(StoreConstOffset, STORE_CONST_OFFSET, GateFlags::NONE_FLAG, 1, 1, 2) \ + V(LoadElement, LOAD_ELEMENT, GateFlags::NO_WRITE, 1, 1, 2) \ + V(StoreElement, STORE_ELEMENT, GateFlags::NONE_FLAG, 1, 1, 3) \ + V(StoreMemory, STORE_MEMORY, GateFlags::NONE_FLAG, 1, 1, 3) \ + V(ObjectTypeCompare, OBJECT_TYPE_COMPARE, GateFlags::CHECKABLE, 1, 1, 2) \ + V(ObjectTypeCheck, OBJECT_TYPE_CHECK, GateFlags::CHECKABLE, 1, 1, 2) \ + V(StableArrayCheck, STABLE_ARRAY_CHECK, GateFlags::CHECKABLE, 1, 1, 1) \ + V(RangeGuard, RANGE_GUARD, GateFlags::NO_WRITE, 1, 1, 1) \ + V(GetGlobalEnvObj, GET_GLOBAL_ENV_OBJ, GateFlags::NO_WRITE, 0, 1, 1) \ + V(GetGlobalEnvObjHClass, GET_GLOBAL_ENV_OBJ_HCLASS, GateFlags::NO_WRITE, 0, 1, 1) \ + V(GetGlobalConstantValue, GET_GLOBAL_CONSTANT_VALUE, GateFlags::NO_WRITE, 0, 1, 0) \ + V(HClassStableArrayCheck, HCLASS_STABLE_ARRAY_CHECK, GateFlags::CHECKABLE, 1, 1, 1) \ + V(HeapAlloc, HEAP_ALLOC, GateFlags::NONE_FLAG, 1, 1, 1) \ + V(RangeCheckPredicate, RANGE_CHECK_PREDICATE, GateFlags::CHECKABLE, 1, 1, 2) + +#define MCR_GATE_META_DATA_LIST_WITH_BOOL(V) \ + V(LoadProperty, LOAD_PROPERTY, GateFlags::NO_WRITE, 1, 1, 2) + +#define MCR_GATE_META_DATA_LIST_WITH_GATE_TYPE(V) \ + V(PrimitiveTypeCheck, PRIMITIVE_TYPE_CHECK, GateFlags::CHECKABLE, 1, 1, 1) \ + V(TypedArrayCheck, TYPED_ARRAY_CHECK, GateFlags::CHECKABLE, 1, 1, 1) \ + V(LoadTypedArrayLength, LOAD_TYPED_ARRAY_LENGTH, GateFlags::NO_WRITE, 1, 1, 1) \ + V(IndexCheck, INDEX_CHECK, GateFlags::CHECKABLE, 1, 1, 2) \ + V(TypedUnaryOp, TYPED_UNARY_OP, GateFlags::NO_WRITE, 1, 1, 1) \ + V(TypedConditionJump, TYPED_CONDITION_JUMP, GateFlags::NO_WRITE, 1, 1, 1) \ + V(TypedConvert, TYPE_CONVERT, GateFlags::NO_WRITE, 1, 1, 1) \ + V(CheckAndConvert, CHECK_AND_CONVERT, GateFlags::CHECKABLE, 1, 1, 1) \ + V(Convert, CONVERT, GateFlags::NONE_FLAG, 0, 0, 1) \ + V(JSInlineTargetTypeCheck, JSINLINETARGET_TYPE_CHECK, GateFlags::CHECKABLE, 1, 1, 2) \ + V(InlineAccessorCheck, INLINE_ACCESSOR_CHECK, GateFlags::CHECKABLE, 1, 1, 2) \ + V(TypeOfCheck, TYPE_OF_CHECK, GateFlags::CHECKABLE, 1, 1, 1) \ + V(TypeOf, TYPE_OF, GateFlags::NO_WRITE, 1, 1, 0) + +#define MCR_GATE_META_DATA_LIST_WITH_ONE_PARAMETER(V) \ + MCR_GATE_META_DATA_LIST_WITH_VALUE(V) \ + MCR_GATE_META_DATA_LIST_WITH_GATE_TYPE(V) + +#define MCR_GATE_OPCODE_LIST(V) \ + V(TYPED_BINARY_OP) \ + V(TYPED_CALLTARGETCHECK_OP) + +} +#endif // ECMASCRIPT_COMPILER_MCR_OPCODE_H \ No newline at end of file diff --git a/ecmascript/compiler/new_object_stub_builder.cpp b/ecmascript/compiler/new_object_stub_builder.cpp index 907a55d524fe803639e31a246cc342e6b6892eba..87564e8f953f8c54aa541ccb977bd1658ce32cf1 100644 --- a/ecmascript/compiler/new_object_stub_builder.cpp +++ b/ecmascript/compiler/new_object_stub_builder.cpp @@ -16,6 +16,7 @@ #include "ecmascript/compiler/new_object_stub_builder.h" #include "ecmascript/compiler/stub_builder-inl.h" +#include "ecmascript/ecma_string.h" #include "ecmascript/global_env.h" #include "ecmascript/global_env_constants.h" #include "ecmascript/js_arguments.h" @@ -23,6 +24,10 @@ #include "ecmascript/js_thread.h" #include "ecmascript/lexical_env.h" #include "ecmascript/mem/mem.h" +#include "ecmascript/js_map_iterator.h" +#include "ecmascript/js_set_iterator.h" +#include "ecmascript/js_set.h" +#include "ecmascript/js_map.h" namespace panda::ecmascript::kungfu { void NewObjectStubBuilder::NewLexicalEnv(Variable *result, Label *exit, GateRef numSlots, GateRef parent) @@ -269,7 +274,7 @@ void NewObjectStubBuilder::NewArgumentsObj(Variable *result, Label *exit, } void NewObjectStubBuilder::NewJSArrayLiteral(Variable *result, Label *exit, RegionSpaceFlag spaceType, GateRef obj, - GateRef hclass, bool isEmptyArray) + GateRef hclass, GateRef trackInfo, bool isEmptyArray) { auto env = GetEnvironment(); Label initializeArray(env); @@ -286,10 +291,11 @@ void NewObjectStubBuilder::NewJSArrayLiteral(Variable *result, Label *exit, Regi GateRef propertiesOffset = IntPtr(JSObject::PROPERTIES_OFFSET); GateRef elementsOffset = IntPtr(JSObject::ELEMENTS_OFFSET); GateRef lengthOffset = IntPtr(JSArray::LENGTH_OFFSET); + GateRef trackInfoOffset = IntPtr(JSArray::TRACK_INFO_OFFSET); if (isEmptyArray) { Store(VariableType::JS_POINTER(), glue_, result->ReadVariable(), propertiesOffset, obj); Store(VariableType::JS_POINTER(), glue_, result->ReadVariable(), elementsOffset, obj); - Store(VariableType::JS_ANY(), glue_, result->ReadVariable(), lengthOffset, IntToTaggedInt(Int32(0))); + Store(VariableType::INT32(), glue_, result->ReadVariable(), lengthOffset, Int32(0)); } else { auto newProperties = Load(VariableType::JS_POINTER(), obj, propertiesOffset); Store(VariableType::JS_POINTER(), glue_, result->ReadVariable(), propertiesOffset, newProperties); @@ -297,9 +303,10 @@ void NewObjectStubBuilder::NewJSArrayLiteral(Variable *result, Label *exit, Regi auto newElements = Load(VariableType::JS_POINTER(), obj, elementsOffset); Store(VariableType::JS_POINTER(), glue_, result->ReadVariable(), elementsOffset, newElements); - GateRef arrayLength = Load(VariableType::JS_ANY(), obj, lengthOffset); - Store(VariableType::JS_ANY(), glue_, result->ReadVariable(), lengthOffset, arrayLength); + GateRef arrayLength = Load(VariableType::INT32(), obj, lengthOffset); + Store(VariableType::INT32(), glue_, result->ReadVariable(), lengthOffset, arrayLength); } + Store(VariableType::INT64(), glue_, result->ReadVariable(), trackInfoOffset, trackInfo); auto accessor = GetGlobalConstantValue(VariableType::JS_POINTER(), glue_, ConstantIndex::ARRAY_LENGTH_ACCESSOR); SetPropertyInlinedProps(glue_, result->ReadVariable(), hclass, accessor, @@ -364,6 +371,31 @@ void NewObjectStubBuilder::AllocateInYoung(Variable *result, Label *exit) } } +GateRef NewObjectStubBuilder::NewTrackInfo(GateRef glue, GateRef cachedHClass, GateRef cachedFunc) +{ + auto env = GetEnvironment(); + Label entry(env); + env->SubCfgEntry(&entry); + + Label initialize(env); + DEFVARIABLE(result, VariableType::JS_ANY(), Undefined()); + auto hclass = GetGlobalConstantValue(VariableType::JS_POINTER(), glue, ConstantIndex::TRACK_INFO_CLASS_INDEX); + GateRef size = GetObjectSizeFromHClass(hclass); + SetParameters(glue, size); + HeapAlloc(&result, &initialize, RegionSpaceFlag::IN_YOUNG_SPACE); + Bind(&initialize); + Store(VariableType::JS_POINTER(), glue_, *result, IntPtr(0), hclass); + GateRef cachedHClassOffset = IntPtr(TrackInfo::CACHED_HCLASS_OFFSET); + Store(VariableType::JS_POINTER(), glue, *result, cachedHClassOffset, cachedHClass); + GateRef cachedFuncOffset = IntPtr(TrackInfo::CACHED_FUNC_OFFSET); + Store(VariableType::JS_POINTER(), glue, *result, cachedFuncOffset, cachedFunc); + auto elementsKind = GetElementsKindFromHClass(cachedHClass); + SetElementsKindToTrackInfo(glue, *result, elementsKind); + auto ret = *result; + env->SubCfgExit(); + return ret; +} + void NewObjectStubBuilder::InitializeWithSpeicalValue(Label *exit, GateRef object, GateRef value, GateRef start, GateRef end) { @@ -421,6 +453,49 @@ void NewObjectStubBuilder::AllocLineStringObject(Variable *result, Label *exit, Jump(exit); } +void NewObjectStubBuilder::AllocSlicedStringObject(Variable *result, Label *exit, GateRef from, GateRef length, + FlatStringStubBuilder *flatString) +{ + auto env = GetEnvironment(); + + size_ = AlignUp(IntPtr(SlicedString::SIZE), IntPtr(static_cast(MemAlignment::MEM_ALIGN_OBJECT))); + Label afterAllocate(env); + AllocateInYoung(result, &afterAllocate); + + Bind(&afterAllocate); + GateRef stringClass = GetGlobalConstantValue(VariableType::JS_POINTER(), glue_, + ConstantIndex::SLICED_STRING_CLASS_INDEX); + StoreHClass(glue_, result->ReadVariable(), stringClass); + GateRef mixLength = Load(VariableType::INT32(), flatString->GetFlatString(), IntPtr(EcmaString::MIX_LENGTH_OFFSET)); + GateRef isCompressed = Int32And(Int32(EcmaString::STRING_COMPRESSED_BIT), mixLength); + SetLength(glue_, result->ReadVariable(), length, isCompressed); + SetRawHashcode(glue_, result->ReadVariable(), Int32(0)); + BuiltinsStringStubBuilder builtinsStringStubBuilder(this); + builtinsStringStubBuilder.StoreParent(glue_, result->ReadVariable(), flatString->GetFlatString()); + builtinsStringStubBuilder.StoreStartIndex(glue_, result->ReadVariable(), + Int32Add(from, flatString->GetStartIndex())); + Jump(exit); +} + +void NewObjectStubBuilder::AllocTreeStringObject(Variable *result, Label *exit, GateRef first, GateRef second, + GateRef length, bool compressed) +{ + auto env = GetEnvironment(); + + size_ = AlignUp(IntPtr(TreeEcmaString::SIZE), IntPtr(static_cast(MemAlignment::MEM_ALIGN_OBJECT))); + Label afterAllocate(env); + AllocateInYoung(result, &afterAllocate); + + Bind(&afterAllocate); + GateRef stringClass = GetGlobalConstantValue(VariableType::JS_POINTER(), glue_, + ConstantIndex::TREE_STRING_CLASS_INDEX); + StoreHClass(glue_, result->ReadVariable(), stringClass); + SetLength(glue_, result->ReadVariable(), length, compressed); + SetRawHashcode(glue_, result->ReadVariable(), Int32(0)); + Store(VariableType::JS_POINTER(), glue_, result->ReadVariable(), IntPtr(TreeEcmaString::FIRST_OFFSET), first); + Store(VariableType::JS_POINTER(), glue_, result->ReadVariable(), IntPtr(TreeEcmaString::SECOND_OFFSET), second); + Jump(exit); +} GateRef NewObjectStubBuilder::FastNewThisObject(GateRef glue, GateRef ctor) { @@ -499,7 +574,89 @@ GateRef NewObjectStubBuilder::NewThisObjectChecked(GateRef glue, GateRef ctor) return ret; } -GateRef NewObjectStubBuilder::CreateEmptyArray(GateRef glue) +GateRef NewObjectStubBuilder::LoadTrackInfo(GateRef glue, GateRef jsFunc, GateRef pc, GateRef profileTypeInfo, + GateRef slotId, GateRef arrayLiteral, ProfileOperation callback) +{ + auto env = GetEnvironment(); + Label entry(env); + env->SubCfgEntry(&entry); + Label exit(env); + DEFVARIABLE(ret, VariableType::JS_POINTER(), Undefined()); + + Label uninitialized(env); + Label fastpath(env); + GateRef slotValue = GetValueFromTaggedArray(profileTypeInfo, slotId); + Branch(TaggedIsHeapObject(slotValue), &fastpath, &uninitialized); + Bind(&fastpath); + { + ret = slotValue; + Jump(&exit); + } + Bind(&uninitialized); + { + auto hclass = LoadArrayHClassSlowPath(glue, jsFunc, pc, arrayLiteral, callback); + ret = NewTrackInfo(glue, hclass, jsFunc); + SetValueToTaggedArray(VariableType::JS_POINTER(), glue, profileTypeInfo, slotId, *ret); + callback.TryPreDump(); + Jump(&exit); + } + Bind(&exit); + auto result = *ret; + env->SubCfgExit(); + return result; +} + +GateRef NewObjectStubBuilder::LoadArrayHClassSlowPath( + GateRef glue, GateRef jsFunc, GateRef pc, GateRef arrayLiteral, ProfileOperation callback) +{ + auto env = GetEnvironment(); + Label entry(env); + env->SubCfgEntry(&entry); + Label exit(env); + Label originLoad(env); + + DEFVARIABLE(ret, VariableType::JS_POINTER(), Undefined()); + + auto hcIndexInfos = LoadHCIndexInfosFromConstPool(jsFunc); + auto indexInfosLength = GetLengthOfTaggedArray(hcIndexInfos); + Label aotLoad(env); + Branch(Int32Equal(indexInfosLength, Int32(0)), &originLoad, &aotLoad); + Bind(&aotLoad); + { + auto pfAddr = LoadPfHeaderFromConstPool(jsFunc); + GateRef traceId = TruncPtrToInt32(PtrSub(IntPtr(pc), pfAddr)); + GateRef hcIndex = LoadHCIndexFromConstPool(hcIndexInfos, indexInfosLength, traceId, &originLoad); + GateRef gConstAddr = Load(VariableType::JS_ANY(), glue, + IntPtr(JSThread::GlueData::GetGlobalConstOffset(env->Is32Bit()))); + ret = Load(VariableType::JS_POINTER(), gConstAddr, hcIndex); + Jump(&exit); + } + Bind(&originLoad); + { + // emptyarray + if (arrayLiteral == Circuit::NullGate()) { + if (callback.IsEmpty()) { + GateRef glueGlobalEnvOffset = IntPtr(JSThread::GlueData::GetGlueGlobalEnvOffset(env->Is32Bit())); + GateRef glueGlobalEnv = Load(VariableType::NATIVE_POINTER(), glue, glueGlobalEnvOffset); + auto arrayFunc = + GetGlobalEnvValue(VariableType::JS_ANY(), glueGlobalEnv, GlobalEnv::ARRAY_FUNCTION_INDEX); + ret = Load(VariableType::JS_POINTER(), arrayFunc, IntPtr(JSFunction::PROTO_OR_DYNCLASS_OFFSET)); + } else { + ret = + GetGlobalConstantValue(VariableType::JS_POINTER(), glue, ConstantIndex::ELEMENT_NONE_HCLASS_INDEX); + } + } else { + ret = LoadHClass(arrayLiteral); + } + Jump(&exit); + } + Bind(&exit); + auto result = *ret; + env->SubCfgExit(); + return result; +} + +GateRef NewObjectStubBuilder::CreateEmptyArrayCommon(GateRef glue, GateRef hclass, GateRef trackInfo) { auto env = GetEnvironment(); Label entry(env); @@ -508,23 +665,61 @@ GateRef NewObjectStubBuilder::CreateEmptyArray(GateRef glue) DEFVARIABLE(result, VariableType::JS_ANY(), Undefined()); - GateRef glueGlobalEnvOffset = IntPtr(JSThread::GlueData::GetGlueGlobalEnvOffset(env->Is32Bit())); - GateRef glueGlobalEnv = Load(VariableType::NATIVE_POINTER(), glue, glueGlobalEnvOffset); - auto arrayFunc = GetGlobalEnvValue(VariableType::JS_ANY(), glueGlobalEnv, GlobalEnv::ARRAY_FUNCTION_INDEX); - auto hclass = Load(VariableType::JS_POINTER(), arrayFunc, IntPtr(JSFunction::PROTO_OR_DYNCLASS_OFFSET)); GateRef size = GetObjectSizeFromHClass(hclass); - auto emptyArray = GetGlobalConstantValue(VariableType::JS_POINTER(), glue, ConstantIndex::EMPTY_ARRAY_OBJECT_INDEX); - + GateRef emptyArray = GetGlobalConstantValue( + VariableType::JS_POINTER(), glue, ConstantIndex::EMPTY_ARRAY_OBJECT_INDEX); SetParameters(glue, size); - NewJSArrayLiteral(&result, &exit, RegionSpaceFlag::IN_YOUNG_SPACE, emptyArray, hclass, true); - + NewJSArrayLiteral(&result, &exit, RegionSpaceFlag::IN_YOUNG_SPACE, emptyArray, hclass, trackInfo, true); Bind(&exit); - auto ret = *result; + GateRef ret = *result; env->SubCfgExit(); return ret; } -GateRef NewObjectStubBuilder::CreateArrayWithBuffer(GateRef glue, GateRef index, GateRef jsFunc) +GateRef NewObjectStubBuilder::CreateEmptyArray(GateRef glue) +{ + auto env = GetEnvironment(); + DEFVARIABLE(trackInfo, VariableType::JS_ANY(), Undefined()); + GateRef glueGlobalEnvOffset = IntPtr(JSThread::GlueData::GetGlueGlobalEnvOffset(env->Is32Bit())); + GateRef glueGlobalEnv = Load(VariableType::NATIVE_POINTER(), glue, glueGlobalEnvOffset); + GateRef arrayFunc = GetGlobalEnvValue(VariableType::JS_ANY(), glueGlobalEnv, GlobalEnv::ARRAY_FUNCTION_INDEX); + GateRef hclass = Load(VariableType::JS_POINTER(), arrayFunc, IntPtr(JSFunction::PROTO_OR_DYNCLASS_OFFSET)); + return CreateEmptyArrayCommon(glue, hclass, *trackInfo); +} + +GateRef NewObjectStubBuilder::CreateEmptyArray( + GateRef glue, GateRef jsFunc, GateRef pc, GateRef profileTypeInfo, GateRef slotId, ProfileOperation callback) +{ + auto env = GetEnvironment(); + Label entry(env); + env->SubCfgEntry(&entry); + + DEFVARIABLE(trackInfo, VariableType::JS_ANY(), Undefined()); + DEFVARIABLE(hclass, VariableType::JS_ANY(), Undefined()); + Label slowpath(env); + Label mayFastpath(env); + Label createArray(env); + Branch(TaggedIsUndefined(profileTypeInfo), &slowpath, &mayFastpath); + Bind(&mayFastpath); + { + trackInfo = LoadTrackInfo(glue, jsFunc, pc, profileTypeInfo, slotId, Circuit::NullGate(), callback); + hclass = Load(VariableType::JS_ANY(), *trackInfo, IntPtr(TrackInfo::CACHED_HCLASS_OFFSET)); + trackInfo = env->GetBuilder()->CreateWeakRef(*trackInfo); + Jump(&createArray); + } + Bind(&slowpath); + { + hclass = LoadArrayHClassSlowPath(glue, jsFunc, pc, Circuit::NullGate(), callback); + Jump(&createArray); + } + Bind(&createArray); + GateRef result = CreateEmptyArrayCommon(glue, *hclass, *trackInfo); + env->SubCfgExit(); + return result; +} + +GateRef NewObjectStubBuilder::CreateArrayWithBuffer(GateRef glue, + GateRef index, GateRef jsFunc, GateRef pc, GateRef profileTypeInfo, GateRef slotId, ProfileOperation callback) { auto env = GetEnvironment(); Label entry(env); @@ -532,20 +727,98 @@ GateRef NewObjectStubBuilder::CreateArrayWithBuffer(GateRef glue, GateRef index, Label exit(env); DEFVARIABLE(result, VariableType::JS_ANY(), Undefined()); + DEFVARIABLE(trackInfo, VariableType::JS_ANY(), Undefined()); + DEFVARIABLE(hclass, VariableType::JS_ANY(), Undefined()); + GateRef method = GetMethodFromFunction(jsFunc); GateRef constPool = Load(VariableType::JS_ANY(), method, IntPtr(Method::CONSTANT_POOL_OFFSET)); GateRef module = GetModuleFromFunction(jsFunc); auto obj = GetArrayLiteralFromConstPool(glue, constPool, index, module); - auto hclass = LoadHClass(obj); - GateRef size = GetObjectSizeFromHClass(hclass); + + Label slowpath(env); + Label mayFastpath(env); + Label createArray(env); + Branch(TaggedIsUndefined(profileTypeInfo), &slowpath, &mayFastpath); + Bind(&mayFastpath); + { + trackInfo = LoadTrackInfo(glue, jsFunc, pc, profileTypeInfo, slotId, obj, callback); + hclass = Load(VariableType::JS_ANY(), *trackInfo, IntPtr(TrackInfo::CACHED_HCLASS_OFFSET)); + trackInfo = env->GetBuilder()->CreateWeakRef(*trackInfo); + Jump(&createArray); + } + Bind(&slowpath); + { + hclass = LoadArrayHClassSlowPath(glue, jsFunc, pc, obj, callback); + Jump(&createArray); + } + Bind(&createArray); + GateRef size = GetObjectSizeFromHClass(*hclass); SetParameters(glue, size); - NewJSArrayLiteral(&result, &exit, RegionSpaceFlag::IN_YOUNG_SPACE, obj, hclass, false); + NewJSArrayLiteral(&result, &exit, RegionSpaceFlag::IN_YOUNG_SPACE, obj, *hclass, *trackInfo, false); Bind(&exit); auto ret = *result; env->SubCfgExit(); return ret; } + +template +void NewObjectStubBuilder::CreateJSCollectionIterator( + Variable *result, Label *exit, GateRef thisValue, GateRef kind) +{ + ASSERT_PRINT((std::is_same_v || std::is_same_v), + "IteratorType must be JSSetIterator or JSMapIterator type"); + auto env = GetEnvironment(); + ConstantIndex iterClassIdx = static_cast(0); + int32_t iterOffset = 0; // ITERATED_SET_OFFSET + int32_t iterPrototypeIdx = 0; // ITERATOR_PROTOTYPE_INDEX + size_t linkedOffset = 0; // LINKED_MAP_OFFSET + if constexpr (std::is_same_v) { + iterClassIdx = ConstantIndex::JS_SET_ITERATOR_CLASS_INDEX; + iterOffset = IteratorType::ITERATED_SET_OFFSET; + iterPrototypeIdx = GlobalEnv::SET_ITERATOR_PROTOTYPE_INDEX; + linkedOffset = CollectionType::LINKED_SET_OFFSET; + } else { + iterClassIdx = ConstantIndex::JS_MAP_ITERATOR_CLASS_INDEX; + iterOffset = IteratorType::ITERATED_MAP_OFFSET; + iterPrototypeIdx = GlobalEnv::MAP_ITERATOR_PROTOTYPE_INDEX; + linkedOffset = CollectionType::LINKED_MAP_OFFSET; + } + GateRef iteratorHClass = GetGlobalConstantValue(VariableType::JS_POINTER(), glue_, iterClassIdx); + GateRef glueGlobalEnvOffset = IntPtr(JSThread::GlueData::GetGlueGlobalEnvOffset(env->Is32Bit())); + GateRef glueGlobalEnv = Load(VariableType::NATIVE_POINTER(), glue_, glueGlobalEnvOffset); + GateRef prototype = GetGlobalEnvValue(VariableType::JS_POINTER(), glueGlobalEnv, iterPrototypeIdx); + SetPrototypeToHClass(VariableType::JS_POINTER(), glue_, iteratorHClass, prototype); + + Label afterAllocate(env); + NewJSObject(result, &afterAllocate, iteratorHClass); + Bind(&afterAllocate); + Label setProperties(env); + Branch(TaggedIsException(result->ReadVariable()), exit, &setProperties); + Bind(&setProperties); + + SetExtensibleToBitfield(glue_, result->ReadVariable(), true); + // GetLinked + GateRef linked = Load(VariableType::JS_ANY(), thisValue, IntPtr(linkedOffset)); + + // SetIterated + GateRef iteratorOffset = IntPtr(iterOffset); + Store(VariableType::JS_POINTER(), glue_, result->ReadVariable(), iteratorOffset, linked); + + // SetIteratorNextIndex + GateRef nextIndexOffset = IntPtr(IteratorType::NEXT_INDEX_OFFSET); + Store(VariableType::INT32(), glue_, result->ReadVariable(), nextIndexOffset, Int32(0)); + + // SetIterationKind + GateRef kindBitfieldOffset = IntPtr(IteratorType::BIT_FIELD_OFFSET); + Store(VariableType::INT32(), glue_, result->ReadVariable(), kindBitfieldOffset, kind); + Jump(exit); +} + +template void NewObjectStubBuilder::CreateJSCollectionIterator( + Variable *result, Label *exit, GateRef set, GateRef kind); +template void NewObjectStubBuilder::CreateJSCollectionIterator( + Variable *result, Label *exit, GateRef set, GateRef kind); } // namespace panda::ecmascript::kungfu diff --git a/ecmascript/compiler/new_object_stub_builder.h b/ecmascript/compiler/new_object_stub_builder.h index 93752935dfdadf148b50f00d52f3c8e74268962b..a6c4ffc22fd808a1f507ec5752c914bb69aa885f 100644 --- a/ecmascript/compiler/new_object_stub_builder.h +++ b/ecmascript/compiler/new_object_stub_builder.h @@ -16,6 +16,8 @@ #ifndef ECMASCRIPT_COMPILER_NEW_OBJECT_STUB_BUILDER_H #define ECMASCRIPT_COMPILER_NEW_OBJECT_STUB_BUILDER_H +#include "ecmascript/compiler/builtins/builtins_string_stub_builder.h" +#include "ecmascript/compiler/profiler_operation.h" #include "ecmascript/compiler/stub_builder.h" namespace panda::ecmascript::kungfu { @@ -50,18 +52,33 @@ public: void NewArgumentsList(Variable *result, Label *exit, GateRef sp, GateRef startIdx, GateRef numArgs); void NewArgumentsObj(Variable *result, Label *exit, GateRef argumentsList, GateRef numArgs); void AllocLineStringObject(Variable *result, Label *exit, GateRef length, bool compressed); + void AllocSlicedStringObject(Variable *result, Label *exit, GateRef from, GateRef length, + FlatStringStubBuilder *flatString); + void AllocTreeStringObject(Variable *result, Label *exit, GateRef first, GateRef second, + GateRef length, bool compressed); void HeapAlloc(Variable *result, Label *exit, RegionSpaceFlag spaceType); void NewJSArrayLiteral(Variable *result, Label *exit, RegionSpaceFlag spaceType, GateRef obj, GateRef hclass, - bool isEmptyArray); + GateRef trackInfo, bool isEmptyArray); + GateRef NewTrackInfo(GateRef glue, GateRef cachedHClass, GateRef cachedFunc); void InitializeWithSpeicalValue(Label *exit, GateRef object, GateRef value, GateRef start, GateRef end); GateRef FastNewThisObject(GateRef glue, GateRef ctor); GateRef NewThisObjectChecked(GateRef glue, GateRef ctor); GateRef CreateEmptyArray(GateRef glue); - GateRef CreateArrayWithBuffer(GateRef glue, GateRef index, GateRef jsFunc); + GateRef CreateEmptyArray(GateRef glue, GateRef jsFunc, GateRef pc, GateRef profileTypeInfo, GateRef slotId, + ProfileOperation callback); + GateRef CreateArrayWithBuffer(GateRef glue, GateRef index, GateRef jsFunc, GateRef pc, + GateRef profileTypeInfo, GateRef slotId, ProfileOperation callback); void NewTaggedArrayChecked(Variable *result, GateRef len, Label *exit); + template + void CreateJSCollectionIterator(Variable *result, Label *exit, GateRef set, GateRef kind); private: static constexpr int MAX_TAGGED_ARRAY_LENGTH = 50; + GateRef LoadTrackInfo(GateRef glue, GateRef jsFunc, GateRef pc, GateRef profileTypeInfo, GateRef slotId, + GateRef arrayLiteral, ProfileOperation callback); + GateRef LoadArrayHClassSlowPath( + GateRef glue, GateRef jsFunc, GateRef pc, GateRef arrayLiteral, ProfileOperation callback); + GateRef CreateEmptyArrayCommon(GateRef glue, GateRef hclass, GateRef trackInfo); void AllocateInYoung(Variable *result, Label *exit); void InitializeTaggedArrayWithSpeicalValue(Label *exit, GateRef array, GateRef value, GateRef start, GateRef length); diff --git a/ecmascript/compiler/ntype_hcr_lowering.cpp b/ecmascript/compiler/ntype_hcr_lowering.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2f08b29f07af2bedd4ea32625bc6939671f6786f --- /dev/null +++ b/ecmascript/compiler/ntype_hcr_lowering.cpp @@ -0,0 +1,304 @@ +/* + * 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/compiler/ntype_hcr_lowering.h" +#include "ecmascript/compiler/circuit_builder-inl.h" +#include "ecmascript/dfx/vmstat/opt_code_profiler.h" + +namespace panda::ecmascript::kungfu { + +void NTypeHCRLowering::RunNTypeHCRLowering() +{ + std::vector gateList; + circuit_->GetAllGates(gateList); + for (const auto &gate : gateList) { + auto op = acc_.GetOpCode(gate); + if (op == OpCode::JS_BYTECODE) { + Lower(gate); + } + } + + if (IsLogEnabled()) { + LOG_COMPILER(INFO) << ""; + LOG_COMPILER(INFO) << "\033[34m" + << "====================" + << " After NTypeHCRlowering " + << "[" << GetMethodName() << "]" + << "====================" + << "\033[0m"; + circuit_->PrintAllGatesWithBytecode(); + LOG_COMPILER(INFO) << "\033[34m" << "========================= End ==========================" << "\033[0m"; + } +} + +void NTypeHCRLowering::Lower(GateRef gate) +{ + EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate); + // initialize label manager + Environment env(gate, circuit_, &builder_); + switch (ecmaOpcode) { + case EcmaOpcode::CREATEEMPTYARRAY_IMM8: + case EcmaOpcode::CREATEEMPTYARRAY_IMM16: + LowerNTypedCreateEmptyArray(gate); + break; + case EcmaOpcode::CREATEARRAYWITHBUFFER_IMM8_ID16: + case EcmaOpcode::CREATEARRAYWITHBUFFER_IMM16_ID16: + LowerNTypedCreateArrayWithBuffer(gate); + break; + case EcmaOpcode::STOWNBYINDEX_IMM8_V8_IMM16: + case EcmaOpcode::STOWNBYINDEX_IMM16_V8_IMM16: + case EcmaOpcode::WIDE_STOWNBYINDEX_PREF_V8_IMM32: + LowerNTypedStownByIndex(gate); + break; + case EcmaOpcode::STOWNBYNAME_IMM8_ID16_V8: + case EcmaOpcode::STOWNBYNAME_IMM16_ID16_V8: + LowerNTypedStOwnByName(gate); + break; + case EcmaOpcode::THROW_UNDEFINEDIFHOLEWITHNAME_PREF_ID16: + LowerThrowUndefinedIfHoleWithName(gate); + break; + case EcmaOpcode::LDLEXVAR_IMM4_IMM4: + case EcmaOpcode::LDLEXVAR_IMM8_IMM8: + case EcmaOpcode::WIDE_LDLEXVAR_PREF_IMM16_IMM16: + LowerLdLexVar(gate); + break; + case EcmaOpcode::STLEXVAR_IMM4_IMM4: + case EcmaOpcode::STLEXVAR_IMM8_IMM8: + case EcmaOpcode::WIDE_STLEXVAR_PREF_IMM16_IMM16: + LowerStLexVar(gate); + break; + default: + break; + } +} + +void NTypeHCRLowering::LowerThrowUndefinedIfHoleWithName(GateRef gate) +{ + GateRef value = acc_.GetValueIn(gate, 1); // 1: the second parameter + builder_.LexVarIsHoleCheck(value); + acc_.ReplaceHirAndDeleteIfException(gate, builder_.GetStateDepend(), Circuit::NullGate()); +} + +void NTypeHCRLowering::LowerLdLexVar(GateRef gate) +{ + AddProfiling(gate); + GateRef level = acc_.GetValueIn(gate, 0); // 0: first parameter + GateRef index = acc_.GetValueIn(gate, 1); // 1: the second parameter + GateRef currentEnv = acc_.GetValueIn(gate, 2); // 2: the third parameter + + uint32_t levelValue = static_cast(acc_.GetConstantValue(level)); + uint32_t indexValue = static_cast(acc_.GetConstantValue(index)); + indexValue += LexicalEnv::RESERVED_ENV_LENGTH; + GateRef result = Circuit::NullGate(); + if (levelValue == 0) { + result = builder_.LoadFromTaggedArray(currentEnv, indexValue); + } else if (levelValue == 1) { // 1: level 1 + auto parentEnv = builder_.LoadFromTaggedArray(currentEnv, LexicalEnv::PARENT_ENV_INDEX); + result = builder_.LoadFromTaggedArray(parentEnv, indexValue); + } else { + // level > 1, go slowpath + return; + } + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result); +} + +void NTypeHCRLowering::LowerStLexVar(GateRef gate) +{ + AddProfiling(gate); + GateRef level = acc_.GetValueIn(gate, 0); // 0: first parameter + GateRef index = acc_.GetValueIn(gate, 1); // 1: the second parameter + GateRef currentEnv = acc_.GetValueIn(gate, 2); // 2: the third parameter + GateRef value = acc_.GetValueIn(gate, 3); // 3: the fourth parameter + + uint32_t levelValue = static_cast(acc_.GetConstantValue(level)); + uint32_t indexValue = static_cast(acc_.GetConstantValue(index)); + indexValue += LexicalEnv::RESERVED_ENV_LENGTH; + GateRef result = Circuit::NullGate(); + if (levelValue == 0) { + result = builder_.StoreToTaggedArray(currentEnv, indexValue, value); + } else if (levelValue == 1) { // 1: level 1 + auto parentEnv = builder_.LoadFromTaggedArray(currentEnv, LexicalEnv::PARENT_ENV_INDEX); + result = builder_.StoreToTaggedArray(parentEnv, indexValue, value); + } else { + // level > 1, go slowpath + return; + } + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result); +} + +void NTypeHCRLowering::LowerNTypedCreateEmptyArray(GateRef gate) +{ + // in the future, the type of the elements in the array will be obtained through pgo, + // and the type will be used to determine whether to create a typed-array. + AddProfiling(gate); + auto thread = tsManager_->GetEcmaVM()->GetJSThread(); + uint64_t bcAbsoluteOffset = GetBcAbsoluteOffset(gate); + ElementsKind kind = acc_.TryGetElementsKind(gate); + auto hclassIdx = thread->GetArrayHClassIndexMap().at(kind); + tsManager_->AddArrayTSConstantIndex(bcAbsoluteOffset, JSTaggedValue(static_cast(hclassIdx))); + GateRef array = builder_.CreateArray(kind, 0); + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), array); +} + +void NTypeHCRLowering::LowerNTypedCreateArrayWithBuffer(GateRef gate) +{ + // 1: number of value inputs + ASSERT(acc_.GetNumValueIn(gate) == 1); + GateRef index = acc_.GetValueIn(gate, 0); + auto thread = tsManager_->GetEcmaVM()->GetJSThread(); + uint64_t bcAbsoluteOffset = GetBcAbsoluteOffset(gate); + uint32_t cpIdx = static_cast(acc_.GetConstantValue(index)); + JSHandle constpoolHandle(tsManager_->GetConstantPool()); + JSTaggedValue arr = ConstantPool::GetLiteralFromCache( + thread, constpoolHandle.GetTaggedValue(), cpIdx, recordName_); + JSHandle arrayHandle(thread, arr); + + ElementsKind kind = acc_.TryGetElementsKind(gate); + auto hclassIdx = thread->GetArrayHClassIndexMap().at(kind); + GateType gateType = acc_.GetGateType(gate); + panda_file::File::EntityId id = ConstantPool::GetIdFromCache(constpoolHandle.GetTaggedValue(), cpIdx); + tsManager_->AddArrayTSConstantIndex(bcAbsoluteOffset, JSTaggedValue(static_cast(hclassIdx))); + tsManager_->AddArrayTSElements(id, arrayHandle->GetElements()); + tsManager_->AddArrayTSElementsKind(id, JSTaggedValue(static_cast(kind))); + gateType = tsManager_->TryNarrowUnionType(gateType); + + int elementIndex = -1; + if (tsManager_->IsArrayTypeKind(gateType)) { + elementIndex = tsManager_->GetElementsIndexByArrayType(gateType, id); + } + if (elementIndex == -1) { // slowpath + return; + } + + AddProfiling(gate); + GateRef elementIndexGate = builder_.IntPtr(elementIndex); + GateRef array = builder_.CreateArrayWithBuffer(kind, ArrayMetaDataAccessor::Mode::CREATE, index, elementIndexGate); + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), array); +} + +void NTypeHCRLowering::LowerNTypedStownByIndex(GateRef gate) +{ + // 3: number of value inputs + ASSERT(acc_.GetNumValueIn(gate) == 3); + GateRef receiver = acc_.GetValueIn(gate, 0); + GateRef index = acc_.GetValueIn(gate, 1); + GateRef value = acc_.GetValueIn(gate, 2); + if (acc_.GetOpCode(receiver) != OpCode::CREATE_ARRAY && + acc_.GetOpCode(receiver) != OpCode::CREATE_ARRAY_WITH_BUFFER) { + return; + } + builder_.COWArrayCheck(receiver); + + AddProfiling(gate); + uint32_t indexValue = static_cast(acc_.GetConstantValue(index)); + uint32_t arraySize = acc_.GetArraySize(receiver); + if (indexValue > arraySize) { + acc_.TrySetElementsKind(receiver, ElementsKind::HOLE); + } + acc_.SetArraySize(receiver, std::max(arraySize, indexValue + 1)); + index = builder_.Int32(indexValue); + builder_.StoreElement(receiver, index, value); + acc_.ReplaceHirAndDeleteIfException(gate, builder_.GetStateDepend(), Circuit::NullGate()); +} + +void NTypeHCRLowering::LowerNTypedStOwnByName(GateRef gate) +{ + // 3: number of value inputs + ASSERT(acc_.GetNumValueIn(gate) == 3); + auto constData = acc_.GetValueIn(gate, 0); + uint16_t propIndex = acc_.GetConstantValue(constData); + auto thread = tsManager_->GetEcmaVM()->GetJSThread(); + auto propKey = tsManager_->GetStringFromConstantPool(propIndex); + + GateRef receiver = acc_.GetValueIn(gate, 1); + GateRef value = acc_.GetValueIn(gate, 2); + + GateType receiverType = acc_.GetGateType(receiver); + receiverType = tsManager_->TryNarrowUnionType(receiverType); + + int hclassIndex = -1; + if (tsManager_->IsObjectTypeKind(receiverType)) { + hclassIndex = tsManager_->GetHClassIndexByObjectType(receiverType); + } + if (hclassIndex == -1) { // slowpath + return; + } + JSHClass *hclass = JSHClass::Cast(tsManager_->GetValueFromCache(hclassIndex).GetTaggedObject()); + + PropertyLookupResult plr = JSHClass::LookupPropertyInAotHClass(thread, hclass, propKey); + if (!plr.IsFound() || !plr.IsLocal() || plr.IsAccessor() || !plr.IsWritable()) { // slowpath + return; + } + AddProfiling(gate); + + GateRef pfrGate = builder_.Int32(plr.GetData()); + builder_.StoreProperty(receiver, pfrGate, value); + + acc_.ReplaceHirAndDeleteIfException(gate, builder_.GetStateDepend(), Circuit::NullGate()); +} + +uint64_t NTypeHCRLowering::GetBcAbsoluteOffset(GateRef gate) const +{ + uint64_t pcOffset = acc_.TryGetPcOffset(gate); + uint64_t pfOffset = reinterpret_cast(jsPandaFile_->GetHeader()); + uint64_t methodOffset = reinterpret_cast(methodLiteral_->GetBytecodeArray()); + uint64_t bcAbsoluteOffset = methodOffset - pfOffset + pcOffset; + return bcAbsoluteOffset; +} + +void NTypeHCRLowering::AddProfiling(GateRef gate) +{ + if (IsTraceBC()) { + // see stateSplit as a part of JSByteCode if exists + GateRef maybeStateSplit = acc_.GetDep(gate); + GateRef current = Circuit::NullGate(); + if (acc_.GetOpCode(maybeStateSplit) == OpCode::STATE_SPLIT) { + current = maybeStateSplit; + } else { + current = gate; + } + + EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate); + auto ecmaOpcodeGate = builder_.Int32(static_cast(ecmaOpcode)); + GateRef constOpcode = builder_.Int32ToTaggedInt(ecmaOpcodeGate); + GateRef typedPath = builder_.Int32ToTaggedInt(builder_.Int32(1)); + GateRef traceGate = builder_.CallRuntime(glue_, RTSTUB_ID(DebugAOTPrint), acc_.GetDep(current), + { constOpcode, typedPath }, gate); + acc_.SetDep(current, traceGate); + builder_.SetDepend(acc_.GetDep(gate)); // set gate depend: trace or STATE_SPLIT + } + + if (IsProfiling()) { + // see stateSplit as a part of JSByteCode if exists + GateRef maybeStateSplit = acc_.GetDep(gate); + GateRef current = Circuit::NullGate(); + if (acc_.GetOpCode(maybeStateSplit) == OpCode::STATE_SPLIT) { + current = maybeStateSplit; + } else { + current = gate; + } + + EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate); + auto ecmaOpcodeGate = builder_.Int32(static_cast(ecmaOpcode)); + GateRef constOpcode = builder_.Int32ToTaggedInt(ecmaOpcodeGate); + GateRef mode = + builder_.Int32ToTaggedInt(builder_.Int32(static_cast(OptCodeProfiler::Mode::TYPED_PATH))); + GateRef profiling = builder_.CallRuntime(glue_, RTSTUB_ID(ProfileOptimizedCode), acc_.GetDep(current), + { constOpcode, mode }, gate); + acc_.SetDep(current, profiling); + builder_.SetDepend(acc_.GetDep(gate)); // set gate depend: profiling or STATE_SPLIT + } +} +} diff --git a/ecmascript/compiler/ntype_hcr_lowering.h b/ecmascript/compiler/ntype_hcr_lowering.h new file mode 100644 index 0000000000000000000000000000000000000000..b2256401f0fcfcc1e55661ee07ea8af999318cbf --- /dev/null +++ b/ecmascript/compiler/ntype_hcr_lowering.h @@ -0,0 +1,92 @@ +/* + * 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_COMPILER_NTYPE_HCR_LOWERING_H +#define ECMASCRIPT_COMPILER_NTYPE_HCR_LOWERING_H + +#include "ecmascript/compiler/argument_accessor.h" +#include "ecmascript/compiler/builtins/builtins_call_signature.h" +#include "ecmascript/compiler/bytecode_circuit_builder.h" +#include "ecmascript/compiler/circuit_builder-inl.h" +#include "ecmascript/compiler/pass_manager.h" + +namespace panda::ecmascript::kungfu { +class NTypeHCRLowering { +public: + NTypeHCRLowering(Circuit *circuit, PassContext *ctx, TSManager *tsManager, const MethodLiteral *methodLiteral, + const CString &recordName, bool enableLog, const std::string& name) + : circuit_(circuit), + acc_(circuit), + builder_(circuit, ctx->GetCompilerConfig()), + recordName_(recordName), + tsManager_(tsManager), + jsPandaFile_(ctx->GetJSPandaFile()), + methodLiteral_(methodLiteral), + enableLog_(enableLog), + profiling_(ctx->GetCompilerConfig()->IsProfiling()), + traceBc_(ctx->GetCompilerConfig()->IsTraceBC()), + methodName_(name), + glue_(acc_.GetGlueFromArgList()) {} + + ~NTypeHCRLowering() = default; + + void RunNTypeHCRLowering(); +private: + void Lower(GateRef gate); + void LowerNTypedCreateEmptyArray(GateRef gate); + void LowerNTypedCreateArrayWithBuffer(GateRef gate); + void LowerNTypedStownByIndex(GateRef gate); + void LowerNTypedStOwnByName(GateRef gate); + void LowerLdLexVar(GateRef gate); + void LowerStLexVar(GateRef gate); + void LowerThrowUndefinedIfHoleWithName(GateRef gate); + uint64_t GetBcAbsoluteOffset(GateRef gate) const; + + bool IsLogEnabled() const + { + return enableLog_; + } + + bool IsProfiling() const + { + return profiling_; + } + + bool IsTraceBC() const + { + return traceBc_; + } + + const std::string& GetMethodName() const + { + return methodName_; + } + + void AddProfiling(GateRef gate); + Circuit *circuit_ {nullptr}; + GateAccessor acc_; + CircuitBuilder builder_; + const CString &recordName_; + TSManager *tsManager_ {nullptr}; + const JSPandaFile *jsPandaFile_ {nullptr}; + const MethodLiteral *methodLiteral_ {nullptr}; + bool enableLog_ {false}; + bool profiling_ {false}; + bool traceBc_ {false}; + std::string methodName_; + GateRef glue_ {Circuit::NullGate()}; +}; +} // panda::ecmascript::kungfu +#endif // ECMASCRIPT_COMPILER_NTYPE_HCR_LOWERING_H diff --git a/ecmascript/compiler/ntype_mcr_lowering.cpp b/ecmascript/compiler/ntype_mcr_lowering.cpp index 6868b2b150e56de395a4851cdf4925696e09c0ae..48d104129f9b60d12df0b885255af548861567b2 100644 --- a/ecmascript/compiler/ntype_mcr_lowering.cpp +++ b/ecmascript/compiler/ntype_mcr_lowering.cpp @@ -14,43 +14,186 @@ */ #include "ecmascript/compiler/ntype_mcr_lowering.h" +#include "ecmascript/dfx/vmstat/opt_code_profiler.h" +#include "ecmascript/compiler/new_object_stub_builder.h" namespace panda::ecmascript::kungfu { -void NTypeMCRLowering::RunNTypeMCRLowering() -{ - std::vector gateList; - circuit_->GetAllGates(gateList); - for (const auto &gate : gateList) { - auto op = acc_.GetOpCode(gate); - if (op == OpCode::JS_BYTECODE) { - Lower(gate); - } + +GateRef NTypeMCRLowering::VisitGate(GateRef gate) +{ + GateRef glue = acc_.GetGlueFromArgList(); + auto op = acc_.GetOpCode(gate); + switch (op) { + case OpCode::CREATE_ARRAY: + LowerCreateArray(gate, glue); + break; + case OpCode::CREATE_ARRAY_WITH_BUFFER: + LowerCreateArrayWithBuffer(gate, glue); + break; + default: + break; } + return Circuit::NullGate(); +} - if (IsLogEnabled()) { - LOG_COMPILER(INFO) << ""; - LOG_COMPILER(INFO) << "\033[34m" - << "====================" - << " After NTypeMCRlowering " - << "[" << GetMethodName() << "]" - << "====================" - << "\033[0m"; - circuit_->PrintAllGatesWithBytecode(); - LOG_COMPILER(INFO) << "\033[34m" << "========================= End ==========================" << "\033[0m"; +void NTypeMCRLowering::LowerCreateArray(GateRef gate, GateRef glue) +{ + Environment env(gate, circuit_, &builder_); + if (acc_.GetArraySize(gate) == 0) { + LowerCreateEmptyArray(gate); + } else { + LowerCreateArrayWithOwn(gate, glue); } } -void NTypeMCRLowering::Lower(GateRef gate) +void NTypeMCRLowering::LowerCreateEmptyArray(GateRef gate) +{ + GateRef length = builder_.Int32(0); + GateRef elements = builder_.GetGlobalConstantValue(ConstantIndex::EMPTY_ARRAY_OBJECT_INDEX); + + auto array = NewJSArrayLiteral(gate, elements, length); + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), array); +} + +void NTypeMCRLowering::LowerCreateArrayWithOwn(GateRef gate, GateRef glue) +{ + uint32_t elementsLength = acc_.GetArraySize(gate); + GateRef length = builder_.IntPtr(elementsLength); + GateRef elements = CreateElementsWithLength(gate, glue, elementsLength); + + auto array = NewJSArrayLiteral(gate, elements, length); + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), array); +} + +void NTypeMCRLowering::LowerCreateArrayWithBuffer(GateRef gate, GateRef glue) { - EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate); - // initialize label manager Environment env(gate, circuit_, &builder_); - switch (ecmaOpcode) { - case EcmaOpcode::TYPEOF_IMM8: - case EcmaOpcode::TYPEOF_IMM16: - break; - default: - break; + // 2: number of value inputs + ASSERT(acc_.GetNumValueIn(gate) == 2); + GateRef index = acc_.GetValueIn(gate, 0); + GateRef aotElmIndex = acc_.GetValueIn(gate, 1); + auto elementIndex = acc_.GetConstantValue(aotElmIndex); + uint32_t constPoolIndex = static_cast(acc_.GetConstantValue(index)); + ArgumentAccessor argAcc(circuit_); + GateRef frameState = GetFrameState(gate); + GateRef jsFunc = argAcc.GetFrameArgsIn(frameState, FrameArgIdx::FUNC); + GateRef literialElements = LoadFromConstPool(jsFunc, elementIndex); + auto thread = tsManager_->GetEcmaVM()->GetJSThread(); + JSHandle constpoolHandle(tsManager_->GetConstantPool()); + JSTaggedValue arr = ConstantPool::GetLiteralFromCache( + thread, constpoolHandle.GetTaggedValue(), constPoolIndex, recordName_); + JSHandle arrayHandle(thread, arr); + TaggedArray *arrayLiteral = TaggedArray::Cast(arrayHandle->GetElements()); + uint32_t literialLength = arrayLiteral->GetLength(); + uint32_t arrayLength = acc_.GetArraySize(gate); + GateRef elements = Circuit::NullGate(); + GateRef length = Circuit::NullGate(); + if (arrayLength > literialLength) { + elements = CreateElementsWithLength(gate, glue, arrayLength); + for (uint32_t i = 0; i < literialLength; i++) { + GateRef value = builder_.LoadFromTaggedArray(literialElements, i); + builder_.StoreToTaggedArray(elements, i, value); + } + length = builder_.IntPtr(arrayLength); + } else { + elements = literialElements; + length = builder_.IntPtr(literialLength); + } + + auto array = NewJSArrayLiteral(gate, elements, length); + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), array); +} + +GateRef NTypeMCRLowering::LoadFromConstPool(GateRef jsFunc, size_t index) +{ + GateRef constPool = builder_.GetConstPool(jsFunc); + return builder_.LoadFromTaggedArray(constPool, index); +} + +GateRef NTypeMCRLowering::CreateElementsWithLength(GateRef gate, GateRef glue, size_t arrayLength) +{ + GateRef elements = Circuit::NullGate(); + GateRef length = builder_.IntPtr(arrayLength); + if (arrayLength < MAX_TAGGED_ARRAY_LENGTH) { + elements = NewTaggedArray(arrayLength); + } else { + elements = LowerCallRuntime(glue, gate, RTSTUB_ID(NewTaggedArray), { builder_.Int32ToTaggedInt(length) }, true); + } + return elements; +} + +GateRef NTypeMCRLowering::NewJSArrayLiteral(GateRef gate, GateRef elements, GateRef length) +{ + ElementsKind kind = acc_.GetArrayMetaDataAccessor(gate).GetElementsKind(); + GateRef hclass = Circuit::NullGate(); + if (!Elements::IsGeneric(kind)) { + auto thread = tsManager_->GetEcmaVM()->GetJSThread(); + auto hclassIndex = thread->GetArrayHClassIndexMap().at(kind); + hclass = builder_.GetGlobalConstantValue(hclassIndex); + } else { + GateRef globalEnv = builder_.GetGlobalEnv(); + hclass = builder_.GetGlobalEnvObjHClass(globalEnv, GlobalEnv::ARRAY_FUNCTION_INDEX); + } + + JSHandle arrayFunc(tsManager_->GetEcmaVM()->GetGlobalEnv()->GetArrayFunction()); + JSTaggedValue protoOrHClass = arrayFunc->GetProtoOrHClass(); + JSHClass *arrayHC = JSHClass::Cast(protoOrHClass.GetTaggedObject()); + size_t arraySize = arrayHC->GetObjectSize(); + size_t lengthAccessorOffset = arrayHC->GetInlinedPropertiesOffset(JSArray::LENGTH_INLINE_PROPERTY_INDEX); + + GateRef emptyArray = builder_.GetGlobalConstantValue(ConstantIndex::EMPTY_ARRAY_OBJECT_INDEX); + GateRef accessor = builder_.GetGlobalConstantValue(ConstantIndex::ARRAY_LENGTH_ACCESSOR); + GateRef size = builder_.IntPtr(arrayHC->GetObjectSize()); + + builder_.StartAllocate(); + GateRef array = builder_.HeapAlloc(size, GateType::TaggedValue(), RegionSpaceFlag::IN_YOUNG_SPACE); + // initialization + for (size_t offset = JSArray::SIZE; offset < arraySize; offset += JSTaggedValue::TaggedTypeSize()) { + builder_.StoreConstOffset(VariableType::INT64(), array, offset, builder_.Undefined()); + } + builder_.StoreConstOffset(VariableType::JS_POINTER(), array, 0, hclass); + builder_.StoreConstOffset(VariableType::INT64(), array, ECMAObject::HASH_OFFSET, + builder_.Int64(JSTaggedValue(0).GetRawData())); + builder_.StoreConstOffset(VariableType::JS_POINTER(), array, JSObject::PROPERTIES_OFFSET, emptyArray); + builder_.StoreConstOffset(VariableType::JS_POINTER(), array, JSObject::ELEMENTS_OFFSET, elements); + builder_.StoreConstOffset(VariableType::INT32(), array, JSArray::LENGTH_OFFSET, length); + builder_.StoreConstOffset(VariableType::JS_POINTER(), array, lengthAccessorOffset, accessor); + builder_.StoreConstOffset(VariableType::INT64(), array, JSArray::TRACK_INFO_OFFSET, builder_.Undefined()); + builder_.FinishAllocate(); + return array; +} + +GateRef NTypeMCRLowering::NewTaggedArray(size_t length) +{ + GateRef elementsHclass = builder_.GetGlobalConstantValue(ConstantIndex::ARRAY_CLASS_INDEX); + GateRef elementsSize = builder_.ComputeTaggedArraySize(builder_.IntPtr(length)); + + builder_.StartAllocate(); + GateRef elements = builder_.HeapAlloc(elementsSize, GateType::TaggedValue(), RegionSpaceFlag::IN_YOUNG_SPACE); + builder_.StoreConstOffset(VariableType::JS_POINTER(), elements, 0, elementsHclass); + builder_.StoreConstOffset(VariableType::JS_ANY(), elements, TaggedArray::LENGTH_OFFSET, + builder_.Int32ToTaggedInt(builder_.IntPtr(length))); + size_t endOffset = TaggedArray::DATA_OFFSET + length * JSTaggedValue::TaggedTypeSize(); + // initialization + for (size_t offset = TaggedArray::DATA_OFFSET; offset < endOffset; offset += JSTaggedValue::TaggedTypeSize()) { + builder_.StoreConstOffset(VariableType::INT64(), elements, offset, builder_.Hole()); + } + builder_.FinishAllocate(); + + return elements; +} + +GateRef NTypeMCRLowering::LowerCallRuntime(GateRef glue, GateRef hirGate, int index, const std::vector &args, + bool useLabel) +{ + if (useLabel) { + GateRef result = builder_.CallRuntime(glue, index, Gate::InvalidGateRef, args, hirGate); + return result; + } else { + const CallSignature *cs = RuntimeStubCSigns::Get(RTSTUB_ID(CallRuntime)); + GateRef target = builder_.IntPtr(index); + GateRef result = builder_.Call(cs, glue, target, dependEntry_, args, hirGate); + return result; } } } diff --git a/ecmascript/compiler/ntype_mcr_lowering.h b/ecmascript/compiler/ntype_mcr_lowering.h index 041686c43480af0d5fc9d35e8cb963f63249eaeb..6a6582c34624ddb2dada823e60c18d3811c5630c 100644 --- a/ecmascript/compiler/ntype_mcr_lowering.h +++ b/ecmascript/compiler/ntype_mcr_lowering.h @@ -17,42 +17,59 @@ #define ECMASCRIPT_COMPILER_NTYPE_MCR_LOWERING_H #include "ecmascript/compiler/argument_accessor.h" +#include "ecmascript/compiler/builtins/builtins_call_signature.h" #include "ecmascript/compiler/bytecode_circuit_builder.h" +#include "ecmascript/compiler/circuit_builder-inl.h" +#include "ecmascript/compiler/combined_pass_visitor.h" #include "ecmascript/compiler/pass_manager.h" - namespace panda::ecmascript::kungfu { -class NTypeMCRLowering { +class NTypeMCRLowering : public PassVisitor { public: - NTypeMCRLowering(Circuit *circuit, PassContext *ctx, - bool enableLog, const std::string& name) - : circuit_(circuit), + NTypeMCRLowering(Circuit *circuit, RPOVisitor *visitor, PassContext *ctx, const CString &recordName, Chunk* chunk) + : PassVisitor(circuit, chunk, visitor), + circuit_(circuit), acc_(circuit), builder_(circuit, ctx->GetCompilerConfig()), - enableLog_(enableLog), - methodName_(name), + dependEntry_(circuit->GetDependRoot()), + tsManager_(ctx->GetTSManager()), + recordName_(recordName), + profiling_(ctx->GetCompilerConfig()->IsProfiling()), + traceBc_(ctx->GetCompilerConfig()->IsTraceBC()), glue_(acc_.GetGlueFromArgList()) {} ~NTypeMCRLowering() = default; - - void RunNTypeMCRLowering(); + GateRef VisitGate(GateRef gate) override; private: + static constexpr int MAX_TAGGED_ARRAY_LENGTH = 50; void Lower(GateRef gate); + void LowerCreateArray(GateRef gate, GateRef glue); + void LowerCreateArrayWithBuffer(GateRef gate, GateRef glue); + void LowerCreateEmptyArray(GateRef gate); + void LowerCreateArrayWithOwn(GateRef gate, GateRef glue); + void LowerStLexVar(GateRef gate); + void LowerLdLexVar(GateRef gate); - bool IsLogEnabled() const - { - return enableLog_; - } + GateRef LoadFromConstPool(GateRef jsFunc, size_t index); + GateRef NewJSArrayLiteral(GateRef gate, GateRef elements, GateRef length); + GateRef NewTaggedArray(size_t length); + GateRef CreateElementsWithLength(GateRef gate, GateRef glue, size_t arrayLength); + GateRef LowerCallRuntime(GateRef glue, GateRef hirGate, int index, const std::vector &args, + bool useLabel = false); - const std::string& GetMethodName() const + GateRef GetFrameState(GateRef gate) const { - return methodName_; + return acc_.GetFrameState(gate); } Circuit *circuit_ {nullptr}; GateAccessor acc_; CircuitBuilder builder_; - bool enableLog_ {false}; - std::string methodName_; + GateRef dependEntry_; + TSManager *tsManager_ {nullptr}; + const CString &recordName_; + panda_file::File::EntityId methodId_ {0}; + bool profiling_ {false}; + bool traceBc_ {false}; GateRef glue_ {Circuit::NullGate()}; }; } // panda::ecmascript::kungfu diff --git a/ecmascript/compiler/number_gate_info.h b/ecmascript/compiler/number_gate_info.h index caf0507d8640e7e63f71bed9c16ed9116ea9e763..8730ee3996cf1aafbf2dc05b014af37c06953d80 100644 --- a/ecmascript/compiler/number_gate_info.h +++ b/ecmascript/compiler/number_gate_info.h @@ -17,6 +17,7 @@ #define ECMASCRIPT_NUMBER_GATE_INFO_H #include "ecmascript/compiler/gate_accessor.h" #include "ecmascript/js_hclass.h" +#include "ecmascript/js_typed_array.h" namespace panda::ecmascript::kungfu { @@ -24,8 +25,10 @@ enum class TypeInfo { NONE, INT1, INT32, + UINT32, FLOAT64, TAGGED, + CHAR, }; class UseInfo { @@ -98,17 +101,20 @@ public: max_ = *std::lower_bound(rangeBounds_.begin(), rangeBounds_.end(), max); } } - + static constexpr int32_t UINT30_MAX = 0x3fffffff; - static const inline std::vector rangeBounds_ = { INT32_MIN, INT32_MIN + 1, - -1, 0, 1, UINT30_MAX, UINT30_MAX + 1, - INT32_MAX - 1, INT32_MAX }; + static constexpr int32_t TYPED_ARRAY_ONHEAP_MAX = JSTypedArray::MAX_ONHEAP_LENGTH; + static constexpr int32_t UINT18_MAX = (1 << 18) - 1; + static const inline std::vector rangeBounds_ = {INT32_MIN, INT32_MIN + 1, + -UINT18_MAX, -TYPED_ARRAY_ONHEAP_MAX, -1, 0, 1, TYPED_ARRAY_ONHEAP_MAX - 1, + TYPED_ARRAY_ONHEAP_MAX, TYPED_ARRAY_ONHEAP_MAX + 1, TYPED_ARRAY_ONHEAP_MAX * 3, + UINT18_MAX, UINT30_MAX, UINT30_MAX + 1, INT32_MAX - 1, INT32_MAX }; static RangeInfo NONE() { return RangeInfo(INT32_MAX, INT32_MIN); } - + static RangeInfo ANY() { return RangeInfo(INT32_MIN, INT32_MAX); @@ -151,11 +157,83 @@ public: RangeInfo operator+ (const RangeInfo &rhs) const { + ASSERT(min_ <= max_ && rhs.min_ <= rhs.max_); int32_t nmax = MaybeAddOverflow(rhs) ? INT32_MAX : max_ + rhs.max_; int32_t nmin = MaybeAddUnderflow(rhs) ? INT32_MIN : min_ + rhs.min_; return RangeInfo(nmin, nmax); } + RangeInfo operator% (const RangeInfo &rhs) const + { + ASSERT(min_ <= max_ && rhs.min_ <= rhs.max_); + RangeInfo result = RangeInfo(0, 0); + int32_t nmax = std::max(std::abs(rhs.min_), std::abs(rhs.max_)); + if (max_ > 0) result = result.Union(RangeInfo(0, nmax - 1)); + if (min_ < 0) result = result.Union(RangeInfo(-nmax + 1, 0)); + return result; + } + + bool MaybeZero() const + { + return min_ <= 0 && max_ >= 0; + } + + RangeInfo operator* (const RangeInfo &rhs) const + { + ASSERT(min_ <= max_ && rhs.min_ <= rhs.max_); + int32_t nmax = GetMaxMulResult(rhs); + int32_t nmin = GetMinMulResult(rhs); + return RangeInfo(nmin, nmax); + } + + int32_t GetMaxMulResult(const RangeInfo &rhs) const + { + return std::max({ TryMul(min_, rhs.min_), TryMul(min_, rhs.max_), + TryMul(max_, rhs.min_), TryMul(max_, rhs.max_) }); + } + + int32_t GetMinMulResult(const RangeInfo &rhs) const + { + return std::min({ TryMul(min_, rhs.min_), TryMul(min_, rhs.max_), + TryMul(max_, rhs.min_), TryMul(max_, rhs.max_) }); + } + + int32_t TryMul(int32_t lhs, int32_t rhs) const + { + if (MaybeMulOverflow(lhs, rhs)) { + return INT32_MAX; + } + if (MaybeMulUnderflow(lhs, rhs)) { + return INT32_MIN; + } + return lhs * rhs; + } + + bool MaybeMulOverflowOrUnderflow(const RangeInfo &rhs) const + { + return MaybeMulOverflow(rhs) || MaybeMulUnderflow(rhs); + } + + bool MaybeMulUnderflow(const RangeInfo &rhs) const + { + return MaybeMulUnderflow(min_, rhs.max_) || MaybeMulUnderflow(max_, rhs.min_); + } + + bool MaybeMulOverflow(const RangeInfo &rhs) const + { + return MaybeMulOverflow(max_, rhs.max_) || MaybeMulOverflow(min_, rhs.min_); + } + + bool MaybeMulUnderflow(int32_t lhs, int32_t rhs) const + { + return (lhs > 0 && rhs < 0 && rhs < INT32_MIN / lhs) || (lhs < 0 && rhs > 0 && lhs < INT32_MIN / rhs); + } + + bool MaybeMulOverflow(int32_t lhs, int32_t rhs) const + { + return (lhs > 0 && rhs > 0 && lhs > INT32_MAX / rhs) || (lhs < 0 && rhs < 0 && lhs < INT32_MAX / rhs); + } + bool MaybeSubOverflow(const RangeInfo &rhs) const { return (rhs.min_ < 0) && (max_ > INT32_MAX + rhs.min_); @@ -173,6 +251,7 @@ public: RangeInfo operator- (const RangeInfo &rhs) const { + ASSERT(min_ <= max_ && rhs.min_ <= rhs.max_); int32_t nmax = MaybeSubOverflow(rhs) ? INT32_MAX : max_ - rhs.min_; int32_t nmin = MaybeSubUnderflow(rhs) ? INT32_MIN : min_ - rhs.max_; return RangeInfo(nmin, nmax); @@ -188,6 +267,8 @@ public: RangeInfo SHR(const RangeInfo &rhs) const { + ASSERT(min_ <= max_); + ASSERT(rhs.max_ == rhs.min_); if (MaybeShrOverflow(rhs)) { // assume no overflow occurs since overflow will lead to deopt return RangeInfo(0, std::max(0, GetMax())); @@ -202,6 +283,7 @@ public: RangeInfo ASHR(const RangeInfo &rhs) const { + ASSERT(min_ <= max_); ASSERT(rhs.max_ == rhs.min_); int32_t shift = rhs.max_ & 0x1f; // 0x1f : shift bits int32_t nmin = min_ >> shift; @@ -223,7 +305,7 @@ public: { return (min_ == INT32_MAX) && (max_ == INT32_MIN); } - + private: int32_t min_ {INT32_MIN}; int32_t max_ {INT32_MAX}; diff --git a/ecmascript/compiler/number_speculative_lowering.cpp b/ecmascript/compiler/number_speculative_lowering.cpp index 0750a4710099f9a7f6490ae6c96e3d64aa976628..b0e9054def5da8b4642bad4ef6b08d504d699f9a 100644 --- a/ecmascript/compiler/number_speculative_lowering.cpp +++ b/ecmascript/compiler/number_speculative_lowering.cpp @@ -13,7 +13,7 @@ * limitations under the License. */ -#include "ecmascript/compiler/gate_meta_data.h" +#include "ecmascript/compiler/share_gate_meta_data.h" #include "ecmascript/compiler/number_gate_info.h" #include "ecmascript/compiler/type.h" #include "ecmascript/compiler/type_mcr_lowering.h" @@ -32,14 +32,19 @@ void NumberSpeculativeLowering::Run() std::vector gateList; acc_.GetAllGates(gateList); for (auto gate : gateList) { - if (acc_.GetOpCode(gate) != OpCode::INDEX_CHECK) { - VisitGate(gate); - } else { - checkedGates_.push_back(gate); + auto op = acc_.GetOpCode(gate); + switch (op) { + case OpCode::RANGE_GUARD: { + rangeGuardGates_.push_back(gate); + break; + } + default: { + VisitGate(gate); + } } } - for (auto check : checkedGates_) { - VisitIndexCheck(check); + for (auto rangeGuard : rangeGuardGates_) { + VisitRangeGuard(rangeGuard); } } @@ -71,6 +76,31 @@ void NumberSpeculativeLowering::VisitGate(GateRef gate) VisitCallBuiltins(gate); break; } + case OpCode::LOAD_ELEMENT: { + VisitLoadElement(gate); + break; + } + case OpCode::INDEX_CHECK: { + VisitIndexCheck(gate); + break; + } + case OpCode::RANGE_CHECK_PREDICATE: { + VisitRangeCheckPredicate(gate); + break; + } + case OpCode::LOAD_ARRAY_LENGTH: + case OpCode::LOAD_TYPED_ARRAY_LENGTH: { + VisitLoadArrayLength(gate); + break; + } + case OpCode::LOAD_STRING_LENGTH: { + VisitLoadStringLength(gate); + break; + } + case OpCode::LOAD_PROPERTY: { + VisitLoadProperty(gate); + break; + } default: break; } @@ -79,6 +109,22 @@ void NumberSpeculativeLowering::VisitGate(GateRef gate) void NumberSpeculativeLowering::VisitTypedBinaryOp(GateRef gate) { Environment env(gate, circuit_, &builder_); + if (acc_.HasStringType(gate)) { + VisitStringBinaryOp(gate); + return; + } + + if (acc_.GetTypedBinaryOp(gate) != TypedBinOp::TYPED_STRICTEQ) { + if (acc_.HasPrimitiveNumberType(gate)) { + VisitNumberBinaryOp(gate); + } + } else { + VisitStrictEqual(gate); + } +} + +void NumberSpeculativeLowering::VisitStrictEqual(GateRef gate) +{ if (acc_.HasNumberType(gate)) { VisitNumberBinaryOp(gate); } else { @@ -313,8 +359,8 @@ void NumberSpeculativeLowering::VisitNumberDiv(GateRef gate) result = builder_.Int32DivWithCheck(left, right); acc_.SetMachineType(gate, MachineType::I32); } else { - builder_.Float64CheckRightIsZero(right); - result = builder_.BinaryArithmetic(circuit_->Fdiv(), MachineType::F64, left, right); + result = builder_.BinaryArithmetic(circuit_->Fdiv(), + MachineType::F64, left, right, GateType::NJSValue()); acc_.SetMachineType(gate, MachineType::F64); } acc_.SetGateType(gate, GateType::NJSValue()); @@ -337,15 +383,16 @@ void NumberSpeculativeLowering::VisitNumberMod(GateRef gate) } GateRef result = Circuit::NullGate(); if (gateType.IsIntType()) { - builder_.Int32CheckRightIsZero(right); + if (GetRange(right).MaybeZero()) { + builder_.Int32CheckRightIsZero(right); + } result = CalculateInts(left, right); UpdateRange(result, GetRange(gate)); acc_.SetMachineType(gate, MachineType::I32); } else { GateRef glue = acc_.GetGlueFromArgList(); - result = builder_.CallNGCRuntime( - glue, RTSTUB_ID(FloatMod), Gate::InvalidGateRef, {left, right}, - Circuit::NullGate()); + result = builder_.CallNGCRuntime(glue, RTSTUB_ID(FloatMod), + Gate::InvalidGateRef, {left, right}, Circuit::NullGate()); acc_.SetMachineType(gate, MachineType::F64); } acc_.SetGateType(gate, GateType::NJSValue()); @@ -357,7 +404,7 @@ void NumberSpeculativeLowering::VisitNumberMonocular(GateRef gate) { TypedUnaryAccessor accessor(acc_.TryGetValue(gate)); GateType type = accessor.GetTypeValue(); - ASSERT(type.IsNumberType()); + ASSERT(type.IsPrimitiveNumberType()); GateRef value = acc_.GetValueIn(gate, 0); GateRef result = Circuit::NullGate(); if (type.IsIntType()) { @@ -377,7 +424,7 @@ void NumberSpeculativeLowering::VisitNumberMonocular(GateRef gate) void NumberSpeculativeLowering::VisitNumberNot(GateRef gate) { - ASSERT(TypedUnaryAccessor(acc_.TryGetValue(gate)).GetTypeValue().IsNumberType()); + ASSERT(TypedUnaryAccessor(acc_.TryGetValue(gate)).GetTypeValue().IsPrimitiveNumberType()); GateRef value = acc_.GetValueIn(gate, 0); GateRef result = builder_.Int32Not(value); UpdateRange(result, GetRange(gate)); @@ -400,13 +447,17 @@ void NumberSpeculativeLowering::VisitIsTrueOrFalse(GateRef gate, bool flag) void NumberSpeculativeLowering::VisitBooleanJump(GateRef gate) { - TypedJumpOp jumpOp = acc_.GetTypedJumpAccessor(gate).GetTypedJumpOp(); + TypedJumpAccessor jumpAcc = acc_.GetTypedJumpAccessor(gate); + TypedJumpOp jumpOp = jumpAcc.GetTypedJumpOp(); ASSERT((jumpOp == TypedJumpOp::TYPED_JEQZ) || (jumpOp == TypedJumpOp::TYPED_JNEZ)); GateRef condition = acc_.GetValueIn(gate, 0); + uint32_t trueWeight = jumpAcc.GetTrueWeight(); + uint32_t falseWeight = jumpAcc.GetFalseWeight(); if (jumpOp == TypedJumpOp::TYPED_JEQZ) { + std::swap(trueWeight, falseWeight); condition = builder_.BoolNot(condition); } - GateRef ifBranch = builder_.Branch(acc_.GetState(gate), condition); + GateRef ifBranch = builder_.Branch(acc_.GetState(gate), condition, trueWeight, falseWeight); acc_.ReplaceGate(gate, ifBranch, acc_.GetDep(gate), Circuit::NullGate()); } @@ -477,27 +528,82 @@ void NumberSpeculativeLowering::VisitPhi(GateRef gate) } } +void NumberSpeculativeLowering::VisitRangeCheckPredicate(GateRef gate) +{ + acc_.SetGateType(gate, GateType::NJSValue()); + acc_.SetMachineType(gate, MachineType::I32); +} + void NumberSpeculativeLowering::VisitIndexCheck(GateRef gate) { - auto type = acc_.GetParamGateType(gate); - if (!tsManager_->IsArrayTypeKind(type)) { - // return checked index value + acc_.SetGateType(gate, GateType::NJSValue()); + acc_.SetMachineType(gate, MachineType::I32); +} + +void NumberSpeculativeLowering::VisitLoadArrayLength(GateRef gate) +{ + acc_.SetGateType(gate, GateType::NJSValue()); + acc_.SetMachineType(gate, MachineType::I32); +} + +void NumberSpeculativeLowering::VisitLoadStringLength(GateRef gate) +{ + acc_.SetGateType(gate, GateType::NJSValue()); + acc_.SetMachineType(gate, MachineType::I32); +} + +void NumberSpeculativeLowering::VisitLoadElement(GateRef gate) +{ + auto op = acc_.GetTypedLoadOp(gate); + switch (op) { + case TypedLoadOp::INT8ARRAY_LOAD_ELEMENT: + case TypedLoadOp::UINT8ARRAY_LOAD_ELEMENT: + case TypedLoadOp::UINT8CLAMPEDARRAY_LOAD_ELEMENT: + case TypedLoadOp::INT16ARRAY_LOAD_ELEMENT: + case TypedLoadOp::UINT16ARRAY_LOAD_ELEMENT: + case TypedLoadOp::INT32ARRAY_LOAD_ELEMENT: + acc_.SetMachineType(gate, MachineType::I32); + break; + case TypedLoadOp::FLOAT32ARRAY_LOAD_ELEMENT: + case TypedLoadOp::FLOAT64ARRAY_LOAD_ELEMENT: + acc_.SetMachineType(gate, MachineType::F64); + break; + default: + break; + } + acc_.SetGateType(gate, GateType::NJSValue()); +} + +void NumberSpeculativeLowering::VisitLoadProperty(GateRef gate) +{ + TypeInfo output = GetOutputType(gate); + if (output == TypeInfo::INT32 || output == TypeInfo::FLOAT64) { + Environment env(gate, circuit_, &builder_); + ASSERT(acc_.GetNumValueIn(gate) == 2); // 2: receiver, plr + GateRef receiver = acc_.GetValueIn(gate, 0); + GateRef propertyLookupResult = acc_.GetValueIn(gate, 1); + PropertyLookupResult plr(acc_.TryGetValue(propertyLookupResult)); + ASSERT(plr.IsLocal() || plr.IsFunction()); + + // Hole check? + GateRef result = Circuit::NullGate(); + if (output == TypeInfo::FLOAT64) { + result = builder_.LoadConstOffset(VariableType::FLOAT64(), receiver, plr.GetOffset()); + acc_.SetMachineType(gate, MachineType::F64); + } else { + result = builder_.LoadConstOffset(VariableType::INT32(), receiver, plr.GetOffset()); + acc_.SetMachineType(gate, MachineType::I32); + } acc_.SetGateType(gate, GateType::NJSValue()); - acc_.SetMachineType(gate, MachineType::I32); - return; + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result); } +} + +void NumberSpeculativeLowering::VisitRangeGuard(GateRef gate) +{ Environment env(gate, circuit_, &builder_); - GateRef length = acc_.GetValueIn(gate, 0); - GateRef index = acc_.GetValueIn(gate, 1); - RangeInfo indexRange = GetRange(index); - if (indexRange.GetMin() < 0) { - builder_.NegativeIndexCheck(index); - } - builder_.LargeIndexCheck(index, length); - // return checked index value - acc_.SetGateType(gate, GateType::NJSValue()); - acc_.SetMachineType(gate, MachineType::I32); - acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), index); + GateRef inputLength = acc_.GetValueIn(gate, 0); + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), inputLength); } template @@ -522,10 +628,14 @@ GateRef NumberSpeculativeLowering::CalculateInts(GateRef left, GateRef right) break; } case TypedBinOp::TYPED_MUL: + if (!leftRange.MaybeMulOverflowOrUnderflow(rightRange)) { + return builder_.Int32Mul(left, right); + } res = builder_.MulWithOverflow(left, right); break; case TypedBinOp::TYPED_MOD: { - return builder_.BinaryArithmetic(circuit_->Smod(), MachineType::I32, left, right); + return builder_.BinaryArithmetic(circuit_->Smod(), + MachineType::I32, left, right, GateType::NJSValue()); break; } default: @@ -712,7 +822,7 @@ GateRef NumberSpeculativeLowering::MonocularDouble(GateRef value) res = builder_.DoubleSub(value, builder_.Double(1)); break; case TypedUnOp::TYPED_NEG: - res = builder_.DoubleSub(builder_.Double(0), value); + res = builder_.DoubleMul(builder_.Double(-1), value); break; default: break; @@ -723,14 +833,20 @@ GateRef NumberSpeculativeLowering::MonocularDouble(GateRef value) void NumberSpeculativeLowering::UpdateRange(GateRef gate, const RangeInfo& range) { auto id = acc_.GetId(gate); - rangeInfos_.resize(id + 1, RangeInfo::ANY()); + if (id >= rangeInfos_.size()) { + rangeInfos_.resize(id + 1, RangeInfo::ANY()); + } rangeInfos_[id] = range; } RangeInfo NumberSpeculativeLowering::GetRange(GateRef gate) const { - ASSERT(!rangeInfos_[acc_.GetId(gate)].IsNone()); - return rangeInfos_[acc_.GetId(gate)]; + auto id = acc_.GetId(gate); + if (id >= rangeInfos_.size()) { + rangeInfos_.resize(id + 1, RangeInfo::ANY()); + } + ASSERT(!rangeInfos_[id].IsNone()); + return rangeInfos_[id]; } GateRef NumberSpeculativeLowering::GetConstInt32(int32_t v) @@ -739,4 +855,39 @@ GateRef NumberSpeculativeLowering::GetConstInt32(int32_t v) UpdateRange(val, RangeInfo(v, v)); return val; } + +void NumberSpeculativeLowering::VisitStringBinaryOp(GateRef gate) +{ + TypedBinOp Op = acc_.GetTypedBinaryOp(gate); + switch (Op) { + case TypedBinOp::TYPED_EQ: { + VisitStringCompare(gate); + break; + } + default: + LOG_COMPILER(FATAL) << "this branch is unreachable"; + UNREACHABLE(); + } +} + +template +void NumberSpeculativeLowering::VisitStringCompare(GateRef gate) +{ + GateRef left = acc_.GetValueIn(gate, 0); + GateRef right = acc_.GetValueIn(gate, 1); + + GateRef result; + switch (Op) { + case TypedBinOp::TYPED_EQ: + result = builder_.StringEqual(left, right); + break; + default: + LOG_COMPILER(FATAL) << "this branch is unreachable"; + UNREACHABLE(); + } + + acc_.SetMachineType(gate, MachineType::I1); + acc_.SetGateType(gate, GateType::NJSValue()); + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result); +} } // namespace panda::ecmascript diff --git a/ecmascript/compiler/number_speculative_lowering.h b/ecmascript/compiler/number_speculative_lowering.h index 6996efcf6dbe3534dbece05d831d15888d02b4f7..b9073dfffdc16a1b3ae4f2f06d9e0770650f32c9 100644 --- a/ecmascript/compiler/number_speculative_lowering.h +++ b/ecmascript/compiler/number_speculative_lowering.h @@ -18,7 +18,7 @@ #include "ecmascript/compiler/circuit_builder-inl.h" #include "ecmascript/compiler/gate_accessor.h" -#include "ecmascript/compiler/gate_meta_data.h" +#include "ecmascript/compiler/share_gate_meta_data.h" #include "ecmascript/compiler/number_gate_info.h" #include "ecmascript/compiler/type.h" #include "ecmascript/mem/chunk_containers.h" @@ -27,16 +27,17 @@ namespace panda::ecmascript::kungfu { class NumberSpeculativeLowering { public: - NumberSpeculativeLowering(Circuit* circuit, Chunk* chunk, TSManager* tsManager, - ChunkVector& typeInfos, ChunkVector& rangeInfos) - : circuit_(circuit), acc_(circuit), builder_(circuit), tsManager_(tsManager), - typeInfos_(typeInfos), rangeInfos_(rangeInfos), checkedGates_(chunk) {} + NumberSpeculativeLowering(Circuit* circuit, Chunk* chunk, ChunkVector& typeInfos, + ChunkVector& rangeInfos) + : circuit_(circuit), acc_(circuit), builder_(circuit), typeInfos_(typeInfos), + rangeInfos_(rangeInfos), rangeGuardGates_(chunk) {} void Run(); private: void VisitGate(GateRef gate); void VisitTypedBinaryOp(GateRef gate); void VisitNumberBinaryOp(GateRef gate); + void VisitStringBinaryOp(GateRef gate); void VisitTypedUnaryOp(GateRef gate); void VisitNumberNot(GateRef gate); void VisitTypedConditionJump(GateRef gate); @@ -44,7 +45,13 @@ private: void VisitPhi(GateRef gate); void VisitUndefinedStrictEq(GateRef gate); void VisitCallBuiltins(GateRef gate); + void VisitRangeGuard(GateRef gate); + void VisitRangeCheckPredicate(GateRef gate); void VisitIndexCheck(GateRef gate); + void VisitLoadArrayLength(GateRef gate); + void VisitLoadStringLength(GateRef gate); + void VisitLoadElement(GateRef gate); + void VisitLoadProperty(GateRef gate); template void VisitNumberCalculate(GateRef gate); @@ -61,6 +68,10 @@ private: void VisitNumberMod(GateRef gate); void VisitBooleanJump(GateRef gate); void VisitIsTrueOrFalse(GateRef gate, bool flag); + void VisitStrictEqual(GateRef gate); + + template + void VisitStringCompare(GateRef gate); template GateRef CalculateInts(GateRef left, GateRef right); @@ -79,6 +90,8 @@ private: template GateRef MonocularDouble(GateRef gate); + GateRef VisitStringEqual(GateRef left, GateRef right); + TypeInfo GetOutputType(GateRef gate) const { auto index = acc_.GetId(gate); @@ -93,10 +106,9 @@ private: Circuit* circuit_; GateAccessor acc_; CircuitBuilder builder_; - TSManager* tsManager_ {nullptr}; ChunkVector& typeInfos_; ChunkVector& rangeInfos_; - ChunkVector checkedGates_; + ChunkVector rangeGuardGates_; }; } // panda::ecmascript::kungfu #endif // ECMASCRIPT_COMPILER_NUMBER_SPECULATIVE_LOWERING_H diff --git a/ecmascript/compiler/number_speculative_retype.cpp b/ecmascript/compiler/number_speculative_retype.cpp index 7594054bb416840d9115cb9585defd700274312a..b4a68500fe02dbe7a19f320d4facc9c7a3d284f0 100644 --- a/ecmascript/compiler/number_speculative_retype.cpp +++ b/ecmascript/compiler/number_speculative_retype.cpp @@ -15,9 +15,10 @@ #include "ecmascript/compiler/number_speculative_retype.h" #include "ecmascript/compiler/circuit_builder-inl.h" -#include "ecmascript/compiler/gate_meta_data.h" +#include "ecmascript/compiler/share_gate_meta_data.h" #include "ecmascript/compiler/number_gate_info.h" #include "ecmascript/compiler/type.h" +#include namespace panda::ecmascript::kungfu { GateRef NumberSpeculativeRetype::SetOutputType(GateRef gate, GateType gateType) @@ -50,6 +51,32 @@ GateRef NumberSpeculativeRetype::SetOutputType(GateRef gate, PGOSampleType pgoTy return old == type ? Circuit::NullGate() : gate; } +GateRef NumberSpeculativeRetype::SetOutputType(GateRef gate, Representation rep) +{ + TypeInfo type = GetOutputTypeInfo(gate); + TypeInfo old = type; + if (rep == Representation::INT) { + type = TypeInfo::INT32; + } else if (rep == Representation::DOUBLE) { + type = TypeInfo::FLOAT64; + } else { + type = TypeInfo::TAGGED; + } + SetOutputTypeInfo(gate, type); + return old == type ? Circuit::NullGate() : gate; +} + +GateRef NumberSpeculativeRetype::SetOutputType(GateRef gate, TypeInfo type) +{ + TypeInfo old = GetOutputTypeInfo(gate); + SetOutputTypeInfo(gate, type); + return old == type ? Circuit::NullGate() : gate; +} +void NumberSpeculativeRetype::setState(NumberSpeculativeRetype::State state) +{ + state_ = state; +} + GateRef NumberSpeculativeRetype::VisitGate(GateRef gate) { OpCode op = acc_.GetOpCode(gate); @@ -60,16 +87,23 @@ GateRef NumberSpeculativeRetype::VisitGate(GateRef gate) return VisitTypedUnaryOp(gate); case OpCode::TYPED_CONDITION_JUMP: return VisitTypedConditionJump(gate); + case OpCode::RANGE_CHECK_PREDICATE: + return VisitRangeCheckPredicate(gate); case OpCode::INDEX_CHECK: return VisitIndexCheck(gate); case OpCode::LOAD_ARRAY_LENGTH: + case OpCode::LOAD_TYPED_ARRAY_LENGTH: return VisitLoadArrayLength(gate); + case OpCode::LOAD_STRING_LENGTH: + return VisitLoadStringLength(gate); case OpCode::LOAD_ELEMENT: return VisitLoadElement(gate); case OpCode::STORE_ELEMENT: return VisitStoreElement(gate); case OpCode::STORE_PROPERTY: return VisitStoreProperty(gate); + case OpCode::LOAD_PROPERTY: + return VisitLoadProperty(gate); case OpCode::VALUE_SELECTOR: return VisitPhi(gate); case OpCode::CONSTANT: @@ -82,22 +116,19 @@ GateRef NumberSpeculativeRetype::VisitGate(GateRef gate) return VisitFrameState(gate); case OpCode::CALL_GETTER: case OpCode::CALL_SETTER: - case OpCode::LOAD_PROPERTY: case OpCode::CONSTRUCT: case OpCode::TYPEDCALL: case OpCode::TYPEDFASTCALL: case OpCode::OBJECT_TYPE_CHECK: - return VisitWithConstantValue(gate, 1); // ignoreIndex + return VisitWithConstantValue(gate, PROPERTY_LOOKUP_RESULT_INDEX); case OpCode::LOOP_EXIT_VALUE: - return VisitLoopExitValue(gate); + case OpCode::RANGE_GUARD: + return VisitIntermediateValue(gate); case OpCode::JS_BYTECODE: case OpCode::PRIMITIVE_TYPE_CHECK: case OpCode::STABLE_ARRAY_CHECK: case OpCode::TYPED_ARRAY_CHECK: - case OpCode::JSCALLTARGET_TYPE_CHECK: - case OpCode::JSFASTCALLTARGET_TYPE_CHECK: - case OpCode::JSCALLTHISTARGET_TYPE_CHECK: - case OpCode::JSFASTCALLTHISTARGET_TYPE_CHECK: + case OpCode::TYPED_CALLTARGETCHECK_OP: case OpCode::TYPED_CALL_CHECK: case OpCode::HEAP_ALLOC: case OpCode::TYPED_NEW_ALLOCATE_THIS: @@ -108,6 +139,10 @@ GateRef NumberSpeculativeRetype::VisitGate(GateRef gate) case OpCode::FRAME_ARGS: case OpCode::SAVE_REGISTER: case OpCode::RESTORE_REGISTER: + case OpCode::LOAD_CONST_OFFSET: + case OpCode::STORE_CONST_OFFSET: + case OpCode::LEX_VAR_IS_HOLE_CHECK: + case OpCode::TYPE_OF_CHECK: return VisitOthers(gate); default: return Circuit::NullGate(); @@ -115,6 +150,21 @@ GateRef NumberSpeculativeRetype::VisitGate(GateRef gate) } GateRef NumberSpeculativeRetype::VisitTypedBinaryOp(GateRef gate) +{ + if (acc_.HasStringType(gate)) { + return VisitStringBinaryOp(gate); + } + + if (acc_.GetTypedBinaryOp(gate) != TypedBinOp::TYPED_STRICTEQ) { + if (acc_.HasPrimitiveNumberType(gate)) { + return VisitNumberBinaryOp(gate); + } + } + + return VisitStrictEqual(gate); +} + +GateRef NumberSpeculativeRetype::VisitStrictEqual(GateRef gate) { if (acc_.HasNumberType(gate)) { return VisitNumberBinaryOp(gate); @@ -167,7 +217,7 @@ GateRef NumberSpeculativeRetype::VisitConstant(GateRef gate) return Circuit::NullGate(); } -GateRef NumberSpeculativeRetype::VisitLoopExitValue(GateRef gate) +GateRef NumberSpeculativeRetype::VisitIntermediateValue(GateRef gate) { GateRef value = acc_.GetValueIn(gate, 0); TypeInfo valueInfo = GetOutputTypeInfo(value); @@ -179,6 +229,26 @@ GateRef NumberSpeculativeRetype::VisitLoopExitValue(GateRef gate) return Circuit::NullGate(); } +GateRef NumberSpeculativeRetype::VisitStringBinaryOp(GateRef gate) +{ + TypedBinOp op = acc_.GetTypedBinaryOp(gate); + switch (op) { + case TypedBinOp::TYPED_EQ: + return VisitStringCompare(gate); + default: + LOG_COMPILER(FATAL) << "this branch is unreachable"; + UNREACHABLE(); + } +} + +GateRef NumberSpeculativeRetype::VisitStringCompare(GateRef gate) +{ + if (IsRetype()) { + return SetOutputType(gate, GateType::BooleanType()); + } + return Circuit::NullGate(); +} + TypeInfo NumberSpeculativeRetype::GetOuputForPhi(GateRef gate, bool ignoreConstant) { size_t valueNum = acc_.GetNumValueIn(gate); @@ -190,12 +260,12 @@ TypeInfo NumberSpeculativeRetype::GetOuputForPhi(GateRef gate, bool ignoreConsta if (inputInfo == TypeInfo::NONE) { continue; } - if (ignoreConstant && acc_.GetOpCode(input) == OpCode::CONSTANT) { + if (ignoreConstant && acc_.IsConstantNumber(input)) { hasConstantInput = true; continue; } // use less general input as phi output - if (tempType == TypeInfo::NONE || tempType == TypeInfo::TAGGED) { + if (tempType == TypeInfo::NONE) { tempType = inputInfo; } else if (tempType != inputInfo) { tempType = TypeInfo::TAGGED; @@ -222,17 +292,20 @@ GateRef NumberSpeculativeRetype::VisitPhi(GateRef gate) } ASSERT(IsConvert()); size_t valueNum = acc_.GetNumValueIn(gate); - auto state = acc_.GetState(gate); - auto dependSelector = acc_.GetDependSelectorFromMerge(state); + auto merge = acc_.GetState(gate); + auto dependSelector = acc_.GetDependSelectorFromMerge(merge); TypeInfo output = GetOutputTypeInfo(gate); for (size_t i = 0; i < valueNum; ++i) { GateRef input = acc_.GetValueIn(gate, i); if (output == TypeInfo::TAGGED || output == TypeInfo::NONE) { input = ConvertToTagged(input); } else { + auto state = acc_.GetState(merge, i); auto depend = acc_.GetDep(dependSelector, i); Environment env(state, depend, {}, circuit_, &builder_); input = ConvertTaggedToNJSValue(input, output); + acc_.ReplaceStateIn(merge, builder_.GetState(), i); + acc_.ReplaceDependIn(dependSelector, builder_.GetDepend(), i); } acc_.ReplaceValueIn(gate, input, i); } @@ -306,7 +379,6 @@ GateRef NumberSpeculativeRetype::VisitTypedUnaryOp(GateRef gate) case TypedUnOp::TYPED_NOT: return VisitNumberNot(gate); case TypedUnOp::TYPED_ISFALSE: - return VisitIsTrueOrFalse(gate); case TypedUnOp::TYPED_ISTRUE: return VisitIsTrueOrFalse(gate); default: @@ -366,7 +438,17 @@ GateRef NumberSpeculativeRetype::VisitNumberShiftAndLogical(GateRef gate) Environment env(gate, circuit_, &builder_); GateType leftType = acc_.GetLeftType(gate); GateType rightType = acc_.GetRightType(gate); - ConvertForIntOperator(gate, leftType, rightType); + PGOSampleType sampleType = acc_.GetTypedBinaryType(gate); + if (sampleType.IsNumber()) { + if (sampleType.IsInt()) { + leftType = GateType::IntType(); + rightType = GateType::IntType(); + } else { + leftType = GateType::NumberType(); + rightType = GateType::NumberType(); + } + } + ConvertForShiftAndLogicalOperator(gate, leftType, rightType); } return Circuit::NullGate(); } @@ -375,7 +457,7 @@ GateRef NumberSpeculativeRetype::VisitNumberMonocular(GateRef gate) { TypedUnaryAccessor accessor(acc_.TryGetValue(gate)); GateType type = accessor.GetTypeValue(); - ASSERT(type.IsNumberType()); + ASSERT(type.IsPrimitiveNumberType()); if (type.IsIntType()) { return VisitIntMonocular(gate); } else { @@ -392,6 +474,8 @@ GateRef NumberSpeculativeRetype::VisitIntMonocular(GateRef gate) if (IsConvert()) { GateRef value = acc_.GetValueIn(gate, 0); acc_.ReplaceValueIn(gate, CheckAndConvertToInt32(value, GateType::IntType()), 0); + acc_.ReplaceStateIn(gate, builder_.GetState()); + acc_.ReplaceDependIn(gate, builder_.GetDepend()); } return Circuit::NullGate(); } @@ -406,6 +490,8 @@ GateRef NumberSpeculativeRetype::VisitDoubleMonocular(GateRef gate) TypedUnaryAccessor accessor(acc_.TryGetValue(gate)); GateRef value = acc_.GetValueIn(gate, 0); acc_.ReplaceValueIn(gate, CheckAndConvertToFloat64(value, accessor.GetTypeValue()), 0); + acc_.ReplaceStateIn(gate, builder_.GetState()); + acc_.ReplaceDependIn(gate, builder_.GetDepend()); } return Circuit::NullGate(); } @@ -414,7 +500,7 @@ GateRef NumberSpeculativeRetype::VisitIsTrueOrFalse(GateRef gate) { TypedUnaryAccessor accessor(acc_.TryGetValue(gate)); GateType valueType = accessor.GetTypeValue(); - ASSERT(valueType.IsNumberType() || valueType.IsBooleanType()); + ASSERT(valueType.IsPrimitiveNumberType()); if (IsRetype()) { return SetOutputType(gate, GateType::BooleanType()); } @@ -424,6 +510,8 @@ GateRef NumberSpeculativeRetype::VisitIsTrueOrFalse(GateRef gate) auto input = CheckAndConvertToBool(value, valueType); ResizeAndSetTypeInfo(input, TypeInfo::INT1); acc_.ReplaceValueIn(gate, input, 0); + acc_.ReplaceStateIn(gate, builder_.GetState()); + acc_.ReplaceDependIn(gate, builder_.GetDepend()); } return Circuit::NullGate(); } @@ -432,14 +520,17 @@ GateRef NumberSpeculativeRetype::VisitNumberNot(GateRef gate) { TypedUnaryAccessor accessor(acc_.TryGetValue(gate)); GateType valueType = accessor.GetTypeValue(); - ASSERT(valueType.IsNumberType()); + ASSERT(valueType.IsPrimitiveNumberType()); if (IsRetype()) { return SetOutputType(gate, GateType::IntType()); } if (IsConvert()) { Environment env(gate, circuit_, &builder_); GateRef value = acc_.GetValueIn(gate, 0); - acc_.ReplaceValueIn(gate, CheckAndConvertToInt32(value, valueType), 0); + acc_.ReplaceValueIn(gate, + CheckAndConvertToInt32(value, valueType, ConvertSupport::ENABLE, OpType::SHIFT_AND_LOGICAL), 0); + acc_.ReplaceStateIn(gate, builder_.GetState()); + acc_.ReplaceDependIn(gate, builder_.GetDepend()); } return Circuit::NullGate(); } @@ -461,6 +552,8 @@ GateRef NumberSpeculativeRetype::VisitBooleanJump(GateRef gate) } ResizeAndSetTypeInfo(input, TypeInfo::INT1); acc_.ReplaceValueIn(gate, input, 0); + acc_.ReplaceStateIn(gate, builder_.GetState()); + acc_.ReplaceDependIn(gate, builder_.GetDepend()); } return Circuit::NullGate(); } @@ -506,6 +599,8 @@ GateRef NumberSpeculativeRetype::VisitCallBuiltins(GateRef gate) GateRef input = acc_.GetValueIn(gate, i); acc_.ReplaceValueIn(gate, CheckAndConvertToFloat64(input, GateType::NumberType()), i); } + acc_.ReplaceStateIn(gate, builder_.GetState()); + acc_.ReplaceDependIn(gate, builder_.GetDepend()); } return Circuit::NullGate(); } @@ -559,11 +654,17 @@ GateRef NumberSpeculativeRetype::CheckAndConvertToBool(GateRef gate, GateType ga return gate; case TypeInfo::INT32: return builder_.ConvertInt32ToBool(gate); + case TypeInfo::UINT32: + return builder_.ConvertUInt32ToBool(gate); case TypeInfo::FLOAT64: return builder_.ConvertFloat64ToBool(gate); case TypeInfo::TAGGED: { if (gateType.IsBooleanType()) { return builder_.CheckTaggedBooleanAndConvertToBool(gate); + } else if (gateType.IsUndefinedType()) { + return builder_.CheckUndefinedAndConvertToBool(gate); + } else if (gateType.IsNullType()) { + return builder_.CheckNullAndConvertToBool(gate); } else { ASSERT(gateType.IsNumberType()); return builder_.CheckTaggedNumberAndConvertToBool(gate); @@ -639,6 +740,23 @@ void NumberSpeculativeRetype::ConvertForIntOperator(GateRef gate, GateType leftT acc_.ReplaceValueIn(gate, CheckAndConvertToInt32(left, leftType), 0); acc_.ReplaceValueIn(gate, CheckAndConvertToInt32(right, rightType), 1); + + acc_.ReplaceStateIn(gate, builder_.GetState()); + acc_.ReplaceDependIn(gate, builder_.GetDepend()); +} + +void NumberSpeculativeRetype::ConvertForShiftAndLogicalOperator(GateRef gate, GateType leftType, GateType rightType) +{ + GateRef left = acc_.GetValueIn(gate, 0); + GateRef right = acc_.GetValueIn(gate, 1); + GateRef cLeft = CheckAndConvertToInt32(left, leftType, ConvertSupport::ENABLE, OpType::SHIFT_AND_LOGICAL); + GateRef cRight = CheckAndConvertToInt32(right, rightType, ConvertSupport::ENABLE, OpType::SHIFT_AND_LOGICAL); + + acc_.ReplaceValueIn(gate, cLeft, 0); + acc_.ReplaceValueIn(gate, cRight, 1); + + acc_.ReplaceStateIn(gate, builder_.GetState()); + acc_.ReplaceDependIn(gate, builder_.GetDepend()); } void NumberSpeculativeRetype::ConvertForDoubleOperator(GateRef gate, GateType leftType, GateType rightType) @@ -648,6 +766,9 @@ void NumberSpeculativeRetype::ConvertForDoubleOperator(GateRef gate, GateType le acc_.ReplaceValueIn(gate, CheckAndConvertToFloat64(left, leftType), 0); acc_.ReplaceValueIn(gate, CheckAndConvertToFloat64(right, rightType), 1); + + acc_.ReplaceStateIn(gate, builder_.GetState()); + acc_.ReplaceDependIn(gate, builder_.GetDepend()); } GateRef NumberSpeculativeRetype::TryConvertConstant(GateRef gate, bool needInt32) @@ -655,19 +776,35 @@ GateRef NumberSpeculativeRetype::TryConvertConstant(GateRef gate, bool needInt32 if (acc_.GetOpCode(gate) != OpCode::CONSTANT) { return Circuit::NullGate(); } - GateType gateType = acc_.GetGateType(gate); - if (gateType.IsIntType()) { - int32_t rawValue = acc_.GetInt32FromConstant(gate); - double value = static_cast(rawValue); - return needInt32 ? builder_.Int32(rawValue) : builder_.Double(value); - } else if (gateType.IsDoubleType() && !needInt32) { - double rawValue = acc_.GetFloat64FromConstant(gate); + + if (acc_.GetGateType(gate).IsNJSValueType()) { + MachineType mType = acc_.GetMachineType(gate); + if (mType == MachineType::I32) { + int32_t rawValue = acc_.GetInt32FromConstant(gate); + double value = static_cast(rawValue); + return needInt32 ? builder_.Int32(rawValue) : builder_.Double(value); + } else if (mType == MachineType::F64 && !needInt32) { + double rawValue = acc_.GetFloat64FromConstant(gate); + return builder_.Double(rawValue); + } else { + return Circuit::NullGate(); + } + } + + JSTaggedValue value(acc_.GetConstantValue(gate)); + if (value.IsInt()) { + int32_t rawValue = value.GetInt(); + double doubleValue = static_cast(rawValue); + return needInt32 ? builder_.Int32(rawValue) : builder_.Double(doubleValue); + } else if (value.IsDouble() && !needInt32) { + double rawValue = value.GetDouble(); return builder_.Double(rawValue); } return Circuit::NullGate(); } -GateRef NumberSpeculativeRetype::CheckAndConvertToInt32(GateRef gate, GateType gateType) +GateRef NumberSpeculativeRetype::CheckAndConvertToInt32(GateRef gate, GateType gateType, ConvertSupport support, + OpType type) { auto result = TryConvertConstant(gate, true); if (result != Circuit::NullGate()) { @@ -677,18 +814,40 @@ GateRef NumberSpeculativeRetype::CheckAndConvertToInt32(GateRef gate, GateType g } TypeInfo output = GetOutputTypeInfo(gate); switch (output) { + case TypeInfo::INT1: + result = builder_.ConvertBoolToInt32(gate, support); + break; + case TypeInfo::CHAR: case TypeInfo::INT32: return gate; + case TypeInfo::UINT32: { + if (type != OpType::SHIFT_AND_LOGICAL) { + result = builder_.CheckUInt32AndConvertToInt32(gate); + } else { + result = gate; + } + break; + } case TypeInfo::FLOAT64: result = builder_.ConvertFloat64ToInt32(gate); break; case TypeInfo::TAGGED: { - ASSERT(gateType.IsNumberType()); if (gateType.IsIntType()) { result = builder_.CheckTaggedIntAndConvertToInt32(gate); } else if (gateType.IsDoubleType()) { result = builder_.CheckTaggedDoubleAndConvertToInt32(gate); + } else if (gateType.IsNullType()) { + result = builder_.CheckNullAndConvertToInt32(gate); + } else if (gateType.IsBooleanType()) { + result = builder_.CheckTaggedBooleanAndConvertToInt32(gate); + } else if (gateType.IsUndefinedType()) { + if (type == OpType::SHIFT_AND_LOGICAL) { + result = builder_.CheckUndefinedAndConvertToInt32(gate); + } else { + LOG_ECMA(FATAL) << "undefined cannot convert to int type"; + } } else { + ASSERT(gateType.IsNumberType()); result = builder_.CheckTaggedNumberAndConvertToInt32(gate); } break; @@ -703,7 +862,7 @@ GateRef NumberSpeculativeRetype::CheckAndConvertToInt32(GateRef gate, GateType g return result; } -GateRef NumberSpeculativeRetype::CheckAndConvertToFloat64(GateRef gate, GateType gateType) +GateRef NumberSpeculativeRetype::CheckAndConvertToFloat64(GateRef gate, GateType gateType, ConvertSupport support) { auto result = TryConvertConstant(gate, false); if (result != Circuit::NullGate()) { @@ -713,9 +872,15 @@ GateRef NumberSpeculativeRetype::CheckAndConvertToFloat64(GateRef gate, GateType } TypeInfo output = GetOutputTypeInfo(gate); switch (output) { + case TypeInfo::INT1: + result = builder_.ConvertBoolToFloat64(gate, support); + break; case TypeInfo::INT32: result = builder_.ConvertInt32ToFloat64(gate); break; + case TypeInfo::UINT32: + result = builder_.ConvertUInt32ToFloat64(gate); + break; case TypeInfo::FLOAT64: return gate; case TypeInfo::TAGGED: { @@ -723,7 +888,14 @@ GateRef NumberSpeculativeRetype::CheckAndConvertToFloat64(GateRef gate, GateType result = builder_.CheckTaggedIntAndConvertToFloat64(gate); } else if (gateType.IsDoubleType()) { result = builder_.CheckTaggedDoubleAndConvertToFloat64(gate); + } else if (gateType.IsNullType()) { + result = builder_.CheckNullAndConvertToFloat64(gate); + } else if (gateType.IsBooleanType()) { + result = builder_.CheckTaggedBooleanAndConvertToFloat64(gate); + } else if (gateType.IsUndefinedType()) { + result = builder_.CheckUndefinedAndConvertToFloat64(gate); } else { + ASSERT(gateType.IsNumberType()); result = builder_.CheckTaggedNumberAndConvertToFloat64(gate); } break; @@ -746,6 +918,8 @@ GateRef NumberSpeculativeRetype::CheckAndConvertToTagged(GateRef gate, GateType return builder_.ConvertBoolToTaggedBoolean(gate); case TypeInfo::INT32: return builder_.ConvertInt32ToTaggedInt(gate); + case TypeInfo::UINT32: + return builder_.ConvertUInt32ToTaggedNumber(gate); case TypeInfo::FLOAT64: return builder_.ConvertFloat64ToTaggedDouble(gate); case TypeInfo::TAGGED: { @@ -768,8 +942,12 @@ GateRef NumberSpeculativeRetype::ConvertToTagged(GateRef gate) return builder_.ConvertBoolToTaggedBoolean(gate); case TypeInfo::INT32: return builder_.ConvertInt32ToTaggedInt(gate); + case TypeInfo::UINT32: + return builder_.ConvertUInt32ToTaggedNumber(gate); case TypeInfo::FLOAT64: return builder_.ConvertFloat64ToTaggedDouble(gate); + case TypeInfo::CHAR: + return builder_.ConvertCharToEcmaString(gate); case TypeInfo::NONE: case TypeInfo::TAGGED: { return gate; @@ -781,6 +959,28 @@ GateRef NumberSpeculativeRetype::ConvertToTagged(GateRef gate) } } +GateRef NumberSpeculativeRetype::VisitRangeCheckPredicate(GateRef gate) +{ + if (IsRetype()) { + return SetOutputType(gate, GateType::IntType()); + } + + if (IsConvert()) { + Environment env(gate, circuit_, &builder_); + GateRef value0 = acc_.GetValueIn(gate, 0); + GateRef value1 = acc_.GetValueIn(gate, 1); + GateType value0Type = acc_.GetGateType(value0); + GateType value1Type = acc_.GetGateType(value1); + acc_.ReplaceValueIn(gate, CheckAndConvertToInt32(value0, value0Type), 0); + acc_.ReplaceValueIn(gate, CheckAndConvertToInt32(value1, value1Type), 1); + + acc_.ReplaceStateIn(gate, builder_.GetState()); + acc_.ReplaceDependIn(gate, builder_.GetDepend()); + } + + return Circuit::NullGate(); +} + GateRef NumberSpeculativeRetype::VisitIndexCheck(GateRef gate) { if (IsRetype()) { @@ -791,15 +991,13 @@ GateRef NumberSpeculativeRetype::VisitIndexCheck(GateRef gate) Environment env(gate, circuit_, &builder_); GateRef receiver = acc_.GetValueIn(gate, 0); GateRef index = acc_.GetValueIn(gate, 1); - GateType gateType = acc_.GetParamGateType(gate); GateType receiverType = acc_.GetGateType(receiver); GateType indexType = acc_.GetGateType(index); - if (tsManager_->IsArrayTypeKind(gateType)) { - // IndexCheck receive length at first value input. - ASSERT(receiverType.IsNumberType()); - acc_.ReplaceValueIn(gate, CheckAndConvertToInt32(receiver, receiverType), 0); - } + acc_.ReplaceValueIn(gate, CheckAndConvertToInt32(receiver, receiverType), 0); acc_.ReplaceValueIn(gate, CheckAndConvertToInt32(index, indexType), 1); + + acc_.ReplaceStateIn(gate, builder_.GetState()); + acc_.ReplaceDependIn(gate, builder_.GetDepend()); } return Circuit::NullGate(); @@ -814,10 +1012,37 @@ GateRef NumberSpeculativeRetype::VisitLoadArrayLength(GateRef gate) return Circuit::NullGate(); } +GateRef NumberSpeculativeRetype::VisitLoadStringLength(GateRef gate) +{ + if (IsRetype()) { + return SetOutputType(gate, GateType::IntType()); + } + + return Circuit::NullGate(); +} + GateRef NumberSpeculativeRetype::VisitLoadElement(GateRef gate) { if (IsRetype()) { - return SetOutputType(gate, GateType::AnyType()); + auto op = acc_.GetTypedLoadOp(gate); + switch (op) { + case TypedLoadOp::INT8ARRAY_LOAD_ELEMENT: + case TypedLoadOp::UINT8ARRAY_LOAD_ELEMENT: + case TypedLoadOp::UINT8CLAMPEDARRAY_LOAD_ELEMENT: + case TypedLoadOp::INT16ARRAY_LOAD_ELEMENT: + case TypedLoadOp::UINT16ARRAY_LOAD_ELEMENT: + case TypedLoadOp::INT32ARRAY_LOAD_ELEMENT: + return SetOutputType(gate, GateType::IntType()); + case TypedLoadOp::UINT32ARRAY_LOAD_ELEMENT: + return SetOutputType(gate, TypeInfo::UINT32); + case TypedLoadOp::FLOAT32ARRAY_LOAD_ELEMENT: + case TypedLoadOp::FLOAT64ARRAY_LOAD_ELEMENT: + return SetOutputType(gate, GateType::DoubleType()); + case TypedLoadOp::STRING_LOAD_ELEMENT: + return SetOutputType(gate, TypeInfo::CHAR); + default: + return SetOutputType(gate, GateType::AnyType()); + } } if (IsConvert()) { @@ -825,6 +1050,8 @@ GateRef NumberSpeculativeRetype::VisitLoadElement(GateRef gate) GateRef index = acc_.GetValueIn(gate, 1); GateType indexType = acc_.GetGateType(index); acc_.ReplaceValueIn(gate, CheckAndConvertToInt32(index, indexType), 1); + acc_.ReplaceStateIn(gate, builder_.GetState()); + acc_.ReplaceDependIn(gate, builder_.GetDepend()); } return Circuit::NullGate(); @@ -841,10 +1068,28 @@ GateRef NumberSpeculativeRetype::VisitStoreElement(GateRef gate) GateRef index = acc_.GetValueIn(gate, 1); GateType indexType = acc_.GetGateType(index); GateRef value = acc_.GetValueIn(gate, 2); - if (indexType.IsNumberType()) { - acc_.ReplaceValueIn(gate, CheckAndConvertToInt32(index, indexType), 1); + acc_.ReplaceValueIn(gate, CheckAndConvertToInt32(index, indexType), 1); + auto op = acc_.GetTypedStoreOp(gate); + switch (op) { + case TypedStoreOp::INT8ARRAY_STORE_ELEMENT: + case TypedStoreOp::UINT8ARRAY_STORE_ELEMENT: + case TypedStoreOp::UINT8CLAMPEDARRAY_STORE_ELEMENT: + case TypedStoreOp::INT16ARRAY_STORE_ELEMENT: + case TypedStoreOp::UINT16ARRAY_STORE_ELEMENT: + case TypedStoreOp::INT32ARRAY_STORE_ELEMENT: + case TypedStoreOp::UINT32ARRAY_STORE_ELEMENT: + acc_.ReplaceValueIn(gate, CheckAndConvertToInt32(value, GateType::NumberType()), 2); // 2: value idx + break; + case TypedStoreOp::FLOAT32ARRAY_STORE_ELEMENT: + case TypedStoreOp::FLOAT64ARRAY_STORE_ELEMENT: + acc_.ReplaceValueIn(gate, CheckAndConvertToFloat64(value, GateType::NumberType()), 2); // 2: value idx + break; + default: + acc_.ReplaceValueIn(gate, ConvertToTagged(value), 2); // 2: value idx + break; } - acc_.ReplaceValueIn(gate, ConvertToTagged(value), 2); // 2: index of value to be stored. + acc_.ReplaceStateIn(gate, builder_.GetState()); + acc_.ReplaceDependIn(gate, builder_.GetDepend()); } return Circuit::NullGate(); @@ -857,22 +1102,46 @@ GateRef NumberSpeculativeRetype::VisitStoreProperty(GateRef gate) } ASSERT(IsConvert()); GateRef value = acc_.GetValueIn(gate, 2); // 2: value - TypeInfo output = GetOutputTypeInfo(value); - switch (output) { - case TypeInfo::INT1: - case TypeInfo::INT32: - case TypeInfo::FLOAT64: + + Environment env(gate, circuit_, &builder_); + GateRef propertyLookupResult = acc_.GetValueIn(gate, 1); + PropertyLookupResult plr(acc_.TryGetValue(propertyLookupResult)); + if (plr.GetRepresentation() == Representation::DOUBLE) { + acc_.SetMetaData(gate, circuit_->StorePropertyNoBarrier()); + acc_.ReplaceValueIn( + gate, CheckAndConvertToFloat64(value, GateType::DoubleType(), ConvertSupport::DISABLE), 2); // 2: value + } else if (plr.GetRepresentation() == Representation::INT) { + acc_.SetMetaData(gate, circuit_->StorePropertyNoBarrier()); + acc_.ReplaceValueIn( + gate, CheckAndConvertToInt32(value, GateType::IntType(), ConvertSupport::DISABLE), 2); // 2: value + } else { + TypeInfo valueType = GetOutputTypeInfo(value); + if (valueType == TypeInfo::INT1 || valueType == TypeInfo::INT32 || valueType == TypeInfo::FLOAT64) { acc_.SetMetaData(gate, circuit_->StorePropertyNoBarrier()); - break; - default: - break; + } + acc_.ReplaceValueIn(gate, ConvertToTagged(value), 2); // 2: value } + GateRef receiver = acc_.GetValueIn(gate, 0); // receiver acc_.ReplaceValueIn(gate, ConvertToTagged(receiver), 0); - acc_.ReplaceValueIn(gate, ConvertToTagged(value), 2); // 2: value + acc_.ReplaceStateIn(gate, builder_.GetState()); + acc_.ReplaceDependIn(gate, builder_.GetDepend()); return Circuit::NullGate(); } +GateRef NumberSpeculativeRetype::VisitLoadProperty(GateRef gate) +{ + if (IsRetype()) { + GateRef propertyLookupResult = acc_.GetValueIn(gate, 1); + PropertyLookupResult plr(acc_.TryGetValue(propertyLookupResult)); + return SetOutputType(gate, plr.GetRepresentation()); + } + + ASSERT(IsConvert()); + + return VisitWithConstantValue(gate, PROPERTY_LOOKUP_RESULT_INDEX); // ignoreIndex +} + GateRef NumberSpeculativeRetype::VisitTypeConvert(GateRef gate) { GateRef input = acc_.GetValueIn(gate, 0); @@ -928,15 +1197,10 @@ GateRef NumberSpeculativeRetype::VisitNumberMod(GateRef gate) return Circuit::NullGate(); } -void NumberSpeculativeRetype::Run() +GateRef NumberSpeculativeRetypeManager::VisitGate(GateRef gate) { - // visit gate in RPO, propagate use infos and - // reset the machine type of number operator gate and related phi, - // if some tagged phi is used as native value, change it to native phi. - state_ = State::Retype; - VisitGraph(); - state_ = State::Convert; - VisitGraph(); + retype_->setState(state_); + return retype_->VisitGate(gate); } } // namespace panda::ecmascript::kungfu diff --git a/ecmascript/compiler/number_speculative_retype.h b/ecmascript/compiler/number_speculative_retype.h index 6e727146b8eb79c74067b8f43f96877eb8d6c5ad..fe7adf0ac8bb809577fe7370451ce7ce9e442246 100644 --- a/ecmascript/compiler/number_speculative_retype.h +++ b/ecmascript/compiler/number_speculative_retype.h @@ -17,28 +17,33 @@ #define ECMASCRIPT_COMPILER_NUMBER_SPECULATIVE_RETYPE_H #include "ecmascript/compiler/circuit_builder.h" +#include "ecmascript/compiler/combined_pass_visitor.h" #include "ecmascript/compiler/gate_accessor.h" -#include "ecmascript/compiler/gate_meta_data.h" -#include "ecmascript/compiler/graph_visitor.h" +#include "ecmascript/compiler/share_gate_meta_data.h" #include "ecmascript/compiler/number_gate_info.h" #include "ecmascript/compiler/type.h" #include "ecmascript/mem/chunk_containers.h" #include "ecmascript/ts_types/ts_manager.h" namespace panda::ecmascript::kungfu { -class NumberSpeculativeRetype : public GraphVisitor { +class NumberSpeculativeRetype { public: - NumberSpeculativeRetype(Circuit *circuit, Chunk* chunk, TSManager* tsManager, ChunkVector& typeInfos) - : GraphVisitor(circuit, chunk), acc_(circuit), builder_(circuit), - tsManager_(tsManager), typeInfos_(typeInfos) {} - void Run(); - GateRef VisitGate(GateRef gate); - -private: enum class State { Retype, Convert, }; + NumberSpeculativeRetype(Circuit* circuit, Chunk* chunk, ChunkVector& typeInfos) + : circuit_(circuit), acc_(circuit), builder_(circuit), + typeInfos_(typeInfos), chunk_(chunk) {} + GateRef VisitGate(GateRef gate); + void setState(NumberSpeculativeRetype::State state); + +private: + + enum class OpType { + NORMAL, + SHIFT_AND_LOGICAL, + }; bool IsRetype() const { @@ -52,10 +57,13 @@ private: GateRef SetOutputType(GateRef gate, PGOSampleType type); GateRef SetOutputType(GateRef gate, GateType type); + GateRef SetOutputType(GateRef gate, Representation rep); + GateRef SetOutputType(GateRef gate, TypeInfo type); GateRef VisitPhi(GateRef gate); GateRef VisitConstant(GateRef gate); GateRef VisitTypedBinaryOp(GateRef gate); GateRef VisitNumberBinaryOp(GateRef gate); + GateRef VisitStringBinaryOp(GateRef gate); GateRef VisitUndefinedStrictEq(GateRef gate); GateRef VisitTypedUnaryOp(GateRef gate); @@ -71,11 +79,14 @@ private: GateRef VisitNumberShiftAndLogical(GateRef gate); GateRef VisitNumberMod(GateRef gate); GateRef VisitBooleanJump(GateRef gate); + GateRef VisitRangeCheckPredicate(GateRef gate); GateRef VisitIndexCheck(GateRef gate); GateRef VisitLoadArrayLength(GateRef gate); + GateRef VisitLoadStringLength(GateRef gate); GateRef VisitLoadElement(GateRef gate); GateRef VisitStoreElement(GateRef gate); GateRef VisitStoreProperty(GateRef gate); + GateRef VisitLoadProperty(GateRef gate); GateRef VisitNumberRelated(GateRef gate); GateRef VisitCallBuiltins(GateRef gate); GateRef VisitOthers(GateRef gate); @@ -83,15 +94,20 @@ private: GateRef VisitFrameState(GateRef gate); GateRef VisitIsTrueOrFalse(GateRef gate); GateRef VisitWithConstantValue(GateRef gate, size_t ignoreIndex); - GateRef VisitLoopExitValue(GateRef gate); + GateRef VisitIntermediateValue(GateRef gate); + GateRef VisitStrictEqual(GateRef gate); + + GateRef VisitStringCompare(GateRef gate); void ConvertForBinaryOp(GateRef gate); void ConvertForCompareOp(GateRef gate); void ConvertForIntOperator(GateRef gate, GateType leftType, GateType rightType); + void ConvertForShiftAndLogicalOperator(GateRef gate, GateType leftType, GateType rightType); void ConvertForDoubleOperator(GateRef gate, GateType leftType, GateType rightType); - GateRef CheckAndConvertToInt32(GateRef gate, GateType gateType); - GateRef CheckAndConvertToFloat64(GateRef gate, GateType gateType); + GateRef CheckAndConvertToInt32(GateRef gate, GateType gateType, ConvertSupport support = ConvertSupport::ENABLE, + OpType type = OpType::NORMAL); + GateRef CheckAndConvertToFloat64(GateRef gate, GateType gateType, ConvertSupport support = ConvertSupport::ENABLE); GateRef CheckAndConvertToTagged(GateRef gate, GateType gateType); GateRef CheckAndConvertToBool(GateRef gate, GateType gateType); GateRef ConvertToTagged(GateRef gate); @@ -123,11 +139,25 @@ private: typeInfos_[index] = info; } + static constexpr size_t PROPERTY_LOOKUP_RESULT_INDEX = 1; + Circuit *circuit_ {nullptr}; GateAccessor acc_; CircuitBuilder builder_; - TSManager* tsManager_ {nullptr}; ChunkVector& typeInfos_; State state_ {0}; + [[maybe_unused]] Chunk *chunk_ {nullptr}; }; + +class NumberSpeculativeRetypeManager : public PassVisitor { +public: + NumberSpeculativeRetypeManager(Circuit* circuit, RPOVisitor* visitor, Chunk* chunk, + NumberSpeculativeRetype* retype, NumberSpeculativeRetype::State state) + : PassVisitor(circuit, chunk, visitor), retype_(retype), state_(state) {} + GateRef VisitGate(GateRef gate) override; +private: + NumberSpeculativeRetype* retype_; + NumberSpeculativeRetype::State state_; +}; + } // panda::ecmascript::kungfu #endif // ECMASCRIPT_COMPILER_NUMBER_SPECULATIVE_RETYPE_H diff --git a/ecmascript/compiler/number_speculative_runner.cpp b/ecmascript/compiler/number_speculative_runner.cpp index c4b702936827be208502697f008afa1de17df8d2..7cea26cdbb1bddf4846f55646fbea8df495ee84a 100644 --- a/ecmascript/compiler/number_speculative_runner.cpp +++ b/ecmascript/compiler/number_speculative_runner.cpp @@ -13,6 +13,7 @@ * limitations under the License. */ +#include "ecmascript/compiler/combined_pass_visitor.h" #include "ecmascript/compiler/number_gate_info.h" #include "ecmascript/compiler/number_speculative_lowering.h" #include "ecmascript/compiler/number_speculative_runner.h" @@ -21,10 +22,40 @@ namespace panda::ecmascript::kungfu { void NumberSpeculativeRunner::Run() { + CombinedPassVisitor rangeGuardVisitor(circuit_, enableLog_, methodName_, chunk_); + RangeGuard rangeGuard(circuit_, &rangeGuardVisitor, chunk_); + rangeGuardVisitor.AddPass(&rangeGuard); + rangeGuardVisitor.VisitGraph(); + + if (IsLogEnabled()) { + LOG_COMPILER(INFO) << ""; + LOG_COMPILER(INFO) << "\033[34m" + << "====================" + << " After range guard " + << "[" << GetMethodName() << "]" + << "====================" + << "\033[0m"; + circuit_->PrintAllGatesWithBytecode(); + LOG_COMPILER(INFO) << "\033[34m" << "========================= End ==========================" << "\033[0m"; + } + auto maxId = circuit_->GetMaxGateId(); typeInfos_.resize(maxId + 1, TypeInfo::NONE); - NumberSpeculativeRetype retype(circuit_, chunk_, tsManager_, typeInfos_); - retype.Run(); + + // visit gate in RPO, propagate use infos and + // reset the machine type of number operator gate and related phi, + // if some tagged phi is used as native value, change it to native phi. + NumberSpeculativeRetype retype(circuit_, chunk_, typeInfos_); + CombinedPassVisitor retypeVisitor(circuit_, enableLog_, methodName_, chunk_); + NumberSpeculativeRetypeManager retypePhase(circuit_, &retypeVisitor, chunk_, + &retype, NumberSpeculativeRetype::State::Retype); + retypeVisitor.AddPass(&retypePhase); + retypeVisitor.VisitGraph(); + CombinedPassVisitor convertVisitor(circuit_, enableLog_, methodName_, chunk_); + NumberSpeculativeRetypeManager convertPhase(circuit_, &convertVisitor, + chunk_, &retype, NumberSpeculativeRetype::State::Convert); + convertVisitor.AddPass(&convertPhase); + convertVisitor.VisitGraph(); if (IsLogEnabled()) { LOG_COMPILER(INFO) << ""; @@ -40,8 +71,11 @@ void NumberSpeculativeRunner::Run() maxId = circuit_->GetMaxGateId(); rangeInfos_.resize(maxId + 1, RangeInfo::NONE()); - RangeAnalysis rangeAnalysis(circuit_, chunk_, typeInfos_, rangeInfos_); - rangeAnalysis.Run(); + CombinedPassVisitor rangeAnalysisVisitor(circuit_, enableLog_, methodName_, chunk_); + RangeAnalysis rangeAnalysis(circuit_, &rangeAnalysisVisitor, chunk_, typeInfos_, rangeInfos_, IsOnHeap()); + rangeAnalysisVisitor.AddPass(&rangeAnalysis); + rangeAnalysisVisitor.VisitGraph(); + if (IsLogEnabled()) { LOG_COMPILER(INFO) << ""; LOG_COMPILER(INFO) << "\033[34m" @@ -54,7 +88,7 @@ void NumberSpeculativeRunner::Run() LOG_COMPILER(INFO) << "\033[34m" << "========================= End ==========================" << "\033[0m"; } - NumberSpeculativeLowering lowering(circuit_, chunk_, tsManager_, typeInfos_, rangeInfos_); + NumberSpeculativeLowering lowering(circuit_, chunk_, typeInfos_, rangeInfos_); lowering.Run(); if (IsLogEnabled()) { LOG_COMPILER(INFO) << ""; diff --git a/ecmascript/compiler/number_speculative_runner.h b/ecmascript/compiler/number_speculative_runner.h index fbf02010a53f61840629788cd974d6d0c0447f51..27b6c624328475ef3cb98d64bfb8e98803f8e0f0 100644 --- a/ecmascript/compiler/number_speculative_runner.h +++ b/ecmascript/compiler/number_speculative_runner.h @@ -17,16 +17,17 @@ #define ECMASCRIPT_COMPILER_NUMBER_SPECULATIVE_RUNNER_H #include "ecmascript/compiler/number_speculative_lowering.h" +#include "ecmascript/compiler/pass_manager.h" #include "ecmascript/compiler/number_speculative_retype.h" +#include "ecmascript/compiler/range_guard.h" #include "ecmascript/ts_types/ts_manager.h" namespace panda::ecmascript::kungfu { class NumberSpeculativeRunner { public: - NumberSpeculativeRunner(Circuit *circuit, TSManager* tsManager, - bool enableLog, const std::string& name, Chunk* chunk) - : circuit_(circuit), acc_(circuit), tsManager_(tsManager), enableLog_(enableLog), - methodName_(name), chunk_(chunk), typeInfos_(chunk), rangeInfos_(chunk) {} + NumberSpeculativeRunner(Circuit *circuit, bool enableLog, const std::string& name, Chunk* chunk, bool onHeapCheck) + : circuit_(circuit), acc_(circuit), enableLog_(enableLog), methodName_(name), + chunk_(chunk), typeInfos_(chunk), rangeInfos_(chunk), onHeapCheck_(onHeapCheck) {} ~NumberSpeculativeRunner() = default; void Run(); @@ -36,6 +37,11 @@ private: return enableLog_; } + bool IsOnHeap() const + { + return onHeapCheck_; + } + const std::string& GetMethodName() const { return methodName_; @@ -43,12 +49,12 @@ private: Circuit *circuit_ {nullptr}; GateAccessor acc_; - TSManager* tsManager_ {nullptr}; bool enableLog_ {false}; std::string methodName_; Chunk *chunk_ {nullptr}; ChunkVector typeInfos_; ChunkVector rangeInfos_; + bool onHeapCheck_ {false}; }; } // panda::ecmascript::kungfu #endif // ECMASCRIPT_COMPILER_NUMBER_SPECULATIVE_RUNNER_H diff --git a/ecmascript/compiler/object_access_helper.cpp b/ecmascript/compiler/object_access_helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c83c3d53ab1bc8f650fff54ad7f84dbe1b7e10c5 --- /dev/null +++ b/ecmascript/compiler/object_access_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/compiler/object_access_helper.h" +#include "ecmascript/ts_types/ts_type.h" + +namespace panda::ecmascript::kungfu { +bool ObjectAccessHelper::Compute(ChunkVector &infos) +{ + ASSERT(infos.empty()); + bool result = false; + ObjectAccessInfo info(type_); + TSTypeKind kind = tsManager_->GetTypeKind(type_.GetGTRef()); + switch (kind) { + case TSTypeKind::CLASS_INSTANCE: + result = ComputeForClassInstance(info); + break; + case TSTypeKind::CLASS: + case TSTypeKind::OBJECT: + result = ComputeForClassOrObject(info); + break; + case TSTypeKind::UNION: + return ComputePolymorphism(infos); + default: + return false; + } + + infos.emplace_back(info); + return result; +} + +bool ObjectAccessHelper::ComputeForClassInstance(ObjectAccessInfo &info) +{ + int hclassIndex = tsManager_->GetHClassIndexByInstanceGateType(info.Type()); + if (hclassIndex == -1) { + return false; + } + + JSHClass *hclass = JSHClass::Cast(tsManager_->GetValueFromCache(hclassIndex).GetTaggedObject()); + if (!hclass->HasTSSubtyping()) { + return false; + } + + PropertyLookupResult plr = JSHClass::LookupPropertyInAotHClass(thread_, hclass, key_); + info.Set(hclassIndex, plr); + + if (IsLoading()) { + return plr.IsFound(); + } + + return (plr.IsFound() && !plr.IsFunction()); +} + +bool ObjectAccessHelper::ComputeForClassOrObject(ObjectAccessInfo &info) +{ + GateType type = info.Type(); + int hclassIndex = -1; + if (tsManager_->IsClassTypeKind(type)) { + hclassIndex = tsManager_->GetConstructorHClassIndexByClassGateType(type); + } else if (tsManager_->IsObjectTypeKind(type)) { + hclassIndex = tsManager_->GetHClassIndexByObjectType(type); + } + + if (hclassIndex == -1) { + return false; + } + + JSHClass *hclass = JSHClass::Cast(tsManager_->GetValueFromCache(hclassIndex).GetTaggedObject()); + PropertyLookupResult plr = JSHClass::LookupPropertyInAotHClass(thread_, hclass, key_); + info.Set(hclassIndex, plr); + + if (IsLoading()) { + return (plr.IsFound() && plr.IsLocal() && !plr.IsAccessor()); + } + + return (plr.IsFound() && plr.IsLocal() && !plr.IsAccessor() && plr.IsWritable()); +} + +bool ObjectAccessHelper::ComputePolymorphism(ChunkVector &infos) +{ + DISALLOW_GARBAGE_COLLECTION; + ASSERT(tsManager_->IsUnionTypeKind(type_)); + JSHandle unionType(tsManager_->GetTSType(type_.GetGTRef())); + TaggedArray *components = TaggedArray::Cast(unionType->GetComponents().GetTaggedObject()); + uint32_t length = components->GetLength(); + for (uint32_t i = 0; i < length; ++i) { + GlobalTSTypeRef gt(components->Get(i).GetInt()); + GateType type = GateType(gt); + ObjectAccessInfo info(type); + TSTypeKind kind = tsManager_->GetTypeKind(gt); + switch (kind) { + case TSTypeKind::CLASS_INSTANCE: { + if (!ComputeForClassInstance(info)) { + return false; + } + break; + } + case TSTypeKind::CLASS: + case TSTypeKind::OBJECT: { + if (!ComputeForClassOrObject(info)) { + return false; + } + break; + } + default: + return false; + } + infos.emplace_back(info); + } + + return infos.size() <= POLYMORPHIC_MAX_SIZE; +} +} // namespace panda::ecmascript::kungfu diff --git a/ecmascript/compiler/object_access_helper.h b/ecmascript/compiler/object_access_helper.h new file mode 100644 index 0000000000000000000000000000000000000000..ef451ef541b260b595a6d76bcd3154913863f3f1 --- /dev/null +++ b/ecmascript/compiler/object_access_helper.h @@ -0,0 +1,114 @@ +/* + * 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_COMPILER_OBJECT_ACCESS_HELPER_H +#define ECMASCRIPT_COMPILER_OBJECT_ACCESS_HELPER_H + +#include "ecmascript/ts_types/ts_manager.h" + +namespace panda::ecmascript::kungfu { +class ObjectAccessInfo final { +public: + explicit ObjectAccessInfo(GateType type, int hclassIndex = -1, PropertyLookupResult plr = PropertyLookupResult()) + : type_(type), hclassIndex_(hclassIndex), plr_(plr) {} + ~ObjectAccessInfo() = default; + + void Set(int hclassIndex, PropertyLookupResult plr) + { + hclassIndex_ = hclassIndex; + plr_ = plr; + } + + GateType Type() const + { + return type_; + } + + int HClassIndex() const + { + return hclassIndex_; + } + + PropertyLookupResult Plr() const + { + return plr_; + } + +private: + GateType type_ {GateType::AnyType()}; + int hclassIndex_ {-1}; + PropertyLookupResult plr_ {}; +}; + +// An auxiliary class serving TSHCRLowering, used for named object property access, +// invoking TSManager and HClass. +class ObjectAccessHelper final { +public: + static constexpr size_t POLYMORPHIC_MAX_SIZE = 4; + + enum AccessMode : uint8_t { + LOAD = 0, + STORE + }; + + explicit ObjectAccessHelper(TSManager *tsManager, AccessMode mode, GateRef receiver, GateType type, + JSTaggedValue key, GateRef value) + : tsManager_(tsManager), + thread_(tsManager_->GetThread()), + mode_(mode), + receiver_(receiver), + type_(type), + key_(key), + value_(value) {} + + ~ObjectAccessHelper() = default; + + bool Compute(ChunkVector &infos); + + AccessMode GetAccessMode() const + { + return mode_; + } + + bool IsLoading() const + { + return mode_ == AccessMode::LOAD; + } + + GateRef GetReceiver() const + { + return receiver_; + } + + GateRef GetValue() const + { + return value_; + } + +private: + bool ComputeForClassInstance(ObjectAccessInfo &info); + bool ComputeForClassOrObject(ObjectAccessInfo &info); + bool ComputePolymorphism(ChunkVector &infos); + + TSManager *tsManager_ {nullptr}; + const JSThread *thread_ {nullptr}; + AccessMode mode_ {}; + GateRef receiver_ {Circuit::NullGate()}; + GateType type_ {GateType::AnyType()}; + JSTaggedValue key_ {JSTaggedValue::Hole()}; + GateRef value_ {Circuit::NullGate()}; +}; +} // panda::ecmascript::kungfu +#endif // ECMASCRIPT_COMPILER_OBJECT_ACCESS_HELPER_H diff --git a/ecmascript/compiler/ohos_pkg_args.h b/ecmascript/compiler/ohos_pkg_args.h new file mode 100644 index 0000000000000000000000000000000000000000..aa118c0b7a81b7c655250ac0887999f91b72e21a --- /dev/null +++ b/ecmascript/compiler/ohos_pkg_args.h @@ -0,0 +1,188 @@ +/* + * 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_COMPILER_OHOS_PKG_ARGS_H +#define ECMASCRIPT_COMPILER_OHOS_PKG_ARGS_H + +#include + +#include "ecmascript/ecma_vm.h" +#include "ecmascript/base/json_parser.h" +#include "ecmascript/js_array.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/log_wrapper.h" +#include "ecmascript/mem/c_string.h" +#include "ecmascript/platform/file.h" + +namespace panda::ecmascript::kungfu { +class OhosPkgArgs { +public: + constexpr static const char *const KEY_BUNDLE_NAME = "bundleName"; + constexpr static const char *const KEY_MODULE_NAME = "moduleName"; + constexpr static const char *const KEY_PKG_PATH = "pkgPath"; + constexpr static const char *const KEY_FILE_NAME = "abcName"; + constexpr static const char *const KEY_ABC_OFFSET = "abcOffset"; + constexpr static const char *const KEY_ABC_SIZE = "abcSize"; + + OhosPkgArgs() = default; + + static bool ParseListFromJson(EcmaVM *vm, const std::string &jsonInfo, std::list &infoList) + { + LocalScope scope(vm); + ObjectFactory *factory = vm->GetFactory(); + ecmascript::base::Utf8JsonParser parser(vm->GetJSThread()); + + JSHandle handleMsg(factory->NewFromASCII(jsonInfo.c_str())); + JSHandle handleStr(JSTaggedValue::ToString(vm->GetAssociatedJSThread(), handleMsg)); // JSON Object + JSHandle result = parser.Parse(*handleStr); + JSTaggedValue resultValue(static_cast(result->GetRawData())); + if (!resultValue.IsArray(vm->GetJSThread())) { + LOG_COMPILER(ERROR) << "Pkg list info parse failed. result is not an array. jsonData: " << jsonInfo.c_str(); + return false; + } + JSHandle valueHandle(vm->GetJSThread(), resultValue); + JSHandle elements(vm->GetJSThread(), valueHandle->GetElements()); + for (uint32_t i = 0; i < elements->GetLength(); i++) { + OhosPkgArgs pkgInfo; + JSHandle entry(vm->GetJSThread(), elements->Get(i)); + if (entry->IsHole()) { + continue; + } + JSTaggedValue entryValue(static_cast(entry->GetRawData())); + JSHandle entryHandle(vm->GetJSThread(), entryValue); + if (!pkgInfo.ParseFromJsObject(vm, entryHandle)) { + LOG_COMPILER(ERROR) << "Pkg list entry info parse failed. jsonData: " << jsonInfo.c_str(); + return false; + } + infoList.emplace_back(pkgInfo); + } + return true; + } + + bool ParseFromJson(EcmaVM *vm, const std::string &jsonInfo) + { + LocalScope scope(vm); + ObjectFactory *factory = vm->GetFactory(); + ecmascript::base::Utf8JsonParser parser(vm->GetJSThread()); + + JSHandle handleMsg(factory->NewFromASCII(jsonInfo.c_str())); + JSHandle handleStr(JSTaggedValue::ToString(vm->GetAssociatedJSThread(), handleMsg)); // JSON Object + JSHandle result = parser.Parse(*handleStr); + JSTaggedValue resultValue(static_cast(result->GetRawData())); + if (!resultValue.IsECMAObject()) { + LOG_COMPILER(ERROR) << "Pkg info parse failed. result is not an object. jsonData: " << jsonInfo.c_str(); + return false; + } + JSHandle valueHandle(vm->GetJSThread(), resultValue); + return ParseFromJsObject(vm, valueHandle); + } + + bool ParseFromJsObject(EcmaVM *vm, JSHandle &valueHandle) + { + LocalScope scope(vm); + JSHandle nameList(JSObject::EnumerableOwnNames(vm->GetJSThread(), valueHandle)); + for (uint32_t i = 0; i < nameList->GetLength(); i++) { + JSHandle key(vm->GetJSThread(), nameList->Get(i)); + JSHandle value = JSObject::GetProperty(vm->GetJSThread(), valueHandle, key).GetValue(); + if (!key->IsString() || !value->IsString()) { + LOG_COMPILER(ERROR) << "Pkg info parse from js object failed. key and value must be string type."; + return false; + } + UpdateProperty(ConvertToString(*JSTaggedValue::ToString(vm->GetJSThread(), key)).c_str(), + ConvertToString(*JSTaggedValue::ToString(vm->GetJSThread(), value)).c_str()); + } + return Valid(); + } + + void UpdateProperty(const char *key, const char *value) + { + if (strcmp(key, KEY_BUNDLE_NAME) == 0) { + bundleName_ = value; + } else if (strcmp(key, KEY_MODULE_NAME) == 0) { + moduleName_ = value; + } else if (strcmp(key, KEY_PKG_PATH) == 0) { + pkgPath_ = value; + } else if (strcmp(key, KEY_FILE_NAME) == 0) { + abcName_ = value; + } else if (strcmp(key, KEY_ABC_OFFSET) == 0) { + char *str = nullptr; + abcOffset_ = static_cast(strtol(value, &str, 0)); + } else if (strcmp(key, KEY_ABC_SIZE) == 0) { + char *str = nullptr; + abcSize_ = static_cast(strtol(value, &str, 0)); + } else { + LOG_COMPILER(ERROR) << "Unknown keyword when parse pkg info. key: " << key << ", value: " << value; + } + } + + bool Valid() const + { + if (!base::StringHelper::EndsWith(abcName_, ".abc")) { + LOG_COMPILER(ERROR) << "The last argument must be abc file, but now is: " << abcName_ << std::endl; + return false; + } + return !bundleName_.empty() && !moduleName_.empty() && !pkgPath_.empty() && (abcOffset_ != INVALID_VALUE) && + (abcSize_ != INVALID_VALUE); + } + + void Dump() const + { + LOG_ECMA(INFO) << "PkgInfo: " << KEY_BUNDLE_NAME << ": " << bundleName_ << ", " << KEY_MODULE_NAME << ": " + << moduleName_ << ", " << KEY_PKG_PATH << ": " << pkgPath_ << ", " << KEY_ABC_OFFSET << ": " + << std::hex << abcOffset_ << ", " << KEY_ABC_SIZE << ": " << abcSize_; + } + + const std::string &GetBundleName() const + { + return bundleName_; + } + + const std::string &GetModuleName() const + { + return moduleName_; + } + + const std::string &GetPath() const + { + return pkgPath_; + } + + std::string GetFullName() const + { + return pkgPath_ + GetPathSeparator() + moduleName_ + GetPathSeparator() + abcName_; + } + + uint32_t GetOffset() const + { + return abcOffset_; + } + + uint32_t GetSize() const + { + return abcSize_; + } + +private: + static constexpr uint32_t INVALID_VALUE = std::numeric_limits::max(); + std::string bundleName_; + std::string moduleName_; + std::string pkgPath_; + std::string abcName_; + uint32_t abcOffset_ {INVALID_VALUE}; + uint32_t abcSize_ {INVALID_VALUE}; +}; +} // namespace panda::ecmascript::kungfu +#endif \ No newline at end of file diff --git a/ecmascript/compiler/operations_stub_builder.cpp b/ecmascript/compiler/operations_stub_builder.cpp index 4c9138899189fed93ef415f6f3cad6ec4f2eafbb..bbc433520da4c0bd9d238f5905acb39315997d62 100644 --- a/ecmascript/compiler/operations_stub_builder.cpp +++ b/ecmascript/compiler/operations_stub_builder.cpp @@ -26,15 +26,32 @@ GateRef OperationsStubBuilder::Equal(GateRef glue, GateRef left, GateRef right, env->SubCfgEntry(&entry); Label exit(env); Label isHole(env); + Label notHole(env); DEFVARIABLE(result, VariableType::JS_ANY(), Hole()); - result = FastEqual(left, right, callback); - Branch(TaggedIsHole(*result), &isHole, &exit); + result = FastEqual(glue, left, right, callback); + Branch(TaggedIsHole(*result), &isHole, ¬Hole); Bind(&isHole); { // slow path result = CallRuntime(glue, RTSTUB_ID(Eq), { left, right }); Jump(&exit); } + Bind(¬Hole); + { + Label resultIsTrue(env); + Label resultNotTrue(env); + Branch(TaggedIsTrue(*result), &resultIsTrue, &resultNotTrue); + Bind(&resultIsTrue); + { + callback.ProfileBranch(true); + Jump(&exit); + } + Bind(&resultNotTrue); + { + callback.ProfileBranch(false); + Jump(&exit); + } + } Bind(&exit); auto ret = *result; env->SubCfgExit(); @@ -49,7 +66,7 @@ GateRef OperationsStubBuilder::NotEqual(GateRef glue, GateRef left, GateRef righ Label exit(env); DEFVARIABLE(result, VariableType::JS_ANY(), Hole()); - result = FastEqual(left, right, callback); + result = FastEqual(glue, left, right, callback); Label isHole(env); Label notHole(env); Branch(TaggedIsHole(*result), &isHole, ¬Hole); @@ -67,10 +84,12 @@ GateRef OperationsStubBuilder::NotEqual(GateRef glue, GateRef left, GateRef righ Bind(&resultIsTrue); { result = TaggedFalse(); + callback.ProfileBranch(false); Jump(&exit); } Bind(&resultNotTrue); { + callback.ProfileBranch(true); result = TaggedTrue(); Jump(&exit); } @@ -87,12 +106,19 @@ GateRef OperationsStubBuilder::StrictEqual(GateRef glue, GateRef left, GateRef r Label entry(env); env->SubCfgEntry(&entry); Label exit(env); + Label strictEqual(env); Label notStrictEqual(env); DEFVARIABLE(result, VariableType::JS_ANY(), TaggedTrue()); - Branch(FastStrictEqual(glue, left, right, callback), &exit, ¬StrictEqual); + Branch(FastStrictEqual(glue, left, right, callback), &strictEqual, ¬StrictEqual); + Bind(&strictEqual); + { + callback.ProfileBranch(true); + Jump(&exit); + } Bind(¬StrictEqual); { result = TaggedFalse(); + callback.ProfileBranch(false); Jump(&exit); } Bind(&exit); @@ -108,11 +134,18 @@ GateRef OperationsStubBuilder::StrictNotEqual(GateRef glue, GateRef left, GateRe env->SubCfgEntry(&entry); Label exit(env); Label strictEqual(env); + Label notStrictEqual(env); DEFVARIABLE(result, VariableType::JS_ANY(), TaggedTrue()); - Branch(FastStrictEqual(glue, left, right, callback), &strictEqual, &exit); + Branch(FastStrictEqual(glue, left, right, callback), &strictEqual, ¬StrictEqual); Bind(&strictEqual); { result = TaggedFalse(); + callback.ProfileBranch(false); + Jump(&exit); + } + Bind(¬StrictEqual); + { + callback.ProfileBranch(true); Jump(&exit); } Bind(&exit); @@ -206,11 +239,13 @@ GateRef OperationsStubBuilder::Less(GateRef glue, GateRef left, GateRef right, P } Bind(&leftLessRight); { + callback.ProfileBranch(true); result = TaggedTrue(); Jump(&exit); } Bind(&leftNotLessRight); { + callback.ProfileBranch(false); result = TaggedFalse(); Jump(&exit); } @@ -311,11 +346,13 @@ GateRef OperationsStubBuilder::LessEq(GateRef glue, GateRef left, GateRef right, } Bind(&leftLessEqRight); { + callback.ProfileBranch(true); result = TaggedTrue(); Jump(&exit); } Bind(&leftNotLessEqRight); { + callback.ProfileBranch(false); result = TaggedFalse(); Jump(&exit); } @@ -394,8 +431,8 @@ GateRef OperationsStubBuilder::Greater(GateRef glue, GateRef left, GateRef right } Bind(&rightIsInt1); { - int32_t type = Int32(PGOSampleType::IntType()); - callback.ProfileOpType(Int32(type)); + GateRef type = Int32(PGOSampleType::IntType()); + COMBINE_TYPE_CALL_BACK(curType, type); doubleRight = ChangeInt32ToFloat64(TaggedGetInt(right)); Jump(&exit2); } @@ -415,11 +452,13 @@ GateRef OperationsStubBuilder::Greater(GateRef glue, GateRef left, GateRef right } Bind(&leftGreaterRight); { + callback.ProfileBranch(true); result = TaggedTrue(); Jump(&exit); } Bind(&leftNotGreaterRight); { + callback.ProfileBranch(false); result = TaggedFalse(); Jump(&exit); } @@ -520,11 +559,13 @@ GateRef OperationsStubBuilder::GreaterEq(GateRef glue, GateRef left, GateRef rig } Bind(&leftGreaterEqRight); { + callback.ProfileBranch(true); result = TaggedTrue(); Jump(&exit); } Bind(&leftNotGreaterEQRight); { + callback.ProfileBranch(false); result = TaggedFalse(); Jump(&exit); } @@ -547,7 +588,7 @@ GateRef OperationsStubBuilder::Add(GateRef glue, GateRef left, GateRef right, Pr env->SubCfgEntry(&entry); Label exit(env); Label slowPath(env); - DEFVARIABLE(result, VariableType::JS_ANY(), FastAdd(left, right, callback)); + DEFVARIABLE(result, VariableType::JS_ANY(), FastAdd(glue, left, right, callback)); Branch(TaggedIsHole(*result), &slowPath, &exit); Bind(&slowPath); { @@ -568,7 +609,7 @@ GateRef OperationsStubBuilder::Sub(GateRef glue, GateRef left, GateRef right, Pr env->SubCfgEntry(&entry); Label exit(env); Label slowPath(env); - DEFVARIABLE(result, VariableType::JS_ANY(), FastSub(left, right, callback)); + DEFVARIABLE(result, VariableType::JS_ANY(), FastSub(glue, left, right, callback)); Branch(TaggedIsHole(*result), &slowPath, &exit); Bind(&slowPath); { @@ -589,7 +630,7 @@ GateRef OperationsStubBuilder::Mul(GateRef glue, GateRef left, GateRef right, Pr env->SubCfgEntry(&entry); Label exit(env); Label slowPath(env); - DEFVARIABLE(result, VariableType::JS_ANY(), FastMul(left, right, callback)); + DEFVARIABLE(result, VariableType::JS_ANY(), FastMul(glue, left, right, callback)); Branch(TaggedIsHole(*result), &slowPath, &exit); Bind(&slowPath); { @@ -1345,7 +1386,7 @@ GateRef OperationsStubBuilder::Neg(GateRef glue, GateRef value, ProfileOperation { callback.ProfileOpType(Int32(PGOSampleType::DoubleType())); GateRef valueDouble = GetDoubleOfTDouble(value); - result = DoubleToTaggedDoublePtr(DoubleSub(Double(0), valueDouble)); + result = DoubleToTaggedDoublePtr(DoubleMul(Double(-1), valueDouble)); Jump(&exit); } Bind(&valueNotDouble); diff --git a/ecmascript/compiler/pass.h b/ecmascript/compiler/pass.h index 920104482b1dacef47982f0bcfbffef3df274443..ad0c66d3ab60ddc4291e0af66337c4a04128d878 100644 --- a/ecmascript/compiler/pass.h +++ b/ecmascript/compiler/pass.h @@ -18,14 +18,20 @@ #include "ecmascript/compiler/async_function_lowering.h" #include "ecmascript/compiler/bytecode_circuit_builder.h" +#include "ecmascript/compiler/combined_pass_visitor.h" #include "ecmascript/compiler/common_stubs.h" #include "ecmascript/compiler/compiler_log.h" +#include "ecmascript/compiler/dead_code_elimination.h" #include "ecmascript/compiler/early_elimination.h" +#include "ecmascript/compiler/array_bounds_check_elimination.h" +#include "ecmascript/compiler/graph_editor.h" #include "ecmascript/compiler/graph_linearizer.h" #include "ecmascript/compiler/later_elimination.h" #include "ecmascript/compiler/lcr_lowering.h" #include "ecmascript/compiler/llvm_codegen.h" #include "ecmascript/compiler/loop_analysis.h" +#include "ecmascript/compiler/loop_peeling.h" +#include "ecmascript/compiler/ntype_hcr_lowering.h" #include "ecmascript/compiler/ntype_mcr_lowering.h" #include "ecmascript/compiler/number_speculative_runner.h" #include "ecmascript/compiler/scheduler.h" @@ -34,8 +40,10 @@ #include "ecmascript/compiler/ts_class_analysis.h" #include "ecmascript/compiler/ts_inline_lowering.h" #include "ecmascript/compiler/ts_hcr_lowering.h" +#include "ecmascript/compiler/ts_hcr_opt_pass.h" #include "ecmascript/compiler/type_inference/global_type_infer.h" #include "ecmascript/compiler/type_inference/initialization_analysis.h" +#include "ecmascript/compiler/type_inference/pgo_type_infer.h" #include "ecmascript/compiler/type_mcr_lowering.h" #include "ecmascript/compiler/value_numbering.h" #include "ecmascript/compiler/verifier.h" @@ -49,10 +57,10 @@ public: std::string methodName, MethodInfo *methodInfo = nullptr, bool hasTypes = false, const CString &recordName = "", MethodLiteral *methodLiteral = nullptr, uint32_t methodOffset = 0, NativeAreaAllocator *allocator = nullptr, - PGOProfilerDecoder *decoder = nullptr) + PGOProfilerDecoder *decoder = nullptr, PassOptions *passOptions = nullptr) : builder_(builder), circuit_(circuit), ctx_(ctx), log_(log), methodName_(methodName), methodInfo_(methodInfo), hasTypes_(hasTypes), recordName_(recordName), methodLiteral_(methodLiteral), - methodOffset_(methodOffset), allocator_(allocator), decoder_(decoder) + methodOffset_(methodOffset), allocator_(allocator), decoder_(decoder), passOptions_(passOptions) { } @@ -153,6 +161,11 @@ public: return decoder_; } + PassOptions *GetPassOptions() const + { + return passOptions_; + } + bool IsTypeAbort() const { if (hasTypes_) { @@ -202,6 +215,7 @@ private: uint32_t methodOffset_; NativeAreaAllocator *allocator_ {nullptr}; PGOProfilerDecoder *decoder_ {nullptr}; + PassOptions *passOptions_ {nullptr}; }; template @@ -224,22 +238,39 @@ class TypeInferPass { public: bool Run(PassData* data) { + PassOptions *passOptions = data->GetPassOptions(); + if (passOptions != nullptr && !passOptions->EnableTypeInfer()) { + return false; + } TimeScope timescope("TypeInferPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); - if (data->HasTypes()) { - bool enableLog = data->GetLog()->GetEnableMethodLog() && data->GetLog()->OutputType(); - GlobalTypeInfer globalTypeInfer(data->GetPassContext(), data->GetMethodOffset(), data->GetRecordName(), - data->GetPGOProfilerDecoder(), enableLog); - globalTypeInfer.ProcessTypeInference(data->GetBuilder(), data->GetCircuit()); - if (data->GetMethodLiteral()->IsClassConstructor()) { - InitializationAnalysis initAnalysis(data->GetCircuit(), data->GetTSManager(), data->GetRecordName(), - data->GetMethodName(), enableLog); - initAnalysis.Run(); - } + bool enableLog = data->GetLog()->GetEnableMethodLog() && data->GetLog()->OutputType(); + GlobalTypeInfer globalTypeInfer(data->GetPassContext(), data->GetMethodOffset(), data->GetRecordName(), + data->GetPGOProfilerDecoder(), passOptions->EnableOptTrackField(), + enableLog); + globalTypeInfer.ProcessTypeInference(data->GetBuilder(), data->GetCircuit()); + if (data->HasTypes() && data->GetMethodLiteral()->IsClassConstructor()) { + InitializationAnalysis initAnalysis(data->GetCircuit(), data->GetTSManager(), data->GetRecordName(), + data->GetMethodName(), enableLog); + initAnalysis.Run(); } return true; } }; +class PGOTypeInferPass { +public: + bool Run(PassData* data) + { + TimeScope timescope("PGOTypeInferPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); + bool enableLog = data->GetLog()->GetEnableMethodLog() && data->GetLog()->OutputType(); + Chunk chunk(data->GetNativeAreaAllocator()); + PGOTypeInfer pgoTypeInfer(data->GetCircuit(), data->GetTSManager(), data->GetBuilder(), + data->GetMethodName(), &chunk, enableLog); + pgoTypeInfer.Run(); + return true; + } +}; + class TSClassAnalysisPass { public: bool Run(PassData *data) @@ -253,65 +284,132 @@ public: class TSHCRLoweringPass { public: - bool Run(PassData *data) + bool Run(PassData* data) { + PassOptions *passOptions = data->GetPassOptions(); + if (!passOptions->EnableTypeLowering()) { + return false; + } TimeScope timescope("TSHCRLoweringPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); bool enableLog = data->GetLog()->EnableMethodCIRLog(); bool enableTypeLog = data->GetLog()->GetEnableMethodLog() && data->GetLog()->OutputType(); - TSHCRLowering lowering(data->GetCircuit(), data->GetPassContext(), enableLog, enableTypeLog, data->GetMethodName()); + TSHCRLowering lowering(data->GetCircuit(), data->GetPassContext(), + enableLog, enableTypeLog, data->GetMethodName()); bool success = lowering.RunTSHCRLowering(); if (!success) { data->MarkAsTypeAbort(); } + Chunk chunk(data->GetNativeAreaAllocator()); + CombinedPassVisitor visitor(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk); + DeadCodeElimination deadCodeElimination(data->GetCircuit(), &visitor, &chunk); + TSHCROptPass optimization(data->GetCircuit(), &visitor, &chunk, data->GetPassContext(), enableLog, + data->GetMethodName()); + + visitor.AddPass(&optimization); + visitor.AddPass(&deadCodeElimination); + visitor.VisitGraph(); + visitor.PrintLog("TSHCROptPass"); + return true; + } +}; + +class NTypeHCRLoweringPass { +public: + bool Run(PassData* data) + { + PassOptions *passOptions = data->GetPassOptions(); + if (!passOptions->EnableTypeLowering()) { + return false; + } + TimeScope timescope("NTypeHCRLoweringPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); + bool enableLog = data->GetLog()->EnableMethodCIRLog(); + NTypeHCRLowering lowering(data->GetCircuit(), data->GetPassContext(), data->GetTSManager(), + data->GetMethodLiteral(), data->GetRecordName(), enableLog, data->GetMethodName()); + lowering.RunNTypeHCRLowering(); + Chunk chunk(data->GetNativeAreaAllocator()); + CombinedPassVisitor visitor(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk); + DeadCodeElimination deadCodeElimination(data->GetCircuit(), &visitor, &chunk); + visitor.AddPass(&deadCodeElimination); + visitor.VisitGraph(); return true; } }; class TypeMCRLoweringPass { public: - bool Run(PassData *data) + bool Run(PassData* data) { + PassOptions *passOptions = data->GetPassOptions(); + if (!passOptions->EnableTypeLowering()) { + return false; + } TimeScope timescope("TypeMCRLoweringPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); bool enableLog = data->GetLog()->EnableMethodCIRLog(); - TypeMCRLowering lowering(data->GetCircuit(), data->GetCompilerConfig(), data->GetTSManager(), - enableLog, data->GetMethodName()); - lowering.RunTypeMCRLowering(); + Chunk chunk(data->GetNativeAreaAllocator()); + CombinedPassVisitor visitor(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk); + TypeMCRLowering lowering(data->GetCircuit(), &visitor, + data->GetCompilerConfig(), data->GetTSManager(), &chunk, + data->GetPassOptions()->EnableOptOnHeapCheck()); + visitor.AddPass(&lowering); + visitor.VisitGraph(); + visitor.PrintLog("TypeMCRLowering"); return true; } }; class NTypeMCRLoweringPass { public: - bool Run(PassData *data) + bool Run(PassData* data) { + PassOptions *passOptions = data->GetPassOptions(); + if (!passOptions->EnableTypeLowering()) { + return false; + } TimeScope timescope("NTypeMCRLoweringPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); bool enableLog = data->GetLog()->EnableMethodCIRLog(); - NTypeMCRLowering lowering(data->GetCircuit(), data->GetPassContext(), enableLog, data->GetMethodName()); - lowering.RunNTypeMCRLowering(); + Chunk chunk(data->GetNativeAreaAllocator()); + CombinedPassVisitor visitor(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk); + NTypeMCRLowering lowering(data->GetCircuit(), &visitor, data->GetPassContext(), + data->GetRecordName(), &chunk); + visitor.AddPass(&lowering); + visitor.VisitGraph(); + visitor.PrintLog("NTypeMCRLowering"); return true; } }; class LCRLoweringPass { public: - bool Run(PassData *data) + bool Run(PassData* data) { + PassOptions *passOptions = data->GetPassOptions(); + if (!passOptions->EnableTypeLowering()) { + return false; + } TimeScope timescope("LCRLoweringPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); bool enableLog = data->GetLog()->EnableMethodCIRLog(); - LCRLowering lowering(data->GetCircuit(), data->GetCompilerConfig(), enableLog, data->GetMethodName()); - lowering.Run(); + Chunk chunk(data->GetNativeAreaAllocator()); + CombinedPassVisitor visitor(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk); + LCRLowering lowering(data->GetCircuit(), &visitor, data->GetCompilerConfig(), &chunk); + visitor.AddPass(&lowering); + visitor.VisitGraph(); + visitor.PrintLog("LCRLowering"); return true; } }; class TSInlineLoweringPass { public: - bool Run(PassData *data) + bool Run(PassData* data) { + PassOptions *passOptions = data->GetPassOptions(); + if (!passOptions->EnableOptInlining() || !passOptions->EnableTypeLowering()) { + return false; + } TimeScope timescope("TSInlineLoweringPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); bool enableLog = data->GetLog()->EnableMethodCIRLog(); TSInlineLowering inlining(data->GetCircuit(), data->GetPassContext(), enableLog, data->GetMethodName(), - data->GetNativeAreaAllocator()); + data->GetNativeAreaAllocator(), passOptions, data->GetMethodOffset()); inlining.RunTSInlineLowering(); return true; } @@ -330,6 +428,20 @@ public: } }; +class RunFlowCyclesVerifierPass { +public: + bool Run(PassData* data) + { + TimeScope timescope("FlowCyclesVerifierPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); + bool hasFlowCycle = Verifier::RunFlowCyclesFind(data->GetCircuit()); + if (hasFlowCycle) { + LOG_FULL(FATAL) << "FlowCyclesVerifierPass fail"; + UNREACHABLE(); + } + return !hasFlowCycle; + } +}; + class VerifierPass { public: bool Run(PassData* data) @@ -349,11 +461,16 @@ class NumberSpeculativePass { public: bool Run(PassData* data) { + PassOptions *passOptions = data->GetPassOptions(); + if (!passOptions->EnableTypeLowering()) { + return false; + } TimeScope timescope("NumberSpeculativePass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); Chunk chunk(data->GetNativeAreaAllocator()); bool enableLog = data->GetLog()->EnableMethodCIRLog(); - NumberSpeculativeRunner(data->GetCircuit(), data->GetTSManager(), - enableLog, data->GetMethodName(), &chunk).Run(); + CombinedPassVisitor visitor(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk); + bool onHeapCheck = data->GetPassOptions()->EnableOptOnHeapCheck(); + NumberSpeculativeRunner(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk, onHeapCheck).Run(); return true; } }; @@ -370,20 +487,66 @@ public: auto bb = data->GetBuilder()->GetBasicBlockById(head.second); auto loopInfo = new LoopInfo(&chunk, bb.stateCurrent); loopAnalysis_.CollectLoopBody(loopInfo); + bool enableLog = data->GetLog()->EnableMethodCIRLog(); + if (enableLog) { + loopAnalysis_.PrintLoop(loopInfo); + } + if (data->GetPassOptions()->EnableOptLoopPeeling()) { + LoopPeeling(data->GetBuilder(), data->GetCircuit(), enableLog, + data->GetMethodName(), &chunk, loopInfo).Peel(); + } } loopAnalysis_.LoopExitElimination(); return true; } }; +class RedundantPhiEliminationPass { +public: + bool Run(PassData* data) + { + TimeScope timescope("RedundantPhiEliminationPass", data->GetMethodName(), + data->GetMethodOffset(), data->GetLog()); + bool enableLog = data->GetLog()->EnableMethodCIRLog(); + GraphEditor::EliminateRedundantPhi(data->GetCircuit(), enableLog, data->GetMethodName()); + return true; + } +}; + class EarlyEliminationPass { public: bool Run(PassData* data) { - TimeScope timescope("EarlyEliminationPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); + PassOptions *passOptions = data->GetPassOptions(); + if (!passOptions->EnableTypeLowering() || !passOptions->EnableEarlyElimination()) { + return false; + } + TimeScope timescope("EarlyEliminationPass", data->GetMethodName(), + data->GetMethodOffset(), data->GetLog()); + bool enableLog = data->GetLog()->EnableMethodCIRLog() || data->GetLog()->OutputASM(); + Chunk chunk(data->GetNativeAreaAllocator()); + CombinedPassVisitor visitor(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk); + EarlyElimination earlyElimination(data->GetCircuit(), &visitor, &chunk); + visitor.AddPass(&earlyElimination); + visitor.VisitGraph(); + visitor.PrintLog("early elimination"); + return true; + } +}; + +class ArrayBoundsCheckEliminationPass { +public: + bool Run(PassData* data) + { + PassOptions *passOptions = data->GetPassOptions(); + if (!passOptions->EnableTypeLowering() || !passOptions->EnableArrayBoundsCheckElimination()) { + return false; + } + TimeScope timescope("ArrayBoundsCheckEliminationPass", + data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); Chunk chunk(data->GetNativeAreaAllocator()); bool enableLog = data->GetLog()->EnableMethodCIRLog(); - EarlyElimination(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk).Run(); + ArrayBoundsCheckElimination(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk).Run(); return true; } }; @@ -392,10 +555,19 @@ class LaterEliminationPass { public: bool Run(PassData* data) { - TimeScope timescope("LaterEliminationPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); + PassOptions *passOptions = data->GetPassOptions(); + if (!passOptions->EnableTypeLowering() || !passOptions->EnableLaterElimination()) { + return false; + } + TimeScope timescope("LaterEliminationPass", data->GetMethodName(), + data->GetMethodOffset(), data->GetLog()); + bool enableLog = data->GetLog()->EnableMethodCIRLog() || data->GetLog()->OutputASM(); Chunk chunk(data->GetNativeAreaAllocator()); - bool enableLog = data->GetLog()->EnableMethodCIRLog(); - LaterElimination(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk).Run(); + CombinedPassVisitor visitor(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk); + LaterElimination laterElimination(data->GetCircuit(), &visitor, &chunk); + visitor.AddPass(&laterElimination); + visitor.VisitGraph(); + visitor.PrintLog("later elimination"); return true; } }; @@ -404,10 +576,18 @@ class ValueNumberingPass { public: bool Run(PassData* data) { + PassOptions *passOptions = data->GetPassOptions(); + if (!passOptions->EnableTypeLowering() || !passOptions->EnableValueNumbering()) { + return false; + } TimeScope timescope("ValueNumberingPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); Chunk chunk(data->GetNativeAreaAllocator()); bool enableLog = data->GetLog()->EnableMethodCIRLog(); - ValueNumbering(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk).Run(); + CombinedPassVisitor visitor(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk); + ValueNumbering valueNumbering(data->GetCircuit(), &visitor, &chunk); + visitor.AddPass(&valueNumbering); + visitor.VisitGraph(); + visitor.PrintLog("value numbering"); return true; } }; @@ -427,10 +607,15 @@ class StateSplitLinearizerPass { public: bool Run(PassData* data) { - TimeScope timescope("StateSplitLinearizerPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); + PassOptions *passOptions = data->GetPassOptions(); + if (!passOptions->EnableTypeLowering()) { + return false; + } + TimeScope timescope("StateSplitLinearizerPass", data->GetMethodName(), + data->GetMethodOffset(), data->GetLog()); Chunk chunk(data->GetNativeAreaAllocator()); bool enableLog = data->GetLog()->EnableMethodCIRLog(); - StateSplitLinearizer(data->GetCircuit(), data->GetCompilerConfig(), + StateSplitLinearizer(data->GetCircuit(), nullptr, data->GetCompilerConfig(), enableLog, data->GetMethodName(), &chunk).Run(); return true; } @@ -443,7 +628,8 @@ public: TimeScope timescope("GraphLinearizerPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); Chunk chunk(data->GetNativeAreaAllocator()); bool enableLog = data->GetLog()->EnableMethodCIRLog(); - GraphLinearizer(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk).Run(data->GetCfg()); + bool licm = data->GetPassOptions()->EnableOptLoopInvariantCodeMotion(); + GraphLinearizer(data->GetCircuit(), enableLog, data->GetMethodName(), &chunk, false, licm).Run(data->GetCfg()); return true; } }; @@ -460,10 +646,12 @@ public: auto module = data->GetAotModule(); TimeScope timescope("LLVMIRGenPass", data->GetMethodName(), data->GetMethodOffset(), data->GetLog()); bool enableLog = data->GetLog()->EnableMethodCIRLog() || data->GetLog()->OutputASM(); + PassOptions *passOptions = data->GetPassOptions(); + bool enableOptInlining = passOptions->EnableOptInlining() && passOptions->EnableTypeLowering(); CreateCodeGen(module, enableLog); CodeGenerator codegen(llvmImpl_, data->GetMethodName()); codegen.Run(data->GetCircuit(), data->GetConstScheduleResult(), data->GetCompilerConfig(), - data->GetMethodLiteral(), data->GetJSPandaFile()); + data->GetMethodLiteral(), data->GetJSPandaFile(), enableOptInlining); return true; } private: @@ -480,10 +668,25 @@ public: AsyncFunctionLowering lowering(data->GetBuilder(), data->GetCircuit(), data->GetCompilerConfig(), enableLog, data->GetMethodName()); if (lowering.IsAsyncRelated()) { - lowering.ProcessAll(); + if (IsFunctionMain(data)) { + lowering.ProcessAll(); + } else { + data->MarkAsTypeAbort(); + } } return true; } + +private: + bool IsFunctionMain(PassData* data) + { + auto methodName = data->GetMethodName(); + auto pos = methodName.find(JSPandaFile::ENTRY_FUNCTION_NAME); + if (pos != std::string::npos) { + return true; + } + return false; + } }; } // namespace panda::ecmascript::kungfu #endif diff --git a/ecmascript/compiler/pass_manager.cpp b/ecmascript/compiler/pass_manager.cpp index c51920ca56c6a58c7e4d72c3de77e0ee75230ca7..56cd7b9bcf94e10a25e06c8535ffb914b0dea5d6 100644 --- a/ecmascript/compiler/pass_manager.cpp +++ b/ecmascript/compiler/pass_manager.cpp @@ -19,10 +19,12 @@ #include "ecmascript/ecma_handle_scope.h" #include "ecmascript/jspandafile/js_pandafile_manager.h" #include "ecmascript/jspandafile/panda_file_translator.h" +#include "ecmascript/pgo_profiler/pgo_profiler_manager.h" #include "ecmascript/snapshot/mem/snapshot.h" #include "ecmascript/ts_types/ts_manager.h" namespace panda::ecmascript::kungfu { +using PGOProfilerManager = pgo::PGOProfilerManager; bool PassManager::ShouldCollect() const { return passOptions_->EnableTypeInfer() && @@ -39,13 +41,13 @@ bool PassManager::Compile(JSPandaFile *jsPandaFile, const std::string &fileName, return false; } - if (!profilerDecoder_.LoadAndVerify(jsPandaFile->GetChecksum())) { + if (!PGOProfilerManager::MergeApFiles(jsPandaFile->GetChecksum(), profilerDecoder_)) { LOG_COMPILER(ERROR) << "Load and verify profiler failure"; return false; } ResolveModule(jsPandaFile, fileName); - BytecodeInfoCollector collector(vm_, jsPandaFile, maxAotMethodSize_, ShouldCollect()); + BytecodeInfoCollector collector(vm_, jsPandaFile, profilerDecoder_, maxAotMethodSize_, ShouldCollect()); // Checking released/debuggable pandafile uses method literals, which are initialized in BytecodeInfoCollector, // should after it. @@ -63,6 +65,7 @@ bool PassManager::Compile(JSPandaFile *jsPandaFile, const std::string &fileName, fileName, triple_, &lOptions, + log_, log_->OutputASM(), maxMethodsInModule_); @@ -85,7 +88,8 @@ bool PassManager::Compile(JSPandaFile *jsPandaFile, const std::string &fileName, std::string fullName = module->GetFuncName(methodLiteral, jsPandaFile); bool enableMethodLog = log_->GetEnableMethodLog(); if (enableMethodLog) { - LOG_COMPILER(INFO) << "\033[34m" << "aot method [" << fullName << "] log:" << "\033[0m"; + LOG_COMPILER(INFO) << "\033[34m" << "aot method [" << fullName + << "] recordName [" << recordName << "] log:" << "\033[0m"; } bool hasTypes = jsPandaFile->HasTSTypes(recordName); if (UNLIKELY(!hasTypes)) { @@ -93,65 +97,61 @@ bool PassManager::Compile(JSPandaFile *jsPandaFile, const std::string &fileName, } Circuit circuit(vm_->GetNativeAreaAllocator(), ctx.GetAOTModule()->GetDebugInfo(), - fullName.c_str(), cmpCfg->Is64Bit()); - circuit.SetFrameType(FrameType::OPTIMIZED_JS_FUNCTION_FRAME); + fullName.c_str(), cmpCfg->Is64Bit(), FrameType::OPTIMIZED_JS_FUNCTION_FRAME); PGOProfilerDecoder *decoder = passOptions_->EnableOptPGOType() ? &profilerDecoder_ : nullptr; BytecodeCircuitBuilder builder(jsPandaFile, methodLiteral, methodPCInfo, tsManager, &circuit, ctx.GetByteCodes(), hasTypes, enableMethodLog && log_->OutputCIR(), - passOptions_->EnableTypeLowering(), fullName, recordName, decoder); + passOptions_->EnableTypeLowering(), fullName, recordName, decoder, false, + passOptions_->EnableOptTrackField()); { TimeScope timeScope("BytecodeToCircuit", methodName, methodOffset, log_); builder.BytecodeToCircuit(); } PassData data(&builder, &circuit, &ctx, log_, fullName, &methodInfo, hasTypes, recordName, - methodLiteral, methodOffset, vm_->GetNativeAreaAllocator(), decoder); - + methodLiteral, methodOffset, vm_->GetNativeAreaAllocator(), decoder, passOptions_); PassRunner pipeline(&data); + pipeline.RunPass(); + pipeline.RunPass(); if (builder.EnableLoopOptimization()) { pipeline.RunPass(); + pipeline.RunPass(); } - if (passOptions_->EnableTypeInfer()) { - pipeline.RunPass(); - } + pipeline.RunPass(); if (data.IsTypeAbort()) { data.AbortCompilation(); return; } + pipeline.RunPass(); pipeline.RunPass(); - if (passOptions_->EnableOptInlining() && passOptions_->EnableTypeLowering()) { - pipeline.RunPass(); - } + pipeline.RunPass(); + pipeline.RunPass(); pipeline.RunPass(); - if (passOptions_->EnableTypeLowering()) { - pipeline.RunPass(); - if (data.IsTypeAbort()) { - data.AbortCompilation(); - return; - } - if (passOptions_->EnableEarlyElimination()) { - pipeline.RunPass(); - } - pipeline.RunPass(); - if (passOptions_->EnableLaterElimination()) { - pipeline.RunPass(); - } - if (passOptions_->EnableValueNumbering()) { - pipeline.RunPass(); - } - pipeline.RunPass(); - pipeline.RunPass(); - pipeline.RunPass(); - if (passOptions_->EnableEarlyElimination()) { - pipeline.RunPass(); - } - if (passOptions_->EnableLaterElimination()) { - pipeline.RunPass(); - } - pipeline.RunPass(); + // skip async function, because some application run with errors. + if (methodInfo.IsTypeInferAbort()) { + data.AbortCompilation(); + return; + } + pipeline.RunPass(); + pipeline.RunPass(); + pipeline.RunPass(); + if (data.IsTypeAbort()) { + data.AbortCompilation(); + return; } + pipeline.RunPass(); + pipeline.RunPass(); + pipeline.RunPass(); + pipeline.RunPass(); + pipeline.RunPass(); + pipeline.RunPass(); + pipeline.RunPass(); + pipeline.RunPass(); + pipeline.RunPass(); + pipeline.RunPass(); + pipeline.RunPass(); pipeline.RunPass(); pipeline.RunPass(); pipeline.RunPass(); @@ -191,9 +191,8 @@ void PassManager::ResolveModule(const JSPandaFile *jsPandaFile, const std::strin ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager(); [[maybe_unused]] EcmaHandleScope scope(thread); for (auto info: recordInfo) { - auto recordName = info.first; - if (jsPandaFile->IsModule(thread, recordName)) { - ASSERT(!thread->HasPendingException()); + if (jsPandaFile->IsModule(info.second)) { + auto recordName = info.first; JSHandle moduleRecord = moduleManager->HostResolveImportedModuleWithMerge(fileName.c_str(), recordName); SourceTextModule::Instantiate(thread, moduleRecord); diff --git a/ecmascript/compiler/pass_manager.h b/ecmascript/compiler/pass_manager.h index 005b47ef4195d842b0a33e9a1db4cde3128182f1..b4c39fed24589c22338d1a855a06fa24bd21d1a7 100644 --- a/ecmascript/compiler/pass_manager.h +++ b/ecmascript/compiler/pass_manager.h @@ -21,6 +21,7 @@ #include "ecmascript/compiler/file_generators.h" #include "ecmascript/ecma_vm.h" #include "ecmascript/pgo_profiler/pgo_profiler_decoder.h" +#include "ecmascript/pgo_profiler/pgo_profiler_manager.h" #include "ecmascript/ts_types/ts_manager.h" namespace panda::ecmascript::kungfu { @@ -35,6 +36,7 @@ public: : vm_(collector->GetVM()), bcInfoCollector_(collector), tsManager_(vm_->GetJSThread()->GetCurrentEcmaContext()->GetTSManager()), + bytecodes_(collector->GetByteCodes()), lexEnvManager_(bcInfoCollector_->GetEnvManager()), cmpCfg_(triple, &vm_->GetJSOptions()), log_(log), @@ -51,7 +53,7 @@ public: Bytecodes* GetByteCodes() { - return &bytecodes_; + return bytecodes_; } LexEnvManager* GetLexEnvManager() const @@ -113,7 +115,7 @@ private: EcmaVM *vm_ {nullptr}; BytecodeInfoCollector *bcInfoCollector_ {nullptr}; TSManager *tsManager_ {nullptr}; - Bytecodes bytecodes_; + Bytecodes *bytecodes_ {nullptr}; LexEnvManager *lexEnvManager_ {nullptr}; CompilationConfig cmpCfg_; CompilerLog *log_ {nullptr}; @@ -124,26 +126,40 @@ private: class PassOptions { public: - PassOptions(bool enableTypeLowering, bool enableEarlyElimination, bool enableLaterElimination, - bool enableValueNumbering, bool enableTypeInfer, bool enableOptInlining, bool enableOptPGOType) - : enableTypeLowering_(enableTypeLowering), + PassOptions(bool enableArrayBoundsCheckElimination, bool enableTypeLowering, bool enableEarlyElimination, + bool enableLaterElimination, bool enableValueNumbering, bool enableTypeInfer, + bool enableOptInlining, bool enableOptPGOType, bool enableOptTrackField, bool enableOptLoopPeeling, + bool enableOptOnHeapCheck, bool enableOptLoopInvariantCodeMotion) + : enableArrayBoundsCheckElimination_(enableArrayBoundsCheckElimination), + enableTypeLowering_(enableTypeLowering), enableEarlyElimination_(enableEarlyElimination), enableLaterElimination_(enableLaterElimination), enableValueNumbering_(enableValueNumbering), enableTypeInfer_(enableTypeInfer), enableOptInlining_(enableOptInlining), - enableOptPGOType_(enableOptPGOType) + enableOptPGOType_(enableOptPGOType), + enableOptTrackField_(enableOptTrackField), + enableOptLoopPeeling_(enableOptLoopPeeling), + enableOptOnHeapCheck_(enableOptOnHeapCheck), + enableOptLoopInvariantCodeMotion_(enableOptLoopInvariantCodeMotion) { } #define OPTION_LIST(V) \ - V(TypeLowering, true) \ - V(EarlyElimination, true) \ - V(LaterElimination, true) \ - V(ValueNumbering, false) \ - V(TypeInfer, false) \ - V(OptInlining, false) \ - V(OptPGOType, false) + V(ArrayBoundsCheckElimination, true) \ + V(TypeLowering, true) \ + V(EarlyElimination, true) \ + V(LaterElimination, true) \ + V(ValueNumbering, false) \ + V(TypeInfer, false) \ + V(OptInlining, false) \ + V(OptNoGCCall, false) \ + V(OptPGOType, false) \ + V(NoCheck, false) \ + V(OptTrackField, false) \ + V(OptLoopPeeling, false) \ + V(OptOnHeapCheck, false) \ + V(OptLoopInvariantCodeMotion, false) \ #define DECL_OPTION(NAME, DEFAULT) \ public: \ @@ -162,7 +178,7 @@ private: \ class PassManager { public: - PassManager(EcmaVM* vm, std::string entry, std::string &triple, size_t optLevel, size_t relocMode, + PassManager(EcmaVM* vm, std::string &entry, std::string &triple, size_t optLevel, size_t relocMode, CompilerLog *log, AotMethodLogList *logList, size_t maxAotMethodSize, size_t maxMethodsInModule, const std::string &profIn, uint32_t hotnessThreshold, PassOptions *passOptions) : vm_(vm), entry_(entry), triple_(triple), optLevel_(optLevel), relocMode_(relocMode), log_(log), diff --git a/ecmascript/compiler/pgo_bc_info.cpp b/ecmascript/compiler/pgo_bc_info.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4072b9db140f84b20e58ff02e39f36790dc57e93 --- /dev/null +++ b/ecmascript/compiler/pgo_bc_info.cpp @@ -0,0 +1,78 @@ +/* + * 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/compiler/pgo_bc_info.h" + +namespace panda::ecmascript::kungfu { +void PGOBCInfo::Info::Record(const InfoDetail &detail) +{ + auto it = methodOffsetToValVec_.find(detail.methodOffset); + if (it == methodOffsetToValVec_.end()) { + methodOffsetToValVec_[detail.methodOffset] = + ValVec { Val { detail.bcIndex, detail.bcOffset, detail.cpIndex} }; + } else { + it->second.emplace_back(Val{ detail.bcIndex, detail.bcOffset, detail.cpIndex }); + } + recordNameToValCount_[detail.recordName]++; +} + +uint32_t PGOBCInfo::Info::GetPGOExtendGTCount(const CString &recordName) const +{ + auto it = recordNameToValCount_.find(recordName); + if (it != recordNameToValCount_.end()) { + return it->second; + } + return 0; +} + +const PGOBCInfo::Info& PGOBCInfo::GetInfo(Type type) const +{ + ASSERT(Type::TYPE_FIRST <= type && type <= Type::TYPE_LAST); + return infos_[type]; +} + +uint32_t PGOBCInfo::GetPGOExtendGTCount(const CString &recordName) const +{ + uint32_t count = 0; + for (const Info &info : infos_) { + count += info.GetPGOExtendGTCount(recordName); + } + return count; +} + +void PGOBCInfo::Record(const InfoDetail &detail, Type type) +{ + ASSERT(Type::TYPE_FIRST <= type && type <= Type::TYPE_LAST); + Info &info = infos_[type]; + info.Record(detail); +} + +void PGOBCInfo::Record(const BytecodeInstruction &bcIns, int32_t bcIndex, + const CString &recordName, const MethodLiteral *method) +{ + BytecodeInstruction::Opcode opcode = static_cast(bcIns.GetOpcode()); + uint32_t methodOffset = method->GetMethodId().GetOffset(); + uint32_t bcOffset = bcIns.GetAddress() - method->GetBytecodeArray(); + if (Bytecodes::IsCreateObjectWithBufferOp(opcode)) { + auto cpIndex = bcIns.GetId().AsRawValue(); + Record(InfoDetail {recordName, methodOffset, bcIndex, bcOffset, cpIndex}, Type::OBJ_LITERAL); + } else if (Bytecodes::IsCreateArrayWithBufferOp(opcode)) { + auto cpIndex = bcIns.GetId().AsRawValue(); + Record(InfoDetail {recordName, methodOffset, bcIndex, bcOffset, cpIndex}, Type::ARRAY_LITERAL); + } else if (Bytecodes::IsCallOp(opcode)) { + Record(InfoDetail {recordName, methodOffset, bcIndex, bcOffset, 0}, Type::CALL_TARGET); + } +} +} // namespace panda::ecmascript diff --git a/ecmascript/compiler/pgo_bc_info.h b/ecmascript/compiler/pgo_bc_info.h new file mode 100644 index 0000000000000000000000000000000000000000..79628d30eaff613b5778bdf4da85fb47bd63043e --- /dev/null +++ b/ecmascript/compiler/pgo_bc_info.h @@ -0,0 +1,93 @@ +/* + * 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_COMPILER_PGO_BC_INFO_RECORDER_H +#define ECMASCRIPT_COMPILER_PGO_BC_INFO_RECORDER_H + +#include "ecmascript/jspandafile/method_literal.h" +#include "ecmascript/mem/c_containers.h" + +namespace panda::ecmascript::kungfu { +class PGOBCInfo { +public: + enum Type { + OBJ_LITERAL = 0, + ARRAY_LITERAL, + CALL_TARGET, + TYPE_NUM, + TYPE_FIRST = OBJ_LITERAL, + TYPE_LAST = CALL_TARGET, + }; + + struct InfoDetail { + const CString &recordName; + uint32_t methodOffset; + uint32_t bcIndex; + uint32_t bcOffset; + uint32_t cpIndex; + }; + + class Info { + public: + void Record(const InfoDetail &detail); + + uint32_t GetPGOExtendGTCount(const CString &recordName) const; + + template + void IterateValByMethodOffset(uint32_t methodOffset, Type type, const Callback &cb) const + { + if (methodOffsetToValVec_.find(methodOffset) != methodOffsetToValVec_.end()) { + const ValVec &valVec = methodOffsetToValVec_.at(methodOffset); + for (auto val : valVec) { + cb(type, val.bcIndex, val.bcOffset, val.cpIndex); + } + } + } + private: + struct Val { + uint32_t bcIndex; + uint32_t bcOffset; + uint32_t cpIndex; + }; + using ValVec = CVector; + CMap recordNameToValCount_; + CUnorderedMap methodOffsetToValVec_; + }; + + PGOBCInfo() : infos_(Type::TYPE_NUM, Info{}) {} + ~PGOBCInfo() = default; + + const Info& GetInfo(Type type) const; + + uint32_t GetPGOExtendGTCount(const CString &recordName) const; + + void Record(const BytecodeInstruction &bcIns, int32_t bcIndex, + const CString &recordName, const MethodLiteral *method); + + template + void IterateInfoAndType(uint32_t methodOffset, const Callback &cb) const + { + for (size_t idx = 0; idx < Type::TYPE_NUM; ++idx) { + Type type = static_cast(idx); + infos_[idx].IterateValByMethodOffset(methodOffset, type, cb); + } + } +private: + void Record(const InfoDetail &detail, Type type); + + CVector infos_; +}; +} // panda::ecmascript::kungfu +#endif // ECMASCRIPT_COMPILER_PGO_BC_INFO_RECORDER_H diff --git a/ecmascript/compiler/profiler_operation.h b/ecmascript/compiler/profiler_operation.h index fc182697614029def239920c4e6fb062d814536a..3e3f3fd3eab1990a449b66a6f0053b57c6373692 100644 --- a/ecmascript/compiler/profiler_operation.h +++ b/ecmascript/compiler/profiler_operation.h @@ -17,23 +17,29 @@ #define ECMASCRIPT_COMPILER_PROFILER_OPERATION_H #include +#include -#include "ecmascript/compiler/gate_meta_data.h" +#include "ecmascript/compiler/share_gate_meta_data.h" namespace panda::ecmascript::kungfu { enum class OperationType : uint8_t { CALL, OPERATION_TYPE, DEFINE_CLASS, - STORE_LAYOUT, - LOAD_LAYOUT, + CREATE_OBJECT, + TRUE_BRANCH, + FALSE_BRANCH, + TRY_DUMP, + TRY_PREDUMP, }; +using SlotIDFormat = BytecodeInstruction::Format; + #define COMBINE_TYPE_CALL_BACK(curType, type) \ callback.ProfileCombineOpType( \ - *curType, type, [this](GateRef curType, GateRef type) -> GateRef { return Int32Or(curType, type); }); + *(curType), type, [this](GateRef curType, GateRef type)->GateRef{ return Int32Or(curType, type); }) -using Callback = std::function; +using Callback = std::function &, OperationType)>; class ProfileOperation { public: ProfileOperation() : callback_(nullptr) {} @@ -47,14 +53,14 @@ public: inline void ProfileCall(GateRef func) const { if (callback_) { - callback_(func, OperationType::CALL); + callback_({ func }, OperationType::CALL); } } inline void ProfileOpType(GateRef type) const { if (callback_) { - callback_(type, OperationType::OPERATION_TYPE); + callback_({ type }, OperationType::OPERATION_TYPE); } } @@ -63,28 +69,42 @@ public: { if (callback_) { GateRef ret = combine(curType, type); - callback_(ret, OperationType::OPERATION_TYPE); + callback_({ ret }, OperationType::OPERATION_TYPE); } } inline void ProfileDefineClass(GateRef constructor) const { if (callback_) { - callback_(constructor, OperationType::DEFINE_CLASS); + callback_({ constructor }, OperationType::DEFINE_CLASS); + } + } + + inline void ProfileCreateObject(GateRef newObj) const + { + if (callback_) { + callback_({ newObj }, OperationType::CREATE_OBJECT); + } + } + + inline void TryDump() const + { + if (callback_) { + callback_({ }, OperationType::TRY_DUMP); } } - inline void ProfileObjLayoutByStore(GateRef object) const + inline void TryPreDump() const { if (callback_) { - callback_(object, OperationType::STORE_LAYOUT); + callback_({ }, OperationType::TRY_PREDUMP); } } - inline void ProfileObjLayoutByLoad(GateRef object) const + inline void ProfileBranch(bool isTrue) const { if (callback_) { - callback_(object, OperationType::LOAD_LAYOUT); + callback_({}, isTrue ? OperationType::TRUE_BRANCH : OperationType::FALSE_BRANCH); } } diff --git a/ecmascript/compiler/profiler_stub_builder.cpp b/ecmascript/compiler/profiler_stub_builder.cpp index 6e8373d57639ed12d9beb4f686d35080c3ae6162..c057519460b7df49b5ac54c1614577886d959689 100644 --- a/ecmascript/compiler/profiler_stub_builder.cpp +++ b/ecmascript/compiler/profiler_stub_builder.cpp @@ -15,76 +15,123 @@ #include "ecmascript/compiler/profiler_stub_builder.h" +#include "ecmascript/compiler/share_gate_meta_data.h" #include "ecmascript/compiler/interpreter_stub-inl.h" #include "ecmascript/compiler/stub_builder-inl.h" +#include "ecmascript/ic/profile_type_info.h" namespace panda::ecmascript::kungfu { -void ProfilerStubBuilder::PGOProfiler( - GateRef glue, GateRef pc, GateRef func, GateRef profileTypeInfo, GateRef value, OperationType type) +void ProfilerStubBuilder::PGOProfiler(GateRef glue, GateRef pc, GateRef func, GateRef profileTypeInfo, + const std::vector &values, SlotIDFormat format, OperationType type) { switch (type) { case OperationType::CALL: - ProfileCall(glue, value); + ProfileCall(glue, pc, func, values[0], profileTypeInfo, format); break; case OperationType::OPERATION_TYPE: - ProfileOpType(glue, pc, func, profileTypeInfo, value); + ProfileOpType(glue, pc, func, profileTypeInfo, values[0], format); break; case OperationType::DEFINE_CLASS: - ProfileDefineClass(glue, pc, func, value); + ProfileDefineClass(glue, pc, func, values[0], profileTypeInfo, format); break; - case OperationType::STORE_LAYOUT: - ProfileObjLayout(glue, pc, func, value, Int32(1)); + case OperationType::CREATE_OBJECT: + ProfileCreateObject(glue, pc, func, values[0], profileTypeInfo, format); break; - case OperationType::LOAD_LAYOUT: - ProfileObjLayout(glue, pc, func, value, Int32(0)); + case OperationType::TRY_DUMP: + TryDump(glue, func, profileTypeInfo); + break; + case OperationType::TRY_PREDUMP: + TryPreDump(glue, func, profileTypeInfo); + break; + case OperationType::TRUE_BRANCH: + ProfileBranch(glue, pc, func, profileTypeInfo, true); + break; + case OperationType::FALSE_BRANCH: + ProfileBranch(glue, pc, func, profileTypeInfo, false); break; default: break; } } -void ProfilerStubBuilder::ProfileOpType(GateRef glue, GateRef pc, GateRef func, GateRef profileTypeInfo, GateRef type) +void ProfilerStubBuilder::TryDump(GateRef glue, GateRef func, GateRef profileTypeInfo) { auto env = GetEnvironment(); Label subEntry(env); env->SubCfgEntry(&subEntry); + Label updatePeriodCounter(env); + Label exit(env); + + Branch(IsProfileTypeInfoChanged(profileTypeInfo), &updatePeriodCounter, &exit); + Bind(&updatePeriodCounter); + { + GateRef periodCounterOffset = GetBitFieldOffsetFromProfileTypeInfo(profileTypeInfo); + GateRef newCount = Int32(1); + Store(VariableType::INT32(), glue, profileTypeInfo, periodCounterOffset, newCount); + CallRuntime(glue, RTSTUB_ID(PGODump), {func}); + Jump(&exit); + } + Bind(&exit); + env->SubCfgExit(); +} + +void ProfilerStubBuilder::TryPreDump(GateRef glue, GateRef func, GateRef profileTypeInfo) +{ + auto env = GetEnvironment(); + Label subEntry(env); + env->SubCfgEntry(&subEntry); Label exit(env); Label profiler(env); - Label slowpath(env); - Branch(TaggedIsUndefined(profileTypeInfo), &slowpath, &profiler); - Bind(&slowpath); + Branch(TaggedIsUndefined(profileTypeInfo), &exit, &profiler); + Bind(&profiler); { - GateRef method = Load(VariableType::JS_ANY(), func, IntPtr(JSFunctionBase::METHOD_OFFSET)); - GateRef firstPC = Load(VariableType::NATIVE_POINTER(), method, - IntPtr(Method::NATIVE_POINTER_OR_BYTECODE_ARRAY_OFFSET)); - GateRef offset = TruncPtrToInt32(PtrSub(pc, firstPC)); - CallNGCRuntime(glue, RTSTUB_ID(ProfileOpType), { glue, func, offset, type }); + TryPreDumpInner(glue, func, profileTypeInfo); Jump(&exit); } + Bind(&exit); + env->SubCfgExit(); +} + +void ProfilerStubBuilder::ProfileOpType( + GateRef glue, GateRef pc, GateRef func, GateRef profileTypeInfo, GateRef type, SlotIDFormat format) +{ + auto env = GetEnvironment(); + Label subEntry(env); + env->SubCfgEntry(&subEntry); + + Label exit(env); + Label profiler(env); + Branch(TaggedIsUndefined(profileTypeInfo), &exit, &profiler); Bind(&profiler); { - Label pushLabel(env); + Label uninitialize(env); Label compareLabel(env); + Label updateSlot(env); - GateRef slotId = ZExtInt8ToInt32(Load(VariableType::INT8(), pc, IntPtr(1))); + GateRef slotId = GetSlotID(pc, format); GateRef slotValue = GetValueFromTaggedArray(profileTypeInfo, slotId); DEFVARIABLE(curType, VariableType::INT32(), type); - Branch(TaggedIsInt(slotValue), &compareLabel, &pushLabel); + DEFVARIABLE(curCount, VariableType::INT32(), Int32(0)); + Branch(TaggedIsInt(slotValue), &compareLabel, &uninitialize); Bind(&compareLabel); { GateRef oldSlotValue = TaggedGetInt(slotValue); - curType = Int32Or(oldSlotValue, type); - Branch(Int32Equal(oldSlotValue, *curType), &exit, &pushLabel); + GateRef oldType = Int32And(oldSlotValue, Int32(PGOSampleType::AnyType())); + curType = Int32Or(oldType, type); + curCount = Int32And(oldSlotValue, Int32(0xfffffc00)); // 0xfffffc00: count bits + Branch(Int32Equal(oldType, *curType), &exit, &updateSlot); + } + Bind(&uninitialize); + { + // Slot maybe overflow. + Branch(TaggedIsUndefined(slotValue), &updateSlot, &exit); } - Bind(&pushLabel); + Bind(&updateSlot); { - SetValueToTaggedArray(VariableType::JS_ANY(), glue, profileTypeInfo, slotId, IntToTaggedInt(*curType)); - GateRef method = Load(VariableType::JS_ANY(), func, IntPtr(JSFunctionBase::METHOD_OFFSET)); - GateRef firstPC = - Load(VariableType::NATIVE_POINTER(), method, IntPtr(Method::NATIVE_POINTER_OR_BYTECODE_ARRAY_OFFSET)); - GateRef offset = TruncPtrToInt32(PtrSub(pc, firstPC)); - CallNGCRuntime(glue, RTSTUB_ID(ProfileOpType), { glue, func, offset, *curType }); + GateRef newSlotValue = Int32Or(*curCount, *curType); + SetValueToTaggedArray(VariableType::JS_ANY(), glue, profileTypeInfo, slotId, IntToTaggedInt(newSlotValue)); + TryPreDumpInner(glue, func, profileTypeInfo); Jump(&exit); } } @@ -92,51 +139,159 @@ void ProfilerStubBuilder::ProfileOpType(GateRef glue, GateRef pc, GateRef func, env->SubCfgExit(); } -void ProfilerStubBuilder::ProfileDefineClass(GateRef glue, GateRef pc, GateRef func, GateRef constructor) +void ProfilerStubBuilder::ProfileDefineClass( + GateRef glue, GateRef pc, GateRef func, GateRef constructor, GateRef profileTypeInfo, SlotIDFormat format) { auto env = GetEnvironment(); Label subEntry(env); env->SubCfgEntry(&subEntry); - GateRef method = Load(VariableType::JS_ANY(), func, IntPtr(JSFunctionBase::METHOD_OFFSET)); - GateRef firstPC = - Load(VariableType::NATIVE_POINTER(), method, IntPtr(Method::NATIVE_POINTER_OR_BYTECODE_ARRAY_OFFSET)); - GateRef offset = TruncPtrToInt32(PtrSub(pc, firstPC)); - CallNGCRuntime(glue, RTSTUB_ID(ProfileDefineClass), { glue, func, offset, constructor }); - + Label exit(env); + Label profiler(env); + Branch(TaggedIsUndefined(profileTypeInfo), &exit, &profiler); + Bind(&profiler); + { + GateRef slotId = GetSlotID(pc, format); + GateRef slotValue = GetValueFromTaggedArray(profileTypeInfo, slotId); + Label updateSlot(env); + Branch(TaggedIsUndefined(slotValue), &updateSlot, &exit); + Bind(&updateSlot); + auto weakCtor = env->GetBuilder()->CreateWeakRef(constructor); + SetValueToTaggedArray(VariableType::JS_ANY(), glue, profileTypeInfo, slotId, weakCtor); + TryPreDumpInner(glue, func, profileTypeInfo); + Jump(&exit); + } + Bind(&exit); env->SubCfgExit(); } -void ProfilerStubBuilder::ProfileObjLayout(GateRef glue, GateRef pc, GateRef func, GateRef object, GateRef store) +void ProfilerStubBuilder::ProfileCreateObject( + GateRef glue, GateRef pc, GateRef func, GateRef newObj, GateRef profileTypeInfo, SlotIDFormat format) { auto env = GetEnvironment(); Label subEntry(env); env->SubCfgEntry(&subEntry); - Label isHeap(env); Label exit(env); - Branch(TaggedIsHeapObject(object), &isHeap, &exit); - Bind(&isHeap); + + Label profiler(env); + Branch(TaggedIsUndefined(profileTypeInfo), &exit, &profiler); + Bind(&profiler); { - GateRef method = Load(VariableType::JS_ANY(), func, IntPtr(JSFunctionBase::METHOD_OFFSET)); - GateRef firstPC = - Load(VariableType::NATIVE_POINTER(), method, IntPtr(Method::NATIVE_POINTER_OR_BYTECODE_ARRAY_OFFSET)); - GateRef offset = TruncPtrToInt32(PtrSub(pc, firstPC)); - CallNGCRuntime(glue, RTSTUB_ID(ProfileObjLayout), { glue, func, offset, object, store }); - Jump(&exit); + GateRef slotId = GetSlotID(pc, format); + auto hclass = LoadHClass(newObj); + GateRef slotValue = GetValueFromTaggedArray(profileTypeInfo, slotId); + Label isHeapObject(env); + Label isWeak(env); + Label uninitialized(env); + Label updateSlot(env); + Branch(TaggedIsHeapObject(slotValue), &isHeapObject, &uninitialized); + Bind(&isHeapObject); + { + Branch(TaggedIsWeak(slotValue), &isWeak, &updateSlot); + } + Bind(&isWeak); + { + auto cachedHClass = LoadObjectFromWeakRef(slotValue); + Branch(Equal(cachedHClass, hclass), &exit, &updateSlot); + } + Bind(&uninitialized); + { + Branch(TaggedIsUndefined(slotValue), &updateSlot, &exit); + } + Bind(&updateSlot); + { + auto weakCtor = env->GetBuilder()->CreateWeakRef(hclass); + SetValueToTaggedArray(VariableType::JS_ANY(), glue, profileTypeInfo, slotId, weakCtor); + TryPreDumpInner(glue, func, profileTypeInfo); + Jump(&exit); + } } Bind(&exit); env->SubCfgExit(); } -void ProfilerStubBuilder::ProfileCall(GateRef glue, GateRef func) +void ProfilerStubBuilder::ProfileCall( + GateRef glue, GateRef pc, GateRef func, GateRef target, GateRef profileTypeInfo, SlotIDFormat format) { auto env = GetEnvironment(); Label subEntry(env); env->SubCfgEntry(&subEntry); - CallNGCRuntime(glue, RTSTUB_ID(ProfileCall), { glue, func }); + + Label exit(env); + Label slowpath(env); + Label fastpath(env); + + Label targetIsFunction(env); + Branch(IsJSFunction(target), &targetIsFunction, &exit); + Bind(&targetIsFunction); + { + GateRef targetProfileInfo = GetProfileTypeInfo(target); + Label targetNonHotness(env); + Label IsCurrentHotness(env); + Label currentIsHotness(env); + Branch(TaggedIsUndefined(targetProfileInfo), &targetNonHotness, &IsCurrentHotness); + Bind(&targetNonHotness); + { + CallRuntime(glue, RTSTUB_ID(UpdateHotnessCounterWithProf), { target }); + Jump(&IsCurrentHotness); + } + Bind(&IsCurrentHotness); + { + Branch(TaggedIsUndefined(profileTypeInfo), &exit, ¤tIsHotness); + } + Bind(¤tIsHotness); + { + GateRef slotId = GetSlotID(pc, format); + GateRef slotValue = GetValueFromTaggedArray(profileTypeInfo, slotId); + Label isInt(env); + Label uninitialized(env); + Label updateSlot(env); + Branch(TaggedIsInt(slotValue), &isInt, &uninitialized); + Bind(&isInt); + { + Label change(env); + Label resetSlot(env); + GateRef oldSlotValue = TaggedGetInt(slotValue); + GateRef methodId = env->GetBuilder()->GetMethodId(target); + Branch(Int32Equal(oldSlotValue, TruncInt64ToInt32(methodId)), &exit, &change); + Bind(&change); + { + Branch(Int32Equal(oldSlotValue, Int32(0)), &exit, &resetSlot); + } + Bind(&resetSlot); + { + GateRef nonType = IntToTaggedInt(Int32(0)); + SetValueToTaggedArray(VariableType::JS_ANY(), glue, profileTypeInfo, slotId, nonType); + TryPreDumpInner(glue, func, profileTypeInfo); + Jump(&exit); + } + } + Bind(&uninitialized); + { + Branch(TaggedIsUndefined(slotValue), &updateSlot, &exit); + } + Bind(&updateSlot); + { + GateRef methodId = env->GetBuilder()->GetMethodId(target); + GateRef methodIdValue = IntToTaggedInt(TruncInt64ToInt32(methodId)); + SetValueToTaggedArray(VariableType::JS_ANY(), glue, profileTypeInfo, slotId, methodIdValue); + TryPreDumpInner(glue, func, profileTypeInfo); + Jump(&exit); + } + } + } + Bind(&exit); env->SubCfgExit(); } +GateRef ProfilerStubBuilder::IsProfileTypeInfoChanged(GateRef profileTypeInfo, ProfileOperation callback) +{ + if (callback.IsEmpty()) { + return Boolean(true); + } + return IsProfileTypeInfoChanged(profileTypeInfo); +} + GateRef ProfilerStubBuilder::UpdateTrackTypeInPropAttr(GateRef attr, GateRef value, ProfileOperation callback) { if (callback.IsEmpty()) { @@ -157,17 +312,12 @@ GateRef ProfilerStubBuilder::UpdateTrackTypeInPropAttr(GateRef attr, GateRef val { newTrackType = TaggedToTrackType(value); Label update(env); - Label nonFirst(env); - Branch(Equal(oldTrackType, Int32(static_cast(TrackType::NONE))), &update, &nonFirst); - Bind(&nonFirst); + Label merge(env); + Branch(Int32Equal(*newTrackType, Int32(static_cast(TrackType::TAGGED))), &update, &merge); + Bind(&merge); { - Label isNotEqual(env); - Branch(Equal(oldTrackType, *newTrackType), &exit, &isNotEqual); - Bind(&isNotEqual); - { - newTrackType = Int32(static_cast(TrackType::TAGGED)); - Jump(&update); - } + newTrackType = Int32Or(oldTrackType, *newTrackType); + Branch(Int32Equal(oldTrackType, *newTrackType), &exit, &update); } Bind(&update); { @@ -187,16 +337,31 @@ void ProfilerStubBuilder::UpdatePropAttrIC( if (callback.IsEmpty()) { return; } + auto env = GetEnvironment(); + Label entry(env); + env->SubCfgEntry(&entry); + Label exit(env); + Label updateLayout(env); + GateRef attrIndex = HandlerBaseGetAttrIndex(handler); GateRef hclass = LoadHClass(receiver); GateRef layout = GetLayoutFromHClass(hclass); GateRef propAttr = GetPropAttrFromLayoutInfo(layout, attrIndex); GateRef attr = GetInt32OfTInt(propAttr); - UpdatePropAttrWithValue(glue, receiver, layout, attr, attrIndex, value, callback); + GateRef newAttr = UpdateTrackTypeInPropAttr(attr, value, callback); + Branch(Equal(attr, newAttr), &exit, &updateLayout); + Bind(&updateLayout); + { + SetPropAttrToLayoutInfo(glue, layout, attrIndex, newAttr); + callback.TryPreDump(); + Jump(&exit); + } + Bind(&exit); + env->SubCfgExit(); } -void ProfilerStubBuilder::UpdatePropAttrWithValue(GateRef glue, GateRef receiver, GateRef layout, GateRef attr, - GateRef attrIndex, GateRef value, ProfileOperation callback) +void ProfilerStubBuilder::UpdatePropAttrWithValue( + GateRef glue, GateRef layout, GateRef attr, GateRef attrIndex, GateRef value, ProfileOperation callback) { if (callback.IsEmpty()) { return; @@ -211,7 +376,6 @@ void ProfilerStubBuilder::UpdatePropAttrWithValue(GateRef glue, GateRef receiver Bind(&updateLayout); { SetPropAttrToLayoutInfo(glue, layout, attrIndex, newAttr); - callback.ProfileObjLayoutByStore(receiver); Jump(&exit); } Bind(&exit); @@ -255,4 +419,125 @@ GateRef ProfilerStubBuilder::TaggedToTrackType(GateRef value) env->SubCfgExit(); return ret; } + +void ProfilerStubBuilder::ProfileBranch(GateRef glue, GateRef pc, GateRef func, GateRef profileTypeInfo, bool isTrue) +{ + auto env = GetEnvironment(); + Label subEntry(env); + env->SubCfgEntry(&subEntry); + Label profiler(env); + Label hasSlot(env); + Label currentIsTrue(env); + Label currentIsFalse(env); + Label genCurrentWeight(env); + Label compareLabel(env); + Label updateSlot(env); + Label preProfile(env); + Label needUpdate(env); + Label exit(env); + DEFVARIABLE(oldPrama, VariableType::INT32(), Int32(PGOSampleType::None())); + DEFVARIABLE(newTrue, VariableType::INT32(), isTrue ? Int32(1) : Int32(0)); + DEFVARIABLE(newFalse, VariableType::INT32(), isTrue ? Int32(0) : Int32(1)); + + Branch(TaggedIsUndefined(profileTypeInfo), &exit, &profiler); + Bind(&profiler); + { + GateRef slotId = ZExtInt8ToInt32(Load(VariableType::INT8(), pc, IntPtr(1))); + GateRef slotValue = GetValueFromTaggedArray(profileTypeInfo, slotId); + Branch(TaggedIsHole(slotValue), &exit, &hasSlot); // ishole -- isundefined + Bind(&hasSlot); + { + Branch(TaggedIsInt(slotValue), &compareLabel, &updateSlot); + Bind(&compareLabel); + { + GateRef oldSlotValue = TaggedGetInt(slotValue); + GateRef oldTrue = Int32LSR(oldSlotValue, Int32(PGOSampleType::WEIGHT_TRUE_START_BIT)); + GateRef oldFalse = Int32LSR(oldSlotValue, Int32(PGOSampleType::WEIGHT_START_BIT)); + oldFalse = Int32And(oldFalse, Int32(PGOSampleType::WEIGHT_MASK)); + oldPrama = Int32And(oldSlotValue, Int32(PGOSampleType::AnyType())); + auto condition = BoolAnd(Int32LessThan(oldTrue, Int32(PGOSampleType::WEIGHT_MASK)), + Int32LessThan(oldFalse, Int32(PGOSampleType::WEIGHT_MASK))); + Branch(condition, &needUpdate, &exit); // 2000: limit + Bind(&needUpdate); + { + newTrue = Int32Add(*newTrue, oldTrue); + newFalse = Int32Add(*newFalse, oldFalse); + Jump(&updateSlot); + } + } + Bind(&updateSlot); + { + GateRef newSlotValue = + Int32Or(*oldPrama, Int32LSL(*newTrue, Int32(PGOSampleType::WEIGHT_TRUE_START_BIT))); + newSlotValue = Int32Or(newSlotValue, Int32LSL(*newFalse, Int32(PGOSampleType::WEIGHT_START_BIT))); + SetValueToTaggedArray(VariableType::JS_ANY(), glue, profileTypeInfo, + slotId, IntToTaggedInt(newSlotValue)); + auto totalCount = Int32Add(*newTrue, *newFalse); + auto mask = Int32(0x1FF); + Label updateFinal(env); + Branch(Int32Equal(Int32And(totalCount, mask), Int32(0)), &preProfile, &updateFinal); + Bind(&updateFinal); + { + auto isFinal = BoolOr(Int32Equal(*newTrue, Int32(PGOSampleType::WEIGHT_MASK)), + Int32Equal(*newFalse, Int32(PGOSampleType::WEIGHT_MASK))); + Branch(isFinal, &preProfile, &exit); + } + } + Bind(&preProfile); + { + TryPreDumpInner(glue, func, profileTypeInfo); + Jump(&exit); + } + } + } + Bind(&exit); + env->SubCfgExit(); +} + +void ProfilerStubBuilder::TryPreDumpInner(GateRef glue, GateRef func, GateRef profileTypeInfo) +{ + auto env = GetEnvironment(); + Label subEntry(env); + env->SubCfgEntry(&subEntry); + Label resetPeriodCounter(env); + Label exit(env); + Branch(IsProfileTypeInfoChanged(profileTypeInfo), &exit, &resetPeriodCounter); + Bind(&resetPeriodCounter); + { + GateRef periodCounterOffset = GetBitFieldOffsetFromProfileTypeInfo(profileTypeInfo); + GateRef newCount = Int32(0); + Store(VariableType::INT32(), glue, profileTypeInfo, periodCounterOffset, newCount); + CallRuntime(glue, RTSTUB_ID(PGOPreDump), { func }); + Jump(&exit); + } + Bind(&exit); + env->SubCfgExit(); +} + +GateRef ProfilerStubBuilder::GetSlotID(GateRef pc, SlotIDFormat format) +{ + if (format == SlotIDFormat::IMM16) { + auto hight = Load(VariableType::INT8(), pc, IntPtr(2)); // 2 : skip 1 byte of bytecode + hight = Int16LSL(ZExtInt8ToInt16(hight), Int16(8)); // 8 : set as high 8 bits + auto low = Load(VariableType::INT8(), pc, IntPtr(1)); + auto result = Int16Add(hight, ZExtInt8ToInt16(low)); + return ZExtInt16ToInt32(result); + } + return ZExtInt8ToInt32(Load(VariableType::INT8(), pc, IntPtr(1))); +} + +GateRef ProfilerStubBuilder::GetBitFieldOffsetFromProfileTypeInfo(GateRef profileTypeInfo) +{ + auto length = GetLengthOfTaggedArray(profileTypeInfo); + auto index = Int32Sub(length, Int32(ProfileTypeInfo::BIT_FIELD_INDEX)); + auto indexOffset = PtrMul(ZExtInt32ToPtr(index), IntPtr(JSTaggedValue::TaggedTypeSize())); + return PtrAdd(indexOffset, IntPtr(TaggedArray::DATA_OFFSET)); +} + +GateRef ProfilerStubBuilder::IsProfileTypeInfoChanged(GateRef profileTypeInfo) +{ + GateRef periodCounterOffset = GetBitFieldOffsetFromProfileTypeInfo(profileTypeInfo); + GateRef count = Load(VariableType::INT32(), profileTypeInfo, periodCounterOffset); + return Int32Equal(count, Int32(ProfileTypeInfo::CHANGED_PEROID_COUNT)); +} } // namespace panda::ecmascript::kungfu diff --git a/ecmascript/compiler/profiler_stub_builder.h b/ecmascript/compiler/profiler_stub_builder.h index e795b2746eecb862bc46b91629259757ebc7243f..f2d29b55467bc60220390a171bf8242bb87e75e9 100644 --- a/ecmascript/compiler/profiler_stub_builder.h +++ b/ecmascript/compiler/profiler_stub_builder.h @@ -29,19 +29,41 @@ public: NO_COPY_SEMANTIC(ProfilerStubBuilder); void GenerateCircuit() override {} - void PGOProfiler( - GateRef glue, GateRef pc, GateRef func, GateRef profileTypeInfo, GateRef value, OperationType type); - void ProfileCall(GateRef glue, GateRef func); - void ProfileOpType(GateRef glue, GateRef pc, GateRef func, GateRef profileTypeInfo, GateRef type); - void ProfileDefineClass(GateRef glue, GateRef pc, GateRef func, GateRef constructor); - void ProfileObjLayout(GateRef glue, GateRef pc, GateRef func, GateRef object, GateRef store); + void PGOProfiler(GateRef glue, GateRef pc, GateRef func, GateRef profileTypeInfo, + const std::vector &values, SlotIDFormat format, OperationType type); + + void TryDump(GateRef glue, GateRef func, GateRef profileTypeInfo); + void TryPreDump(GateRef glue, GateRef func, GateRef profileTypeInfo); + + void ProfileCall( + GateRef glue, GateRef pc, GateRef func, GateRef target, GateRef profileTypeInfo, SlotIDFormat format); + void ProfileOpType( + GateRef glue, GateRef pc, GateRef func, GateRef profileTypeInfo, GateRef type, SlotIDFormat format); + void ProfileDefineClass( + GateRef glue, GateRef pc, GateRef func, GateRef constructor, GateRef profileTypeInfo, SlotIDFormat format); + void ProfileCreateObject( + GateRef glue, GateRef pc, GateRef func, GateRef newObj, GateRef profileTypeInfo, SlotIDFormat format); + void ProfileBranch(GateRef glue, GateRef pc, GateRef func, GateRef profileTypeInfo, bool isTrue); GateRef UpdateTrackTypeInPropAttr(GateRef attr, GateRef value, ProfileOperation callback); void UpdatePropAttrIC(GateRef glue, GateRef receiver, GateRef value, GateRef handler, ProfileOperation callback); - void UpdatePropAttrWithValue(GateRef glue, GateRef receiver, GateRef layout, GateRef attr, GateRef attrIndex, - GateRef value, ProfileOperation callback); + void UpdatePropAttrWithValue( + GateRef glue, GateRef layout, GateRef attr, GateRef attrIndex, GateRef value, ProfileOperation callback); + + GateRef IsProfileTypeInfoChanged(GateRef profileTypeInfo, ProfileOperation callback); private: + static constexpr size_t MAX_PROFILE_CALL_COUNT = 10000; + static constexpr size_t MIN_PROFILE_CALL_INTERVAL = 5; + static constexpr size_t BITS_OF_WORD = 8; + static constexpr size_t HIGH_WORD_OFFSET = 2; + static constexpr size_t LOW_WORD_OFFSET = 1; + + void TryPreDumpInner(GateRef glue, GateRef func, GateRef profileTypeInfo); + + GateRef GetSlotID(GateRef pc, SlotIDFormat format); + GateRef GetBitFieldOffsetFromProfileTypeInfo(GateRef profileTypeInfo); + GateRef IsProfileTypeInfoChanged(GateRef profileTypeInfo); GateRef TaggedToTrackType(GateRef value); }; } // namespace panda::ecmascript::kungfu diff --git a/ecmascript/compiler/range_analysis.cpp b/ecmascript/compiler/range_analysis.cpp index cc0d5ae18e341885a5d319d18dd4d5b9b81d656b..e6ec56f3b83d8bc09a3fcc84531977e12d3b7cc8 100644 --- a/ecmascript/compiler/range_analysis.cpp +++ b/ecmascript/compiler/range_analysis.cpp @@ -59,6 +59,12 @@ GateRef RangeAnalysis::VisitGate(GateRef gate) return VisitIndexCheck(gate); case OpCode::LOAD_ARRAY_LENGTH: return VisitLoadArrayLength(gate); + case OpCode::LOAD_STRING_LENGTH: + return VisitLoadStringLength(gate); + case OpCode::LOAD_TYPED_ARRAY_LENGTH: + return VisitLoadTypedArrayLength(gate); + case OpCode::RANGE_GUARD: + return VisitRangeGuard(gate); default: return VisitOthers(gate); } @@ -135,6 +141,12 @@ GateRef RangeAnalysis::VisitTypedBinaryOp(GateRef gate) case TypedBinOp::TYPED_SUB: range = GetRangeOfCalculate(gate); break; + case TypedBinOp::TYPED_MOD: + range = GetRangeOfCalculate(gate); + break; + case TypedBinOp::TYPED_MUL: + range = GetRangeOfCalculate(gate); + break; case TypedBinOp::TYPED_SHR: range = GetRangeOfShift(gate); break; @@ -150,7 +162,10 @@ GateRef RangeAnalysis::VisitTypedBinaryOp(GateRef gate) GateRef RangeAnalysis::VisitIndexCheck(GateRef gate) { ASSERT(IsInt32Type(gate)); - return UpdateRange(gate, RangeInfo(0, INT32_MAX - 1)); + auto value = GetRange(acc_.GetValueIn(gate, 0)); + auto largerRange = RangeInfo(0, INT32_MAX - 1); + auto intersected = value.intersection(largerRange); + return UpdateRange(gate, intersected); } GateRef RangeAnalysis::VisitLoadArrayLength(GateRef gate) @@ -159,10 +174,28 @@ GateRef RangeAnalysis::VisitLoadArrayLength(GateRef gate) return UpdateRange(gate, RangeInfo(0, INT32_MAX)); } +GateRef RangeAnalysis::VisitLoadStringLength(GateRef gate) +{ + ASSERT(IsInt32Type(gate)); + return UpdateRange(gate, RangeInfo(0, INT32_MAX)); +} + +GateRef RangeAnalysis::VisitLoadTypedArrayLength(GateRef gate) +{ + int32_t max = IsOnHeap() ? RangeInfo::TYPED_ARRAY_ONHEAP_MAX : INT32_MAX; + return UpdateRange(gate, RangeInfo(0, max)); +} + +GateRef RangeAnalysis::VisitRangeGuard(GateRef gate) +{ + auto left = acc_.GetFirstValue(gate); + auto right = acc_.GetSecondValue(gate); + return UpdateRange(gate, RangeInfo(left, right)); +} + template RangeInfo RangeAnalysis::GetRangeOfCalculate(GateRef gate) { - ASSERT((Op == TypedBinOp::TYPED_ADD) || (Op == TypedBinOp::TYPED_SUB)); auto left = GetRange(acc_.GetValueIn(gate, 0)); auto right = GetRange(acc_.GetValueIn(gate, 1)); if (left.IsNone() || right.IsNone()) { @@ -173,6 +206,10 @@ RangeInfo RangeAnalysis::GetRangeOfCalculate(GateRef gate) return left + right; case TypedBinOp::TYPED_SUB: return left - right; + case TypedBinOp::TYPED_MOD: + return left % right; + case TypedBinOp::TYPED_MUL: + return left * right; default: return RangeInfo::ANY(); } @@ -232,10 +269,10 @@ RangeInfo RangeAnalysis::GetRangeOfCompare(GateRef gate, GateRef value, bool fla ASSERT((left == value) || (right == value)); bool swap = right == value; if (flag) { - op = GateMetaData::GetRevCompareOp(op); + op = TypedBinaryMetaData::GetRevCompareOp(op); } if (swap) { - op = GateMetaData::GetSwapCompareOp(op); + op = TypedBinaryMetaData::GetSwapCompareOp(op); } auto range = GetRange(swap ? left : right); if (range.IsNone()) { @@ -261,12 +298,6 @@ RangeInfo RangeAnalysis::GetRangeOfCompare(GateRef gate, GateRef value, bool fla } } -void RangeAnalysis::Run() -{ - // visit gate in RPO, propagate range info - VisitGraph(); -} - void RangeAnalysis::PrintRangeInfo() const { std::vector gateList; @@ -302,6 +333,12 @@ void RangeAnalysis::PrintRangeInfo() const case TypedBinOp::TYPED_ASHR: log += " ashr"; break; + case TypedBinOp::TYPED_MOD: + log += " mod"; + break; + case TypedBinOp::TYPED_MUL: + log += " mul"; + break; default: log += " other"; break; diff --git a/ecmascript/compiler/range_analysis.h b/ecmascript/compiler/range_analysis.h index fdbe79503094a3116d2c33adb45fa481ab5e8473..69d389bd269d6317a0dee8e69354fa39628352ed 100644 --- a/ecmascript/compiler/range_analysis.h +++ b/ecmascript/compiler/range_analysis.h @@ -17,24 +17,28 @@ #define ECMASCRIPT_COMPILER_RANGE_ANALYSIS_H #include "ecmascript/compiler/circuit_builder.h" +#include "ecmascript/compiler/combined_pass_visitor.h" #include "ecmascript/compiler/gate_accessor.h" -#include "ecmascript/compiler/gate_meta_data.h" -#include "ecmascript/compiler/graph_visitor.h" +#include "ecmascript/compiler/mcr_gate_meta_data.h" #include "ecmascript/compiler/number_gate_info.h" #include "ecmascript/mem/chunk_containers.h" namespace panda::ecmascript::kungfu { -class RangeAnalysis : public GraphVisitor { +class RangeAnalysis : public PassVisitor { public: - RangeAnalysis(Circuit *circuit, Chunk* chunk, ChunkVector& typeInfos, - ChunkVector& rangeInfos) - : GraphVisitor(circuit, chunk), acc_(circuit), builder_(circuit), - typeInfos_(typeInfos), rangeInfos_(rangeInfos) {} - void Run(); + RangeAnalysis(Circuit* circuit, RPOVisitor* visitor, Chunk* chunk, ChunkVector& typeInfos, + ChunkVector& rangeInfos, bool onHeapCheck) + : PassVisitor(circuit, chunk, visitor), acc_(circuit), builder_(circuit), + typeInfos_(typeInfos), rangeInfos_(rangeInfos), onHeapCheck_(onHeapCheck) {} GateRef VisitGate(GateRef gate); void PrintRangeInfo() const; private: + bool IsOnHeap() const + { + return onHeapCheck_; + } + GateRef VisitPhi(GateRef gate); GateRef VisitTypedBinaryOp(GateRef gate); GateRef VisitTypedUnaryOp(GateRef gate); @@ -42,6 +46,9 @@ private: GateRef VisitOthers(GateRef gate); GateRef VisitIndexCheck(GateRef gate); GateRef VisitLoadArrayLength(GateRef gate); + GateRef VisitLoadStringLength(GateRef gate); + GateRef VisitLoadTypedArrayLength(GateRef gate); + GateRef VisitRangeGuard(GateRef gate); template RangeInfo GetRangeOfCalculate(GateRef gate); template @@ -55,6 +62,7 @@ private: CircuitBuilder builder_; ChunkVector& typeInfos_; ChunkVector& rangeInfos_; + bool onHeapCheck_ {false}; }; } // panda::ecmascript::kungfu #endif // ECMASCRIPT_COMPILER_RANGE_ANALYSIS_H diff --git a/ecmascript/compiler/range_guard.cpp b/ecmascript/compiler/range_guard.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c3cb335c480d1a76c70c234490f2adc3638d8b8b --- /dev/null +++ b/ecmascript/compiler/range_guard.cpp @@ -0,0 +1,196 @@ +/* + * 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/compiler/range_guard.h" + +namespace panda::ecmascript::kungfu { + +void RangeGuard::Initialize() +{ + dependChains_.resize(circuit_->GetMaxGateId() + 1, nullptr); // 1: +1 for size + GateRef entry = acc_.GetDependRoot(); + VisitDependEntry(entry); +} + +GateRef RangeGuard::VisitGate(GateRef gate) +{ + auto op = acc_.GetOpCode(gate); + switch (op) { + case OpCode::VALUE_SELECTOR: + case OpCode::TYPED_BINARY_OP: + case OpCode::TYPED_UNARY_OP: + case OpCode::INDEX_CHECK: { + return TryApplyRangeGuardGate(gate); + } + case OpCode::DEPEND_SELECTOR: { + return TraverseDependSelector(gate); + } + default: { + if (acc_.GetDependCount(gate) == 1) { // 1: depend in is 1 + return TraverseOthers(gate); + } + break; + } + } + return Circuit::NullGate(); +} + +GateRef RangeGuard::TraverseOthers(GateRef gate) +{ + ASSERT(acc_.GetDependCount(gate) >= 1); + auto depIn = acc_.GetDep(gate); + auto dependChain = GetDependChain(depIn); + if (dependChain == nullptr) { + return Circuit::NullGate(); + } + + return UpdateDependChain(gate, dependChain); +} + +GateRef RangeGuard::TraverseDependSelector(GateRef gate) +{ + auto state = acc_.GetState(gate); + if (acc_.IsLoopHead(state)) { + return TraverseOthers(gate); + } + + auto dependCount = acc_.GetDependCount(gate); + for (size_t i = 0; i < dependCount; ++i) { + auto depend = acc_.GetDep(gate, i); + auto dependChain = GetDependChain(depend); + if (dependChain == nullptr) { + return Circuit::NullGate(); + } + } + + // all depend done. + auto depend = acc_.GetDep(gate); + auto dependChain = GetDependChain(depend); + DependChains* copy = new (chunk_) DependChains(chunk_); + copy->CopyFrom(dependChain); + for (size_t i = 1; i < dependCount; ++i) { // 1: second in + auto dependIn = acc_.GetDep(gate, i); + auto tempChain = GetDependChain(dependIn); + copy->Merge(tempChain); + } + return UpdateDependChain(gate, copy); +} + +GateRef RangeGuard::TryApplyRangeGuardForLength(DependChains* dependChain, GateRef gate, GateRef input) +{ + ASSERT(dependChain != nullptr); + uint32_t length = dependChain->FoundIndexCheckedForLength(this, input); + if (length) { // when length not equal to 0, then Found the IndexCheck Success + Environment env(gate, circuit_, &builder_); + // If the IndexCheck before the ArrayLength used, the ArrayLength must start by 1. + auto rangeGuardGate = builder_.RangeGuard(input, 1, length); + return rangeGuardGate; + } + return Circuit::NullGate(); +} + +GateRef RangeGuard::TryApplyRangeGuardForIndex(DependChains* dependChain, GateRef gate, GateRef input) +{ + ASSERT(dependChain != nullptr); + uint32_t length = dependChain->FoundIndexCheckedForIndex(this, input); + if (length) { // when length not equal to 0, then Found the IndexCheck Success + Environment env(gate, circuit_, &builder_); + // If the IndexCheck used in the Array, the index must in the Array range. + auto rangeGuardGate = builder_.RangeGuard(input, 0, length); + return rangeGuardGate; + } + return Circuit::NullGate(); +} + +GateRef RangeGuard::TryApplyRangeGuardGate(GateRef gate) +{ + if (acc_.GetDependCount(gate) < 1) { + return Circuit::NullGate(); + } + + auto depIn = acc_.GetDep(gate); + auto dependChain = GetDependChain(depIn); + // dependChain is null + if (dependChain == nullptr) { + return Circuit::NullGate(); + } + + auto numIns = acc_.GetInValueCount(gate); + for (size_t i = 0; i < numIns; ++i) { + auto originalInput = acc_.GetValueIn(gate, i); + auto originalInputOpcode = acc_.GetOpCode(originalInput); + auto rangeGuardGate = Circuit::NullGate(); + if (originalInputOpcode == OpCode::LOAD_TYPED_ARRAY_LENGTH || + originalInputOpcode == OpCode::LOAD_ARRAY_LENGTH) { + rangeGuardGate = TryApplyRangeGuardForLength(dependChain, gate, originalInput); + } else if(originalInputOpcode != OpCode::CONSTANT && rangeGuardGate == Circuit::NullGate()) { + rangeGuardGate = TryApplyRangeGuardForIndex(dependChain, gate, originalInput); + } + if (rangeGuardGate != Circuit::NullGate()) { + acc_.ReplaceValueIn(gate, rangeGuardGate, i); + } + } + dependChain = dependChain->UpdateNode(gate); + return UpdateDependChain(gate, dependChain); +} + +GateRef RangeGuard::VisitDependEntry(GateRef gate) +{ + auto empty = new (chunk_) DependChains(chunk_); + return UpdateDependChain(gate, empty); +} + +GateRef RangeGuard::UpdateDependChain(GateRef gate, DependChains* dependChain) +{ + ASSERT(dependChain != nullptr); + auto oldDependChain = GetDependChain(gate); + if (dependChain->Equals(oldDependChain)) { + return Circuit::NullGate(); + } + dependChains_[acc_.GetId(gate)] = dependChain; + return gate; +} + +uint32_t RangeGuard::CheckIndexCheckLengthInput(GateRef lhs, GateRef rhs) +{ + auto lhsOpcode = acc_.GetOpCode(lhs); + if (lhsOpcode == OpCode::INDEX_CHECK) { + auto indexCheckLengthInput = acc_.GetValueIn(lhs, 0); // length + auto indexCheckLengthInputOpcode = acc_.GetOpCode(indexCheckLengthInput); + if (indexCheckLengthInput == rhs && indexCheckLengthInputOpcode == OpCode::LOAD_TYPED_ARRAY_LENGTH) { + return RangeInfo::TYPED_ARRAY_ONHEAP_MAX; + } else if (indexCheckLengthInput == rhs && indexCheckLengthInputOpcode == OpCode::LOAD_ARRAY_LENGTH) { + return INT32_MAX; + } + } + return 0; +} + +uint32_t RangeGuard::CheckIndexCheckIndexInput(GateRef lhs, GateRef rhs) +{ + auto lhsOpcode = acc_.GetOpCode(lhs); + if (lhsOpcode == OpCode::INDEX_CHECK) { + auto indexCheckLengthInput = acc_.GetValueIn(lhs, 0); // length + auto indexCheckIndexInput = acc_.GetValueIn(lhs, 1); // index + auto indexCheckLengthInputOpcode = acc_.GetOpCode(indexCheckLengthInput); + // TYPED_ARRAY + if (indexCheckIndexInput == rhs && indexCheckLengthInputOpcode == OpCode::LOAD_TYPED_ARRAY_LENGTH) { + return RangeInfo::TYPED_ARRAY_ONHEAP_MAX; + } else if (indexCheckIndexInput == rhs && indexCheckLengthInputOpcode == OpCode::LOAD_ARRAY_LENGTH) { // ARRAY + return INT32_MAX; + } + } + return 0; +} +} // namespace panda::ecmascript::kungfu diff --git a/ecmascript/compiler/range_guard.h b/ecmascript/compiler/range_guard.h new file mode 100644 index 0000000000000000000000000000000000000000..e5794ff5a669172493f4b8535f46cf4dfe3c1f5f --- /dev/null +++ b/ecmascript/compiler/range_guard.h @@ -0,0 +1,65 @@ +/* + * 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_COMPILER_RANGE_GUARD_H +#define ECMASCRIPT_COMPILER_RANGE_GUARD_H + +#include "ecmascript/compiler/circuit_builder.h" +#include "ecmascript/compiler/gate_accessor.h" +#include "ecmascript/compiler/pass_manager.h" +#include "ecmascript/compiler/base/depend_chain_helper.h" +#include "ecmascript/mem/chunk_containers.h" +#include "ecmascript/compiler/number_gate_info.h" + +namespace panda::ecmascript::kungfu { +class DependChains; +class RangeGuard : public PassVisitor { +public: + RangeGuard(Circuit* circuit, RPOVisitor* visitor, Chunk* chunk) + : PassVisitor(circuit, chunk, visitor), circuit_(circuit), + builder_(circuit), dependChains_(chunk) {} + + ~RangeGuard() = default; + + void Initialize() override; + GateRef VisitGate(GateRef gate) override; + bool CheckInputSource(GateRef lhs, GateRef rhs); + uint32_t CheckIndexCheckLengthInput(GateRef lhs, GateRef rhs); + uint32_t CheckIndexCheckIndexInput(GateRef lhs, GateRef rhs); +private: + + DependChains* GetDependChain(GateRef dependIn) + { + size_t idx = acc_.GetId(dependIn); + ASSERT(idx <= circuit_->GetMaxGateId()); + return dependChains_[idx]; + } + + GateRef VisitDependEntry(GateRef gate); + GateRef UpdateDependChain(GateRef gate, DependChains* dependInfo); + GateRef TryApplyRangeGuardForLength(DependChains* dependInfo, GateRef gate, GateRef input); + GateRef TryApplyRangeGuardForIndex(DependChains* dependInfo, GateRef gate, GateRef input); + GateRef TryApplyRangeGuardGate(GateRef gate); + GateRef TraverseOthers(GateRef gate); + GateRef TraverseDependSelector(GateRef gate); + + Circuit* circuit_; + CircuitBuilder builder_; + ChunkVector dependChains_; + + friend class RangeInfo; +}; +} // panda::ecmascript::kungfu +#endif // ECMASCRIPT_COMPILER_RANGE_GUARD_H \ No newline at end of file diff --git a/ecmascript/compiler/gate_meta_data.cpp b/ecmascript/compiler/share_gate_meta_data.cpp similarity index 80% rename from ecmascript/compiler/gate_meta_data.cpp rename to ecmascript/compiler/share_gate_meta_data.cpp index fb19b9625af06c427da01e7fbe037bc6485905f0..95e30fd791a3b6695850f22f8fb82dc736939c4c 100644 --- a/ecmascript/compiler/gate_meta_data.cpp +++ b/ecmascript/compiler/share_gate_meta_data.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * 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 @@ -14,97 +14,10 @@ */ #include "ecmascript/compiler/gate.h" -#include "ecmascript/compiler/gate_meta_data.h" +#include "ecmascript/compiler/share_gate_meta_data.h" #include "ecmascript/compiler/gate_meta_data_builder.h" namespace panda::ecmascript::kungfu { -std::string MachineTypeToStr(MachineType machineType) -{ - switch (machineType) { - case NOVALUE: - return "NOVALUE"; - case ANYVALUE: - return "ANYVALUE"; - case I1: - return "I1"; - case I8: - return "I8"; - case I16: - return "I16"; - case I32: - return "I32"; - case I64: - return "I64"; - case F32: - return "F32"; - case F64: - return "F64"; - default: - return "???"; - } -} - -std::string GateMetaData::TypedBinOpToStr(TypedBinOp typedBinOp) const -{ - switch (typedBinOp) { - case TypedBinOp::TYPED_ADD: { - return "Add"; - } - case TypedBinOp::TYPED_SUB: { - return "Sub"; - } - case TypedBinOp::TYPED_MUL: { - return "Mul"; - } - case TypedBinOp::TYPED_LESS: { - return "Less"; - } - case TypedBinOp::TYPED_LESSEQ: { - return "LessOrEqual"; - } - case TypedBinOp::TYPED_GREATER: { - return "Greater"; - } - case TypedBinOp::TYPED_GREATEREQ: { - return "GreaterOrEqual"; - } - case TypedBinOp::TYPED_EQ: { - return "Equal"; - } - case TypedBinOp::TYPED_NOTEQ: { - return "NotEqual"; - } - case TypedBinOp::TYPED_STRICTEQ: { - return "StrictEqual"; - } - case TypedBinOp::TYPED_SHL: { - return "Shl"; - } - case TypedBinOp::TYPED_SHR: { - return "Shr"; - } - case TypedBinOp::TYPED_ASHR: { - return "Ashr"; - } - case TypedBinOp::TYPED_AND: { - return "And"; - } - case TypedBinOp::TYPED_OR: { - return "Or"; - } - case TypedBinOp::TYPED_XOR: { - return "Xor"; - } - case TypedBinOp::TYPED_DIV: { - return "Div"; - } - case TypedBinOp::TYPED_EXP: { - return "Exp"; - } - default: - return "Unknown"; - } -} std::string GateMetaData::Str(OpCode opcode) { @@ -112,9 +25,11 @@ std::string GateMetaData::Str(OpCode opcode) #define GATE_NAME_MAP(NAME, OP, R, S, D, V) { OpCode::OP, #OP }, IMMUTABLE_META_DATA_CACHE_LIST(GATE_NAME_MAP) GATE_META_DATA_LIST_WITH_BOOL(GATE_NAME_MAP) + GATE_META_DATA_LIST_WITH_BOOL_VALUE_IN(GATE_NAME_MAP) GATE_META_DATA_LIST_WITH_SIZE(GATE_NAME_MAP) GATE_META_DATA_LIST_WITH_ONE_PARAMETER(GATE_NAME_MAP) GATE_META_DATA_LIST_WITH_PC_OFFSET(GATE_NAME_MAP) + GATE_META_DATA_LIST_FOR_CALL(GATE_NAME_MAP) GATE_META_DATA_LIST_WITH_PC_OFFSET_FIXED_VALUE(GATE_NAME_MAP) #undef GATE_NAME_MAP #define GATE_NAME_MAP(OP) { OpCode::OP, #OP }, @@ -256,6 +171,21 @@ bool GateMetaData::IsControlCase() const } } +bool GateMetaData::IsIfOrSwitchRelated() const +{ + switch (opcode_) { + case OpCode::IF_TRUE: + case OpCode::IF_FALSE: + case OpCode::SWITCH_CASE: + case OpCode::DEFAULT_CASE: + case OpCode::IF_SUCCESS: + case OpCode::IF_EXCEPTION: + return true; + default: + return false; + } +} + bool GateMetaData::IsLoopHead() const { return (opcode_ == OpCode::LOOP_BEGIN); @@ -266,6 +196,11 @@ bool GateMetaData::IsNop() const return (opcode_ == OpCode::NOP || opcode_ == OpCode::DEAD); } +bool GateMetaData::IsDead() const +{ + return opcode_ == OpCode::DEAD; +} + bool GateMetaData::IsConstant() const { return (opcode_ == OpCode::CONSTANT); @@ -276,27 +211,6 @@ bool GateMetaData::IsDependSelector() const return (opcode_ == OpCode::DEPEND_SELECTOR); } -bool GateMetaData::IsTypedOperator() const -{ - return (opcode_ == OpCode::TYPED_BINARY_OP) || (opcode_ == OpCode::TYPE_CONVERT) || - (opcode_ == OpCode::TYPED_UNARY_OP); -} - -bool GateMetaData::IsCheckWithTwoIns() const -{ - return (opcode_ == OpCode::OBJECT_TYPE_CHECK) || - (opcode_ == OpCode::INDEX_CHECK) || - (opcode_ == OpCode::TYPED_CALL_CHECK); -} - -bool GateMetaData::IsCheckWithOneIn() const -{ - return (opcode_ == OpCode::PRIMITIVE_TYPE_CHECK) || - (opcode_ == OpCode::HEAP_OBJECT_CHECK) || - (opcode_ == OpCode::STABLE_ARRAY_CHECK) || - (opcode_ == OpCode::TYPED_ARRAY_CHECK); -} - GateMetaBuilder::GateMetaBuilder(Chunk* chunk) : cache_(), chunk_(chunk) {} @@ -317,6 +231,15 @@ const GateMetaData* GateMetaBuilder::NAME(bool value) \ GATE_META_DATA_LIST_WITH_BOOL(DECLARE_GATE_META) #undef DECLARE_GATE_META +#define DECLARE_GATE_META_WITH_BOOL_VALUE_IN(NAME, OP, R, S, D, V) \ +const GateMetaData* GateMetaBuilder::NAME(size_t value, bool flag) \ +{ \ + auto meta = new (chunk_) BoolMetaData(OpCode::OP, R, S, D, V, flag); \ + return meta; \ +} +GATE_META_DATA_LIST_WITH_BOOL_VALUE_IN(DECLARE_GATE_META_WITH_BOOL_VALUE_IN) +#undef DECLARE_GATE_META_WITH_BOOL_VALUE_IN + #define DECLARE_GATE_META(NAME, OP, R, S, D, V) \ const GateMetaData* GateMetaBuilder::NAME(size_t value) \ { \ @@ -385,6 +308,16 @@ const GateMetaData* GateMetaBuilder::NAME(uint64_t value, uint64_t pcOffset) GATE_META_DATA_LIST_WITH_PC_OFFSET(DECLARE_GATE_META) #undef DECLARE_GATE_META +#define DECLARE_GATE_META_FOR_CALL(NAME, OP, R, S, D, V) \ +const GateMetaData* GateMetaBuilder::NAME(uint64_t value, uint64_t pcOffset, bool noGC) \ +{ \ + auto meta = new (chunk_) TypedCallMetaData(OpCode::OP, R, S, D, value, pcOffset, noGC); \ + meta->SetKind(GateMetaData::Kind::TYPED_CALL); \ + return meta; \ +} +GATE_META_DATA_LIST_FOR_CALL(DECLARE_GATE_META_FOR_CALL) +#undef DECLARE_GATE_META_FOR_CALL + #define DECLARE_GATE_META(NAME, OP, R, S, D, V) \ const GateMetaData* GateMetaBuilder::NAME(uint64_t pcOffset) const \ { \ diff --git a/ecmascript/compiler/gate_meta_data.h b/ecmascript/compiler/share_gate_meta_data.h similarity index 32% rename from ecmascript/compiler/gate_meta_data.h rename to ecmascript/compiler/share_gate_meta_data.h index e8744fd272a131ffb7b89591b382ca999e1af207..eadb1fb6926691f31ee42c21e0fd9e78dae20184 100644 --- a/ecmascript/compiler/gate_meta_data.h +++ b/ecmascript/compiler/share_gate_meta_data.h @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_COMPILER_GATE_META_DATA_H -#define ECMASCRIPT_COMPILER_GATE_META_DATA_H +#ifndef ECMASCRIPT_COMPILER_SHARE_GATE_META_DATA_H +#define ECMASCRIPT_COMPILER_SHARE_GATE_META_DATA_H #include @@ -23,60 +23,22 @@ #include "ecmascript/mem/chunk.h" #include "ecmascript/mem/chunk_containers.h" -#include "ecmascript/pgo_profiler/pgo_profiler_type.h" +#include "ecmascript/elements.h" +#include "ecmascript/pgo_profiler/types/pgo_profiler_type.h" #include "libpandabase/macros.h" +#include "ecmascript/compiler/share_opcodes.h" + namespace panda::ecmascript::kungfu { using GateRef = int32_t; -enum MachineType : uint8_t { // Bit width - NOVALUE = 0, - ANYVALUE, - ARCH, - FLEX, - I1, - I8, - I16, - I32, - I64, - F32, - F64, -}; - -enum class TypedBinOp : uint8_t { - TYPED_ADD = 0, - TYPED_SUB, - TYPED_MUL, - TYPED_DIV, - TYPED_MOD, - TYPED_LESS, - TYPED_LESSEQ, - TYPED_GREATER, - TYPED_GREATEREQ, - TYPED_EQ, - TYPED_NOTEQ, - TYPED_STRICTEQ, - TYPED_SHL, - TYPED_SHR, - TYPED_ASHR, - TYPED_AND, - TYPED_OR, - TYPED_XOR, - TYPED_EXP, -}; - -enum class TypedUnOp : uint8_t { - TYPED_NEG = 0, - TYPED_NOT, - TYPED_INC, - TYPED_DEC, - TYPED_ISFALSE, - TYPED_ISTRUE, -}; - -enum class TypedJumpOp : uint8_t { - TYPED_JEQZ = 0, - TYPED_JNEZ, -}; +using PGOSampleType = pgo::PGOSampleType; +using PGORWOpType = pgo::PGORWOpType; +enum class TypedBinOp : uint8_t; +enum class TypedUnOp : uint8_t; +enum class TypedJumpOp : uint8_t; +enum class TypedLoadOp : uint8_t; +enum class TypedStoreOp : uint8_t; +enum class TypedCallTargetCheckOp : uint8_t; #define GATE_META_DATA_DEOPT_REASON(V) \ V(NotInt, NOTINT) \ @@ -85,22 +47,26 @@ enum class TypedJumpOp : uint8_t { V(NotBool, NOTBOOL) \ V(NotHeapObject, NOTHEAPOBJECT) \ V(NotStableArray, NOTSARRAY) \ - V(NotF32Array, NOTF32ARRAY) \ + V(NotArray, NOTARRAY) \ + V(NotOnHeap, NOTONHEAP) \ V(InconsistentHClass, INCONSISTENTHCLASS) \ V(NotNewObj, NOTNEWOBJ) \ - V(NotArrayIndex, NOTARRAYIDX) \ - V(NotF32ArrayIndex, NOTF32ARRAYIDX) \ + V(NotLegalIndex, NOTLEGALIDX) \ V(NotIncOverflow, NOTINCOV) \ V(NotDecOverflow, NOTDECOV) \ V(NotNegativeOverflow, NOTNEGOV) \ V(NotCallTarget, NOTCALLTGT) \ V(NotJSCallTarget, NOTJSCALLTGT) \ + V(CowArray, COWARRAY) \ V(DivideZero, DIVZERO) \ - V(NegativeIndex, NEGTIVEINDEX) \ - V(LargeIndex, LARGEINDEX) \ V(InlineFail, INLINEFAIL) \ V(NotJSFastCallTarget, NOTJSFASTCALLTGT) \ - V(ModZero, MODZERO) + V(LexVarIsHole, LEXVARISHOLE) \ + V(ModZero, MODZERO) \ + V(Int32Overflow, INT32OVERFLOW) \ + V(NotString, NOTSTRING) \ + V(InconsistentType, INCONSISTENTTYPE) \ + V(NotNull, NOTNULL) enum class DeoptType : uint8_t { NOTCHECK = 0, @@ -109,243 +75,6 @@ enum class DeoptType : uint8_t { #undef DECLARE_DEOPT_TYPE }; -enum class ICmpCondition : uint8_t { - EQ = 1, - UGT, - UGE, - ULT, - ULE, - NE, - SGT, - SGE, - SLT, - SLE, -}; - -enum class FCmpCondition : uint8_t { - ALW_FALSE = 0, - OEQ, - OGT, - OGE, - OLT, - OLE, - ONE, - ORD, - UNO, - UEQ, - UGT, - UGE, - ULT, - ULE, - UNE, - ALW_TRUE, -}; - -enum class TypedStoreOp : uint8_t { - ARRAY_STORE_ELEMENT = 0, - FLOAT32ARRAY_STORE_ELEMENT, -}; - -enum class TypedLoadOp : uint8_t { - ARRAY_LOAD_ELEMENT = 0, - FLOAT32ARRAY_LOAD_ELEMENT, -}; - -std::string MachineTypeToStr(MachineType machineType); - -#define BINARY_GATE_META_DATA_CACHE_LIST(V) \ - V(Add, ADD, GateFlags::NONE_FLAG, 0, 0, 2) \ - V(Sub, SUB, GateFlags::NONE_FLAG, 0, 0, 2) \ - V(Mul, MUL, GateFlags::NONE_FLAG, 0, 0, 2) \ - V(Exp, EXP, GateFlags::NONE_FLAG, 0, 0, 2) \ - V(Sdiv, SDIV, GateFlags::NONE_FLAG, 0, 0, 2) \ - V(Smod, SMOD, GateFlags::NONE_FLAG, 0, 0, 2) \ - V(Udiv, UDIV, GateFlags::NONE_FLAG, 0, 0, 2) \ - V(Umod, UMOD, GateFlags::NONE_FLAG, 0, 0, 2) \ - V(Fdiv, FDIV, GateFlags::NONE_FLAG, 0, 0, 2) \ - V(Fmod, FMOD, GateFlags::NONE_FLAG, 0, 0, 2) \ - V(And, AND, GateFlags::NONE_FLAG, 0, 0, 2) \ - V(Xor, XOR, GateFlags::NONE_FLAG, 0, 0, 2) \ - V(Or, OR, GateFlags::NONE_FLAG, 0, 0, 2) \ - V(Lsl, LSL, GateFlags::NONE_FLAG, 0, 0, 2) \ - V(Lsr, LSR, GateFlags::NONE_FLAG, 0, 0, 2) \ - V(Asr, ASR, GateFlags::NONE_FLAG, 0, 0, 2) \ - V(AddWithOverflow, ADD_WITH_OVERFLOW, GateFlags::NONE_FLAG, 0, 0, 2) \ - V(SubWithOverflow, SUB_WITH_OVERFLOW, GateFlags::NONE_FLAG, 0, 0, 2) \ - V(MulWithOverflow, MUL_WITH_OVERFLOW, GateFlags::NONE_FLAG, 0, 0, 2) \ - V(ExtractValue, EXTRACT_VALUE, GateFlags::NONE_FLAG, 0, 0, 2) \ - V(Sqrt, SQRT, GateFlags::NO_WRITE, 0, 0, 1) \ - V(Int32CheckRightIsZero, INT32_CHECK_RIGHT_IS_ZERO, GateFlags::CHECKABLE, 1, 1, 1) \ - V(Float64CheckRightIsZero, FLOAT64_CHECK_RIGHT_IS_ZERO, GateFlags::CHECKABLE, 1, 1, 1) \ - V(ValueCheckNegOverflow, VALUE_CHECK_NEG_OVERFLOW, GateFlags::CHECKABLE, 1, 1, 1) \ - V(NegativeIndexCheck, NEGATIVE_INDEX_CHECK, GateFlags::CHECKABLE, 1, 1, 1) \ - V(LargeIndexCheck, LARGE_INDEX_CHECK, GateFlags::CHECKABLE, 1, 1, 2) \ - V(OverflowCheck, OVERFLOW_CHECK, GateFlags::CHECKABLE, 1, 1, 1) \ - V(Int32UnsignedUpperBoundCheck, INT32_UNSIGNED_UPPER_BOUND_CHECK, GateFlags::CHECKABLE, 1, 1, 2) \ - V(Int32DivWithCheck, INT32_DIV_WITH_CHECK, GateFlags::CHECKABLE, 1, 1, 2) - -#define UNARY_GATE_META_DATA_CACHE_LIST(V) \ - V(Zext, ZEXT, GateFlags::NONE_FLAG, 0, 0, 1) \ - V(Sext, SEXT, GateFlags::NONE_FLAG, 0, 0, 1) \ - V(Trunc, TRUNC, GateFlags::NONE_FLAG, 0, 0, 1) \ - V(Fext, FEXT, GateFlags::NONE_FLAG, 0, 0, 1) \ - V(Ftrunc, FTRUNC, GateFlags::NONE_FLAG, 0, 0, 1) \ - V(Rev, REV, GateFlags::NONE_FLAG, 0, 0, 1) \ - V(TruncFloatToInt64, TRUNC_FLOAT_TO_INT64, GateFlags::NONE_FLAG, 0, 0, 1) \ - V(TruncFloatToInt32, TRUNC_FLOAT_TO_INT32, GateFlags::NONE_FLAG, 0, 0, 1) \ - V(TaggedToInt64, TAGGED_TO_INT64, GateFlags::NONE_FLAG, 0, 0, 1) \ - V(Int64ToTagged, INT64_TO_TAGGED, GateFlags::NONE_FLAG, 0, 0, 1) \ - V(SignedIntToFloat, SIGNED_INT_TO_FLOAT, GateFlags::NONE_FLAG, 0, 0, 1) \ - V(UnsignedIntToFloat, UNSIGNED_INT_TO_FLOAT, GateFlags::NONE_FLAG, 0, 0, 1) \ - V(FloatToSignedInt, FLOAT_TO_SIGNED_INT, GateFlags::NONE_FLAG, 0, 0, 1) \ - V(UnsignedFloatToInt, UNSIGNED_FLOAT_TO_INT, GateFlags::NONE_FLAG, 0, 0, 1) \ - V(Bitcast, BITCAST, GateFlags::NONE_FLAG, 0, 0, 1) - -#define IMMUTABLE_META_DATA_CACHE_LIST(V) \ - V(CircuitRoot, CIRCUIT_ROOT, GateFlags::NONE_FLAG, 0, 0, 0) \ - V(StateEntry, STATE_ENTRY, GateFlags::ROOT, 0, 0, 0) \ - V(DependEntry, DEPEND_ENTRY, GateFlags::ROOT, 0, 0, 0) \ - V(ReturnList, RETURN_LIST, GateFlags::ROOT, 0, 0, 0) \ - V(ArgList, ARG_LIST, GateFlags::ROOT, 0, 0, 0) \ - V(Return, RETURN, GateFlags::HAS_ROOT, 1, 1, 1) \ - V(ReturnVoid, RETURN_VOID, GateFlags::HAS_ROOT, 1, 1, 0) \ - V(Throw, THROW, GateFlags::CONTROL, 1, 1, 1) \ - V(OrdinaryBlock, ORDINARY_BLOCK, GateFlags::CONTROL, 1, 0, 0) \ - V(IfBranch, IF_BRANCH, GateFlags::CONTROL, 1, 0, 1) \ - V(IfTrue, IF_TRUE, GateFlags::CONTROL, 1, 0, 0) \ - V(IfFalse, IF_FALSE, GateFlags::CONTROL, 1, 0, 0) \ - V(LoopBegin, LOOP_BEGIN, GateFlags::CONTROL, 2, 0, 0) \ - V(LoopBack, LOOP_BACK, GateFlags::CONTROL, 1, 0, 0) \ - V(LoopExit, LOOP_EXIT, GateFlags::CONTROL, 1, 0, 0) \ - V(LoopExitDepend, LOOP_EXIT_DEPEND, GateFlags::FIXED, 1, 1, 0) \ - V(LoopExitValue, LOOP_EXIT_VALUE, GateFlags::FIXED, 1, 0, 1) \ - V(DependRelay, DEPEND_RELAY, GateFlags::FIXED, 1, 1, 0) \ - V(IfSuccess, IF_SUCCESS, GateFlags::CONTROL, 1, 0, 0) \ - V(IfException, IF_EXCEPTION, GateFlags::CONTROL, 1, 1, 0) \ - V(GetException, GET_EXCEPTION, GateFlags::NONE_FLAG, 1, 1, 0) \ - V(GetConstPool, GET_CONSTPOOL, GateFlags::NO_WRITE, 0, 1, 1) \ - V(GetGlobalEnv, GET_GLOBAL_ENV, GateFlags::NO_WRITE, 0, 1, 0) \ - V(StateSplit, STATE_SPLIT, GateFlags::CHECKABLE, 1, 1, 0) \ - V(Load, LOAD, GateFlags::NO_WRITE, 0, 1, 1) \ - V(Store, STORE, GateFlags::NONE_FLAG, 0, 1, 2) \ - V(TypedCallCheck, TYPED_CALL_CHECK, GateFlags::CHECKABLE, 1, 1, 3) \ - V(HeapObjectCheck, HEAP_OBJECT_CHECK, GateFlags::CHECKABLE, 1, 1, 1) \ - V(StableArrayCheck, STABLE_ARRAY_CHECK, GateFlags::CHECKABLE, 1, 1, 1) \ - V(ArrayGuardianCheck, ARRAY_GUARDIAN_CHECK, GateFlags::CHECKABLE, 1, 1, 0) \ - V(HClassStableArrayCheck, HCLASS_STABLE_ARRAY_CHECK, GateFlags::CHECKABLE, 1, 1, 1) \ - V(DeoptCheck, DEOPT_CHECK, GateFlags::NO_WRITE, 1, 1, 3) \ - V(StoreProperty, STORE_PROPERTY, GateFlags::NONE_FLAG, 1, 1, 3) \ - V(StorePropertyNoBarrier, STORE_PROPERTY_NO_BARRIER, GateFlags::NONE_FLAG, 1, 1, 3) \ - V(ToLength, TO_LENGTH, GateFlags::NONE_FLAG, 1, 1, 1) \ - V(DefaultCase, DEFAULT_CASE, GateFlags::CONTROL, 1, 0, 0) \ - V(LoadArrayLength, LOAD_ARRAY_LENGTH, GateFlags::NO_WRITE, 1, 1, 1) \ - V(TypedNewAllocateThis, TYPED_NEW_ALLOCATE_THIS, GateFlags::CHECKABLE, 1, 1, 2) \ - V(TypedSuperAllocateThis, TYPED_SUPER_ALLOCATE_THIS, GateFlags::CHECKABLE, 1, 1, 2) \ - V(GetSuperConstructor, GET_SUPER_CONSTRUCTOR, GateFlags::NO_WRITE, 1, 1, 1) \ - V(UpdateHotness, UPDATE_HOTNESS, GateFlags::NO_WRITE, 1, 1, 0) \ - V(Dead, DEAD, GateFlags::NONE_FLAG, 0, 0, 0) \ - V(FrameArgs, FRAME_ARGS, GateFlags::NONE_FLAG, 0, 0, 4) \ - V(GetEnv, GET_ENV, GateFlags::NONE_FLAG, 0, 0, 1) \ - V(ConvertHoleAsUndefined, CONVERT_HOLE_AS_UNDEFINED, GateFlags::NO_WRITE, 1, 1, 1) \ - V(StartAllocate, START_ALLOCATE, GateFlags::NONE_FLAG, 0, 1, 0) \ - V(FinishAllocate, FINISH_ALLOCATE, GateFlags::NONE_FLAG, 0, 1, 0) \ - BINARY_GATE_META_DATA_CACHE_LIST(V) \ - UNARY_GATE_META_DATA_CACHE_LIST(V) - -#define GATE_META_DATA_LIST_WITH_VALUE_IN(V) \ - V(ValueSelector, VALUE_SELECTOR, GateFlags::FIXED, 1, 0, value) \ - V(FrameValues, FRAME_VALUES, GateFlags::NONE_FLAG, 0, 0, value) \ - V(RuntimeCall, RUNTIME_CALL, GateFlags::NONE_FLAG, 0, 1, value) \ - V(RuntimeCallWithArgv, RUNTIME_CALL_WITH_ARGV, GateFlags::NONE_FLAG, 0, 1, value) \ - V(NoGcRuntimeCall, NOGC_RUNTIME_CALL, GateFlags::NONE_FLAG, 0, 1, value) \ - V(CallOptimized, CALL_OPTIMIZED, GateFlags::NONE_FLAG, 0, 1, value) \ - V(FastCallOptimized, FAST_CALL_OPTIMIZED, GateFlags::NONE_FLAG, 0, 1, value) \ - V(Call, CALL, GateFlags::NONE_FLAG, 0, 1, value) \ - V(BytecodeCall, BYTECODE_CALL, GateFlags::NONE_FLAG, 0, 1, value) \ - V(DebuggerBytecodeCall, DEBUGGER_BYTECODE_CALL, GateFlags::NONE_FLAG, 0, 1, value) \ - V(BuiltinsCallWithArgv, BUILTINS_CALL_WITH_ARGV, GateFlags::NONE_FLAG, 0, 1, value) \ - V(BuiltinsCall, BUILTINS_CALL, GateFlags::NONE_FLAG, 0, 1, value) \ - V(SaveRegister, SAVE_REGISTER, GateFlags::NONE_FLAG, 0, 1, value) - -#define GATE_META_DATA_LIST_WITH_PC_OFFSET(V) \ - V(TypedCallBuiltin, TYPED_CALL_BUILTIN, GateFlags::NO_WRITE, 1, 1, value) \ - V(Construct, CONSTRUCT, GateFlags::NONE_FLAG, 1, 1, value) \ - V(TypedCall, TYPEDCALL, GateFlags::NONE_FLAG, 1, 1, value) \ - V(TypedFastCall, TYPEDFASTCALL, GateFlags::NONE_FLAG, 1, 1, value) - -#define GATE_META_DATA_LIST_WITH_PC_OFFSET_FIXED_VALUE(V) \ - V(CallGetter, CALL_GETTER, GateFlags::NONE_FLAG, 1, 1, 2) \ - V(CallSetter, CALL_SETTER, GateFlags::NONE_FLAG, 1, 1, 3) - -#define GATE_META_DATA_LIST_WITH_SIZE(V) \ - V(Merge, MERGE, GateFlags::CONTROL, value, 0, 0) \ - V(DependSelector, DEPEND_SELECTOR, GateFlags::FIXED, 1, value, 0) \ - GATE_META_DATA_LIST_WITH_VALUE_IN(V) - -#define GATE_META_DATA_LIST_WITH_GATE_TYPE(V) \ - V(PrimitiveTypeCheck, PRIMITIVE_TYPE_CHECK, GateFlags::CHECKABLE, 1, 1, 1) \ - V(ObjectTypeCheck, OBJECT_TYPE_CHECK, GateFlags::CHECKABLE, 1, 1, 2) \ - V(JSCallTargetFromDefineFuncCheck, JSCALLTARGET_FROM_DEFINEFUNC_CHECK, GateFlags::CHECKABLE, 1, 1, 1) \ - V(JSCallTargetTypeCheck, JSCALLTARGET_TYPE_CHECK, GateFlags::CHECKABLE, 1, 1, 2) \ - V(JSFastCallTargetTypeCheck, JSFASTCALLTARGET_TYPE_CHECK, GateFlags::CHECKABLE, 1, 1, 2) \ - V(JSCallThisTargetTypeCheck, JSCALLTHISTARGET_TYPE_CHECK, GateFlags::CHECKABLE, 1, 1, 1) \ - V(JSFastCallThisTargetTypeCheck, JSFASTCALLTHISTARGET_TYPE_CHECK, GateFlags::CHECKABLE, 1, 1, 1) \ - V(TypedArrayCheck, TYPED_ARRAY_CHECK, GateFlags::CHECKABLE, 1, 1, 1) \ - V(IndexCheck, INDEX_CHECK, GateFlags::CHECKABLE, 1, 1, 2) \ - V(TypedUnaryOp, TYPED_UNARY_OP, GateFlags::NO_WRITE, 1, 1, 1) \ - V(TypedConditionJump, TYPED_CONDITION_JUMP, GateFlags::NO_WRITE, 1, 1, 1) \ - V(TypedConvert, TYPE_CONVERT, GateFlags::NO_WRITE, 1, 1, 1) \ - V(CheckAndConvert, CHECK_AND_CONVERT, GateFlags::CHECKABLE, 0, 0, 1) \ - V(Convert, CONVERT, GateFlags::NONE_FLAG, 0, 0, 1) \ - V(JSInlineTargetTypeCheck, JSINLINETARGET_TYPE_CHECK, GateFlags::CHECKABLE, 1, 1, 2) - -#define GATE_META_DATA_LIST_WITH_VALUE(V) \ - V(Icmp, ICMP, GateFlags::NONE_FLAG, 0, 0, 2) \ - V(Fcmp, FCMP, GateFlags::NONE_FLAG, 0, 0, 2) \ - V(Alloca, ALLOCA, GateFlags::NONE_FLAG, 0, 0, 0) \ - V(SwitchBranch, SWITCH_BRANCH, GateFlags::CONTROL, 1, 0, 1) \ - V(SwitchCase, SWITCH_CASE, GateFlags::CONTROL, 1, 0, 0) \ - V(HeapAlloc, HEAP_ALLOC, GateFlags::NONE_FLAG, 1, 1, 1) \ - V(LoadConstOffset, LOAD_CONST_OFFSET, GateFlags::NO_WRITE, 0, 1, 1) \ - V(StoreConstOffset, STORE_CONST_OFFSET, GateFlags::NONE_FLAG, 1, 1, 2) \ - V(LoadElement, LOAD_ELEMENT, GateFlags::NO_WRITE, 1, 1, 2) \ - V(StoreElement, STORE_ELEMENT, GateFlags::NONE_FLAG, 1, 1, 3) \ - V(RestoreRegister, RESTORE_REGISTER, GateFlags::NONE_FLAG, 0, 0, 1) \ - V(Constant, CONSTANT, GateFlags::NONE_FLAG, 0, 0, 0) \ - V(RelocatableData, RELOCATABLE_DATA, GateFlags::NONE_FLAG, 0, 0, 0) \ - V(GetGlobalEnvObjHClass, GET_GLOBAL_ENV_OBJ_HCLASS, GateFlags::NO_WRITE, 0, 1, 1) \ - V(GetGlobalConstantValue, GET_GLOBAL_CONSTANT_VALUE, GateFlags::NO_WRITE, 0, 1, 0) \ - V(FrameState, FRAME_STATE, GateFlags::HAS_FRAME_STATE, 0, 0, 2) - -#define GATE_META_DATA_LIST_WITH_ONE_PARAMETER(V) \ - V(Arg, ARG, GateFlags::HAS_ROOT, 0, 0, 0) \ - GATE_META_DATA_LIST_WITH_VALUE(V) \ - GATE_META_DATA_LIST_WITH_GATE_TYPE(V) - -#define GATE_META_DATA_LIST_WITH_BOOL(V) \ - V(LoadProperty, LOAD_PROPERTY, GateFlags::NO_WRITE, 1, 1, 2) \ - V(CreateArray, CREATE_ARRAY, GateFlags::NONE_FLAG, 1, 1, 1) - -#define GATE_OPCODE_LIST(V) \ - V(JS_BYTECODE) \ - V(TYPED_BINARY_OP) \ - V(CONSTSTRING) - -enum class OpCode : uint8_t { - NOP = 0, -#define DECLARE_GATE_OPCODE(NAME, OP, R, S, D, V) OP, - IMMUTABLE_META_DATA_CACHE_LIST(DECLARE_GATE_OPCODE) - GATE_META_DATA_LIST_WITH_SIZE(DECLARE_GATE_OPCODE) - GATE_META_DATA_LIST_WITH_ONE_PARAMETER(DECLARE_GATE_OPCODE) - GATE_META_DATA_LIST_WITH_PC_OFFSET(DECLARE_GATE_OPCODE) - GATE_META_DATA_LIST_WITH_PC_OFFSET_FIXED_VALUE(DECLARE_GATE_OPCODE) - GATE_META_DATA_LIST_WITH_BOOL(DECLARE_GATE_OPCODE) -#undef DECLARE_GATE_OPCODE -#define DECLARE_GATE_OPCODE(NAME) NAME, - GATE_OPCODE_LIST(DECLARE_GATE_OPCODE) -#undef DECLARE_GATE_OPCODE -}; - enum GateFlags : uint8_t { NONE_FLAG = 0, NO_WRITE = 1 << 0, @@ -369,6 +98,8 @@ public: MUTABLE_STRING, JSBYTECODE, TYPED_BINARY_OP, + TYPED_CALLTARGETCHECK_OP, + TYPED_CALL, }; GateMetaData() = default; GateMetaData(OpCode opcode, GateFlags flags, @@ -376,48 +107,6 @@ public: : opcode_(opcode), flags_(flags), statesIn_(statesIn), dependsIn_(dependsIn), valuesIn_(valuesIn) {} - static TypedBinOp GetRevCompareOp(TypedBinOp op) - { - switch (op) { - case TypedBinOp::TYPED_LESS: - return TypedBinOp::TYPED_GREATEREQ; - case TypedBinOp::TYPED_LESSEQ: - return TypedBinOp::TYPED_GREATER; - case TypedBinOp::TYPED_GREATER: - return TypedBinOp::TYPED_LESSEQ; - case TypedBinOp::TYPED_GREATEREQ: - return TypedBinOp::TYPED_LESS; - case TypedBinOp::TYPED_EQ: - return TypedBinOp::TYPED_NOTEQ; - case TypedBinOp::TYPED_NOTEQ: - return TypedBinOp::TYPED_EQ; - default: - UNREACHABLE(); - return op; - } - } - - static TypedBinOp GetSwapCompareOp(TypedBinOp op) - { - switch (op) { - case TypedBinOp::TYPED_LESS: - return TypedBinOp::TYPED_GREATER; - case TypedBinOp::TYPED_LESSEQ: - return TypedBinOp::TYPED_GREATEREQ; - case TypedBinOp::TYPED_GREATER: - return TypedBinOp::TYPED_LESS; - case TypedBinOp::TYPED_GREATEREQ: - return TypedBinOp::TYPED_LESSEQ; - case TypedBinOp::TYPED_EQ: - return TypedBinOp::TYPED_EQ; - case TypedBinOp::TYPED_NOTEQ: - return TypedBinOp::TYPED_NOTEQ; - default: - UNREACHABLE(); - return op; - } - } - size_t GetStateCount() const { return statesIn_; @@ -477,7 +166,7 @@ public: bool IsOneParameterKind() const { return GetKind() == Kind::IMMUTABLE_ONE_PARAMETER || GetKind() == Kind::MUTABLE_ONE_PARAMETER || - GetKind() == Kind::TYPED_BINARY_OP; + GetKind() == Kind::TYPED_BINARY_OP || GetKind() == Kind::TYPED_CALLTARGETCHECK_OP; } bool IsStringType() const @@ -495,8 +184,10 @@ public: bool IsVirtualState() const; bool IsCFGMerge() const; bool IsControlCase() const; + bool IsIfOrSwitchRelated() const; bool IsLoopHead() const; bool IsNop() const; + bool IsDead() const; bool IsConstant() const; bool IsDependSelector() const; bool IsTypedOperator() const; @@ -514,8 +205,14 @@ public: ~GateMetaData() = default; - std::string TypedBinOpToStr(TypedBinOp typedBinOp) const; static std::string Str(OpCode opcode); + static std::string Str(TypedBinOp op); + static std::string Str(TypedUnOp op); + static std::string Str(TypedJumpOp op); + static std::string Str(TypedLoadOp op); + static std::string Str(TypedStoreOp op); + static std::string Str(TypedCallTargetCheckOp op); + static std::string Str(ValueType type); std::string Str() const { return Str(opcode_); @@ -591,7 +288,7 @@ public: return static_cast(meta); } - bool getBool() const + bool GetBool() const { return value_; } @@ -600,46 +297,6 @@ private: bool value_ { false }; }; -class JSBytecodeMetaData : public GateMetaData { -public: - explicit JSBytecodeMetaData(size_t valuesIn, EcmaOpcode opcode, uint32_t pcOffset, GateFlags flags) - : GateMetaData(OpCode::JS_BYTECODE, flags, 1, 1, valuesIn), - opcode_(opcode), pcOffset_(pcOffset) - { - SetKind(GateMetaData::Kind::JSBYTECODE); - } - - static const JSBytecodeMetaData* Cast(const GateMetaData* meta) - { - meta->AssertKind(GateMetaData::Kind::JSBYTECODE); - return static_cast(meta); - } - - uint32_t GetPcOffset() const - { - return pcOffset_; - } - - void SetType(PGOSampleType type) - { - type_ = type; - } - - PGOSampleType GetType() const - { - return type_; - } - - EcmaOpcode GetByteCodeOpcode() const - { - return opcode_; - } -private: - EcmaOpcode opcode_; - uint32_t pcOffset_; - PGOSampleType type_; -}; - class OneParameterMetaData : public GateMetaData { public: OneParameterMetaData(OpCode opcode, GateFlags flags, uint32_t statesIn, @@ -660,49 +317,25 @@ public: return value_; } -private: - uint64_t value_ { 0 }; -}; - -class TypedBinaryMegaData : public OneParameterMetaData { -public: - TypedBinaryMegaData(uint64_t value, TypedBinOp binOp, PGOSampleType type) - : OneParameterMetaData(OpCode::TYPED_BINARY_OP, GateFlags::NO_WRITE, 1, 1, 2, value), // 2: valuesIn - binOp_(binOp), type_(type) - { - SetKind(GateMetaData::Kind::TYPED_BINARY_OP); - } - - static const TypedBinaryMegaData* Cast(const GateMetaData* meta) + void SetValue(uint64_t value) { - meta->AssertKind(GateMetaData::Kind::TYPED_BINARY_OP); - return static_cast(meta); + value_ = value; } - TypedBinOp GetTypedBinaryOp() const - { - return binOp_; - } - - PGOSampleType GetType() const - { - return type_; - } private: - TypedBinOp binOp_; - PGOSampleType type_; + uint64_t value_ { 0 }; }; class StringMetaData : public GateMetaData { public: - StringMetaData(Chunk* chunk, const std::string &str) + StringMetaData(Chunk* chunk, std::string_view str) : GateMetaData(OpCode::CONSTSTRING, GateFlags::NONE_FLAG, 0, 0, 0), stringData_(str.size() + 1, chunk) { auto srcLength = str.size(); auto destlength = stringData_.size(); auto dest = stringData_.data(); - auto src = str.c_str(); + auto src = str.data(); if (destlength <= static_cast(srcLength) || strcpy_s(dest, destlength, src) != EOK) { LOG_COMPILER(FATAL) << "StringMetaData strcpy_s failed"; } @@ -752,21 +385,26 @@ public: return static_cast(RightBits::Get(bitField_)); } - static uint16_t ToValue(ValueType srcType, ValueType dstType) + bool IsConvertSupport() const + { + return ConvertSupportBits::Get(bitField_) == ConvertSupport::ENABLE; + } + + static uint64_t ToValue(ValueType srcType, ValueType dstType, ConvertSupport support = ConvertSupport::ENABLE) { uint8_t srcVlaue = static_cast(srcType); uint8_t dstVlaue = static_cast(dstType); - return LeftBits::Encode(srcVlaue) | RightBits::Encode(dstVlaue); + return LeftBits::Encode(srcVlaue) | RightBits::Encode(dstVlaue) | ConvertSupportBits::Encode(support); } private: using LeftBits = panda::BitField; using RightBits = LeftBits::NextField; + using ConvertSupportBits = RightBits::NextField; uint64_t bitField_; }; - class GatePairTypeAccessor { public: // type bits shift @@ -795,105 +433,112 @@ private: uint64_t bitField_; }; -class TypedUnaryAccessor { +class UInt32PairAccessor { public: // type bits shift static constexpr int OPRAND_TYPE_BITS = 32; - explicit TypedUnaryAccessor(uint64_t value) : bitField_(value) {} + explicit UInt32PairAccessor(uint64_t value) : bitField_(value) {} + explicit UInt32PairAccessor(uint32_t first, uint32_t second) + { + bitField_ = FirstBits::Encode(first) | SecondBits::Encode(second); + } - GateType GetTypeValue() const + uint32_t GetFirstValue() const { - return GateType(TypedValueBits::Get(bitField_)); + return FirstBits::Get(bitField_); } - TypedUnOp GetTypedUnOp() const + uint32_t GetSecondValue() const { - return TypedUnOpBits::Get(bitField_); + return SecondBits::Get(bitField_); } - static uint64_t ToValue(GateType typeValue, TypedUnOp unaryOp) + uint64_t ToValue() const { - return TypedValueBits::Encode(typeValue.Value()) - | TypedUnOpBits::Encode(unaryOp); + return bitField_; } private: - using TypedValueBits = panda::BitField; - using TypedUnOpBits = TypedValueBits::NextField; + using FirstBits = panda::BitField; + using SecondBits = FirstBits::NextField; uint64_t bitField_; }; -class TypedJumpAccessor { +class ArrayMetaDataAccessor { public: - // type bits shift - static constexpr int OPRAND_TYPE_BITS = 32; - explicit TypedJumpAccessor(uint64_t value) : bitField_(value) {} + enum Mode : uint8_t { + CREATE = 0, + LOAD_ELEMENT, + STORE_ELEMENT, + LOAD_LENGTH + }; - GateType GetTypeValue() const + static constexpr int BITS_SIZE = 8; + static constexpr int ARRAY_LENGTH_BITS_SIZE = 32; + explicit ArrayMetaDataAccessor(uint64_t value) : bitField_(value) {} + explicit ArrayMetaDataAccessor(ElementsKind kind, Mode mode, uint32_t length = 0) { - return GateType(TypedValueBits::Get(bitField_)); + bitField_ = ElementsKindBits::Encode(kind) | ModeBits::Encode(mode) | ArrayLengthBits::Encode(length); } - TypedJumpOp GetTypedJumpOp() const + ElementsKind GetElementsKind() const { - return TypedJumpOpBits::Get(bitField_); + return ElementsKindBits::Get(bitField_); } - static uint64_t ToValue(GateType typeValue, TypedJumpOp jumpOp) + void SetArrayLength(uint32_t length) { - return TypedValueBits::Encode(typeValue.Value()) - | TypedJumpOpBits::Encode(jumpOp); + bitField_ = ArrayLengthBits::Update(bitField_, length); } -private: - using TypedValueBits = panda::BitField; - using TypedJumpOpBits = TypedValueBits::NextField; - - uint64_t bitField_; -}; - -class FrameStateOutput { -public: - static constexpr uint32_t INVALID_INDEX = static_cast(-1); - explicit FrameStateOutput(uint32_t value) : index_(value) {} - - static FrameStateOutput Invalid() + uint32_t GetArrayLength() const { - return FrameStateOutput(INVALID_INDEX); + return ArrayLengthBits::Get(bitField_); } - bool IsInvalid() const + bool IsLoadElement() const { - return index_ == INVALID_INDEX; + return GetMode() == Mode::LOAD_ELEMENT; } - uint32_t GetValue() const + uint64_t ToValue() const { - return index_; + return bitField_; } + private: - uint32_t index_; + Mode GetMode() const + { + return ModeBits::Get(bitField_); + } + + using ElementsKindBits = panda::BitField; + using ModeBits = ElementsKindBits::NextField; + using ArrayLengthBits = ModeBits::NextField; + + uint64_t bitField_; }; -class UInt32PairAccessor { +class ObjectTypeAccessor { public: - // type bits shift - static constexpr int OPRAND_TYPE_BITS = 32; - explicit UInt32PairAccessor(uint64_t value) : bitField_(value) {} - explicit UInt32PairAccessor(uint32_t first, uint32_t second) + static constexpr int TYPE_BITS_SIZE = 32; + static constexpr int IS_HEAP_OBJECT_BIT_SIZE = 1; + + explicit ObjectTypeAccessor(uint64_t value) : bitField_(value) {} + explicit ObjectTypeAccessor(GateType type, bool isHeapObject = false) { - bitField_ = FirstBits::Encode(first) | SecondBits::Encode(second); + bitField_ = TypeBits::Encode(type.Value()) | IsHeapObjectBit::Encode(isHeapObject); } - uint32_t GetFirstValue() const + GateType GetType() const { - return FirstBits::Get(bitField_); + return GateType(TypeBits::Get(bitField_)); } - uint32_t GetSecondValue() const + bool IsHeapObject() const { - return SecondBits::Get(bitField_); + return IsHeapObjectBit::Get(bitField_); } uint64_t ToValue() const @@ -902,11 +547,11 @@ public: } private: - using FirstBits = panda::BitField; - using SecondBits = FirstBits::NextField; + using TypeBits = panda::BitField; + using IsHeapObjectBit = TypeBits::NextField; uint64_t bitField_; }; } // namespace panda::ecmascript::kungfu -#endif // ECMASCRIPT_COMPILER_GATE_META_DATA_H +#endif // ECMASCRIPT_COMPILER_SHARE_GATE_META_DATA_H diff --git a/ecmascript/compiler/share_opcodes.h b/ecmascript/compiler/share_opcodes.h new file mode 100644 index 0000000000000000000000000000000000000000..30b97ef47ae1f71bbdb603660684730080f53eb9 --- /dev/null +++ b/ecmascript/compiler/share_opcodes.h @@ -0,0 +1,155 @@ +/* + * 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_COMPILER_OPCODE_H +#define ECMASCRIPT_COMPILER_OPCODE_H + +#include + +#include "ecmascript/compiler/bytecodes.h" + +#include "ecmascript/compiler/lcr_opcodes.h" +#include "ecmascript/compiler/mcr_opcodes.h" +#include "ecmascript/compiler/hcr_opcodes.h" + +namespace panda::ecmascript::kungfu { + +#define SHARE_IMMUTABLE_META_DATA_CACHE_LIST(V) \ + V(CircuitRoot, CIRCUIT_ROOT, GateFlags::NONE_FLAG, 0, 0, 0) \ + V(StateEntry, STATE_ENTRY, GateFlags::ROOT, 0, 0, 0) \ + V(DependEntry, DEPEND_ENTRY, GateFlags::ROOT, 0, 0, 0) \ + V(OrdinaryBlock, ORDINARY_BLOCK, GateFlags::CONTROL, 1, 0, 0) \ + V(DefaultCase, DEFAULT_CASE, GateFlags::CONTROL, 1, 0, 0) \ + V(ReturnList, RETURN_LIST, GateFlags::ROOT, 0, 0, 0) \ + V(ArgList, ARG_LIST, GateFlags::ROOT, 0, 0, 0) \ + V(Dead, DEAD, GateFlags::NONE_FLAG, 0, 0, 0) \ + V(Throw, THROW, GateFlags::CONTROL, 1, 1, 1) \ + V(LoopExit, LOOP_EXIT, GateFlags::CONTROL, 1, 0, 0) \ + V(LoopExitDepend, LOOP_EXIT_DEPEND, GateFlags::FIXED, 1, 1, 0) \ + V(LoopExitValue, LOOP_EXIT_VALUE, GateFlags::FIXED, 1, 0, 1) \ + V(DependRelay, DEPEND_RELAY, GateFlags::FIXED, 1, 1, 0) \ + V(IfTrue, IF_TRUE, GateFlags::CONTROL, 1, 0, 0) \ + V(IfFalse, IF_FALSE, GateFlags::CONTROL, 1, 0, 0) \ + V(IfSuccess, IF_SUCCESS, GateFlags::CONTROL, 1, 0, 0) \ + V(IfException, IF_EXCEPTION, GateFlags::CONTROL, 1, 1, 0) \ + V(GetException, GET_EXCEPTION, GateFlags::NONE_FLAG, 1, 1, 0) \ + V(GetConstPool, GET_CONSTPOOL, GateFlags::NO_WRITE, 0, 1, 1) \ + V(GetGlobalEnv, GET_GLOBAL_ENV, GateFlags::NO_WRITE, 0, 1, 0) \ + V(GetSuperConstructor, GET_SUPER_CONSTRUCTOR, GateFlags::NO_WRITE, 1, 1, 1) \ + V(CheckSafePointAndStackOver, CHECK_SAFEPOINT_AND_STACKOVER, GateFlags::NO_WRITE, 1, 1, 0) \ + V(DeoptCheck, DEOPT_CHECK, GateFlags::NO_WRITE, 1, 1, 3) \ + V(LoopBegin, LOOP_BEGIN, GateFlags::CONTROL, 2, 0, 0) \ + V(LoopBack, LOOP_BACK, GateFlags::CONTROL, 1, 0, 0) \ + V(Return, RETURN, GateFlags::HAS_ROOT, 1, 1, 1) \ + V(ReturnVoid, RETURN_VOID, GateFlags::HAS_ROOT, 1, 1, 0) \ + V(StateSplit, STATE_SPLIT, GateFlags::CHECKABLE, 1, 1, 0) \ + V(GetEnv, GET_ENV, GateFlags::NONE_FLAG, 0, 0, 1) + +#define SHARE_GATE_META_DATA_LIST_WITH_VALUE_IN(V) \ + V(Call, CALL, GateFlags::NONE_FLAG, 0, 1, value) \ + V(FrameValues, FRAME_VALUES, GateFlags::NONE_FLAG, 0, 0, value) \ + V(ValueSelector, VALUE_SELECTOR, GateFlags::FIXED, 1, 0, value) + +#define SHARE_GATE_META_DATA_LIST_WITH_SIZE(V) \ + V(Merge, MERGE, GateFlags::CONTROL, value, 0, 0) \ + V(DependSelector, DEPEND_SELECTOR, GateFlags::FIXED, 1, value, 0) \ + SHARE_GATE_META_DATA_LIST_WITH_VALUE_IN(V) + +#define SHARE_GATE_META_DATA_LIST_WITH_VALUE(V) \ + V(Constant, CONSTANT, GateFlags::NONE_FLAG, 0, 0, 0) \ + V(FrameArgs, FRAME_ARGS, GateFlags::HAS_FRAME_STATE, 0, 0, 4) \ + V(FrameState, FRAME_STATE, GateFlags::HAS_FRAME_STATE, 0, 0, 2) \ + V(IfBranch, IF_BRANCH, GateFlags::CONTROL, 1, 0, 1) \ + V(RelocatableData, RELOCATABLE_DATA, GateFlags::NONE_FLAG, 0, 0, 0) \ + V(SwitchBranch, SWITCH_BRANCH, GateFlags::CONTROL, 1, 0, 1) \ + V(SwitchCase, SWITCH_CASE, GateFlags::CONTROL, 1, 0, 0) + +#define SHARE_GATE_OPCODE_LIST(V) \ + V(CONSTSTRING) + +#define SHARE_GATE_META_DATA_LIST_WITH_ONE_PARAMETER(V) \ + V(Arg, ARG, GateFlags::HAS_ROOT, 0, 0, 0) \ + SHARE_GATE_META_DATA_LIST_WITH_VALUE(V) + +#define IMMUTABLE_META_DATA_CACHE_LIST(V) \ + SHARE_IMMUTABLE_META_DATA_CACHE_LIST(V) \ + LCR_IMMUTABLE_META_DATA_CACHE_LIST(V) \ + MCR_IMMUTABLE_META_DATA_CACHE_LIST(V) \ + HCR_IMMUTABLE_META_DATA_CACHE_LIST(V) + +#define GATE_META_DATA_LIST_WITH_VALUE_IN(V) \ + SHARE_GATE_META_DATA_LIST_WITH_VALUE_IN(V) \ + HCR_GATE_META_DATA_LIST_WITH_VALUE_IN(V) + +#define GATE_META_DATA_LIST_WITH_PC_OFFSET(V) \ + MCR_GATE_META_DATA_LIST_WITH_PC_OFFSET(V) \ + HCR_GATE_META_DATA_LIST_WITH_PC_OFFSET(V) + +#define GATE_META_DATA_LIST_FOR_CALL(V) \ + MCR_GATE_META_DATA_LIST_FOR_CALL(V) + +#define GATE_META_DATA_LIST_WITH_PC_OFFSET_FIXED_VALUE(V) \ + HCR_GATE_META_DATA_LIST_WITH_PC_OFFSET_FIXED_VALUE(V) + +#define GATE_META_DATA_LIST_WITH_SIZE(V) \ + SHARE_GATE_META_DATA_LIST_WITH_SIZE(V) \ + HCR_GATE_META_DATA_LIST_WITH_SIZE(V) + +#define GATE_META_DATA_LIST_WITH_GATE_TYPE(V) \ + MCR_GATE_META_DATA_LIST_WITH_GATE_TYPE(V) + +#define GATE_META_DATA_LIST_WITH_VALUE(V) \ + SHARE_GATE_META_DATA_LIST_WITH_VALUE(V) \ + LCR_GATE_META_DATA_LIST_WITH_VALUE(V) \ + MCR_GATE_META_DATA_LIST_WITH_VALUE(V) \ + HCR_GATE_META_DATA_LIST_WITH_VALUE(V) + +#define GATE_META_DATA_LIST_WITH_ONE_PARAMETER(V) \ + SHARE_GATE_META_DATA_LIST_WITH_ONE_PARAMETER(V) \ + LCR_GATE_META_DATA_LIST_WITH_ONE_PARAMETER(V) \ + MCR_GATE_META_DATA_LIST_WITH_ONE_PARAMETER(V) \ + HCR_GATE_META_DATA_LIST_WITH_ONE_PARAMETER(V) + +#define GATE_META_DATA_LIST_WITH_BOOL(V) \ + MCR_GATE_META_DATA_LIST_WITH_BOOL(V) + +#define GATE_META_DATA_LIST_WITH_BOOL_VALUE_IN(V) \ + LCR_GATE_META_DATA_LIST_WITH_BOOL_VALUE_IN(V) + +#define GATE_OPCODE_LIST(V) \ + SHARE_GATE_OPCODE_LIST(V) \ + MCR_GATE_OPCODE_LIST(V) \ + HCR_GATE_OPCODE_LIST(V) + +enum class OpCode : uint8_t { + NOP = 0, +#define DECLARE_GATE_OPCODE(NAME, OP, R, S, D, V) OP, + IMMUTABLE_META_DATA_CACHE_LIST(DECLARE_GATE_OPCODE) + GATE_META_DATA_LIST_WITH_SIZE(DECLARE_GATE_OPCODE) + GATE_META_DATA_LIST_WITH_ONE_PARAMETER(DECLARE_GATE_OPCODE) + GATE_META_DATA_LIST_WITH_PC_OFFSET(DECLARE_GATE_OPCODE) + GATE_META_DATA_LIST_FOR_CALL(DECLARE_GATE_OPCODE) + GATE_META_DATA_LIST_WITH_PC_OFFSET_FIXED_VALUE(DECLARE_GATE_OPCODE) + GATE_META_DATA_LIST_WITH_BOOL(DECLARE_GATE_OPCODE) + GATE_META_DATA_LIST_WITH_BOOL_VALUE_IN(DECLARE_GATE_OPCODE) +#undef DECLARE_GATE_OPCODE +#define DECLARE_GATE_OPCODE(NAME) NAME, + GATE_OPCODE_LIST(DECLARE_GATE_OPCODE) +#undef DECLARE_GATE_OPCODE +}; + +} + +#endif // ECMASCRIPT_COMPILER_SHARE_GATE_META_DATA_H \ No newline at end of file diff --git a/ecmascript/compiler/slowpath_lowering.cpp b/ecmascript/compiler/slowpath_lowering.cpp index b0d34c814b8a362c6856db1a3dd23690724c079b..4199aa349c39df4f27401131e87092af9333aadd 100644 --- a/ecmascript/compiler/slowpath_lowering.cpp +++ b/ecmascript/compiler/slowpath_lowering.cpp @@ -14,6 +14,8 @@ */ #include "ecmascript/compiler/slowpath_lowering.h" +#include "ecmascript/dfx/vm_thread_control.h" +#include "ecmascript/compiler/share_gate_meta_data.h" #include "ecmascript/dfx/vmstat/opt_code_profiler.h" #include "ecmascript/js_thread.h" #include "ecmascript/message_string.h" @@ -63,8 +65,8 @@ void SlowPathLowering::CallRuntimeLowering() case OpCode::TYPEDFASTCALL: LowerTypedFastCall(gate); break; - case OpCode::UPDATE_HOTNESS: - LowerUpdateHotness(gate); + case OpCode::CHECK_SAFEPOINT_AND_STACKOVER: + LowerCheckSafePointAndStackOver(gate); break; case OpCode::GET_ENV: LowerGetEnv(gate); @@ -1450,7 +1452,8 @@ void SlowPathLowering::LowerFastStrictEqual(GateRef gate) void SlowPathLowering::LowerCreateEmptyArray(GateRef gate) { GateRef result = builder_.CallStub(glue_, gate, CommonStubCSigns::CreateEmptyArray, { glue_ }); - ReplaceHirWithValue(gate, result, true); + GateRef newRes = LowerUpdateArrayHClass(gate, result); + ReplaceHirWithValue(gate, newRes, true); } void SlowPathLowering::LowerCreateEmptyObject(GateRef gate) @@ -1464,7 +1467,23 @@ void SlowPathLowering::LowerCreateArrayWithBuffer(GateRef gate) GateRef jsFunc = argAcc_.GetFrameArgsIn(gate, FrameArgIdx::FUNC); GateRef index = builder_.TruncInt64ToInt32(acc_.GetValueIn(gate, 0)); GateRef result = builder_.CallStub(glue_, gate, CommonStubCSigns::CreateArrayWithBuffer, { glue_, index, jsFunc }); - ReplaceHirWithValue(gate, result, true); + GateRef newRes = LowerUpdateArrayHClass(gate, result); + ReplaceHirWithValue(gate, newRes, true); +} + +GateRef SlowPathLowering::LowerUpdateArrayHClass(GateRef gate, GateRef array) +{ + ElementsKind kind = acc_.TryGetElementsKind(gate); + if (!Elements::IsGeneric(kind)) { + auto thread = tsManager_->GetEcmaVM()->GetJSThread(); + size_t hclassIndex = static_cast(thread->GetArrayHClassIndexMap().at(kind)); + GateRef gConstAddr = builder_.Load(VariableType::JS_POINTER(), glue_, + builder_.IntPtr(JSThread::GlueData::GetGlobalConstOffset(false))); + GateRef constantIndex = builder_.IntPtr(JSTaggedValue::TaggedTypeSize() * hclassIndex); + GateRef hclass = builder_.Load(VariableType::JS_POINTER(), gConstAddr, constantIndex); + builder_.Store(VariableType::JS_POINTER(), glue_, array, builder_.IntPtr(0), hclass); + } + return array; } void SlowPathLowering::LowerCreateObjectWithBuffer(GateRef gate) @@ -2411,8 +2430,6 @@ void SlowPathLowering::LowerStLexVar(GateRef gate) void SlowPathLowering::LowerDefineClassWithBuffer(GateRef gate) { - GateType type = acc_.GetGateType(gate); - // 5: number of value inputs ASSERT(acc_.GetNumValueIn(gate) == 5); GateRef jsFunc = argAcc_.GetFrameArgsIn(gate, FrameArgIdx::FUNC); @@ -2427,36 +2444,16 @@ void SlowPathLowering::LowerDefineClassWithBuffer(GateRef gate) Label isNotException(&builder_); GateRef result; - if (type.IsAnyType()) { - auto args = { proto, lexicalEnv, constpool, - builder_.ToTaggedInt(methodId), builder_.ToTaggedInt(literalId), module }; - result = LowerCallRuntime(gate, RTSTUB_ID(CreateClassWithBuffer), args, true); - builder_.Branch(builder_.IsSpecial(result, JSTaggedValue::VALUE_EXCEPTION), &isException, &isNotException); - } else { - int index = tsManager_->GetHClassIndexByClassGateType(type); - ASSERT(index != -1); - GateRef ihcIndex = builder_.Int32(index); - GateRef ihclass = builder_.GetObjectFromConstPool(glue_, gate, jsFunc, ihcIndex, ConstPoolType::CLASS_LITERAL); - - int constructorIndex = tsManager_->GetConstructorHClassIndexByClassGateType(type); - ASSERT(index != -1); - GateRef constructorHcIndex = builder_.Int32(constructorIndex); - GateRef constructorHclass = builder_.GetObjectFromConstPool(glue_, gate, jsFunc, - constructorHcIndex, ConstPoolType::CLASS_LITERAL); - - auto args = { proto, lexicalEnv, constpool, - builder_.ToTaggedInt(methodId), - builder_.ToTaggedInt(literalId), ihclass, constructorHclass, module }; - result = LowerCallRuntime(gate, RTSTUB_ID(CreateClassWithIHClass), args, true); - builder_.Branch(builder_.IsSpecial(result, JSTaggedValue::VALUE_EXCEPTION), &isException, &isNotException); - } + auto args = { proto, lexicalEnv, constpool, + builder_.ToTaggedInt(methodId), builder_.ToTaggedInt(literalId), module }; + result = LowerCallRuntime(gate, RTSTUB_ID(CreateClassWithBuffer), args, true); + builder_.Branch(builder_.IsSpecial(result, JSTaggedValue::VALUE_EXCEPTION), &isException, &isNotException); StateDepend successControl; StateDepend failControl; builder_.Bind(&isNotException); { builder_.SetLexicalEnvToFunction(glue_, result, lexicalEnv); - builder_.SetModuleToFunction(glue_, result, module); LowerCallRuntime(gate, RTSTUB_ID(SetClassConstructorLength), { result, builder_.ToTaggedInt(length) }, true); successControl.SetState(builder_.GetState()); @@ -2489,7 +2486,6 @@ void SlowPathLowering::LowerDefineFunc(GateRef gate) builder_.Int32(JSFunction::LENGTH_INLINE_PROPERTY_INDEX), VariableType::INT64()); GateRef env = acc_.GetValueIn(gate, 2); // 2: Get current env builder_.SetLexicalEnvToFunction(glue_, result, env); - builder_.SetModuleToFunction(glue_, result, builder_.GetModuleFromFunction(jsFunc)); builder_.SetHomeObjectToFunction(glue_, result, builder_.GetHomeObjectFromFunction(jsFunc)); builder_.Jump(&successExit); } @@ -2514,7 +2510,7 @@ void SlowPathLowering::LowerTypeof(GateRef gate) Label entry(&builder_); Label exit(&builder_); - GateRef gConstAddr = builder_.PtrAdd(glue_, + GateRef gConstAddr = builder_.Load(VariableType::JS_POINTER(), glue_, builder_.IntPtr(JSThread::GlueData::GetGlobalConstOffset(builder_.GetCompilationConfig()->Is32Bit()))); GateRef undefinedIndex = builder_.GetGlobalConstantString(ConstantIndex::UNDEFINED_STRING_INDEX); GateRef gConstUndefinedStr = builder_.Load(VariableType::JS_POINTER(), gConstAddr, undefinedIndex); @@ -2767,7 +2763,6 @@ void SlowPathLowering::LowerDefineMethod(GateRef gate) builder_.Int32(JSFunction::LENGTH_INLINE_PROPERTY_INDEX), VariableType::INT64()); GateRef env = acc_.GetValueIn(gate, 2); // 2: Get current env builder_.SetLexicalEnvToFunction(glue_, result, env); - builder_.SetModuleToFunction(glue_, result, builder_.GetModuleFromFunction(jsFunc)); builder_.Jump(&successExit); } CREATE_DOUBLE_EXIT(successExit, exceptionExit) @@ -3160,19 +3155,34 @@ void SlowPathLowering::LowerTypedFastCall(GateRef gate) ReplaceHirWithPendingException(gate, state, result, result); } -void SlowPathLowering::LowerUpdateHotness(GateRef gate) +void SlowPathLowering::LowerCheckSafePointAndStackOver(GateRef gate) { Environment env(gate, circuit_, &builder_); - GateRef interruptsFlag = builder_.Load(VariableType::INT8(), glue_, - builder_.IntPtr(JSThread::GlueData::GetInterruptVectorOffset(builder_.GetCompilationConfig()->Is32Bit()))); Label slowPath(&builder_); Label dispatch(&builder_); - builder_.Branch(builder_.Int8Equal(interruptsFlag, - builder_.Int8(VmThreadControl::VM_NEED_SUSPENSION)), &slowPath, &dispatch); + Label checkStackOver(&builder_); + Label stackOverflow(&builder_); + GateRef stackLimit = builder_.Load(VariableType::INT64(), glue_, + builder_.IntPtr(JSThread::GlueData::GetStackLimitOffset(builder_.GetCompilationConfig()->Is32Bit()))); + GateRef interruptsFlag = builder_.Load(VariableType::INT8(), glue_, + builder_.IntPtr(JSThread::GlueData::GetInterruptVectorOffset(builder_.GetCompilationConfig()->Is32Bit()))); + GateRef spValue = builder_.ReadSp(); + builder_.Branch(builder_.Int8Equal(interruptsFlag, builder_.Int8(VmThreadControl::VM_NEED_SUSPENSION)), + &slowPath, &checkStackOver, BranchWeight::ONE_WEIGHT, BranchWeight::DEOPT_WEIGHT); builder_.Bind(&slowPath); { - LowerCallRuntime(glue_, RTSTUB_ID(CheckSafePoint), { }, true); - builder_.Jump(&dispatch); + LowerCallRuntime(glue_, RTSTUB_ID(CheckSafePoint), {}, true); + builder_.Jump(&checkStackOver); + } + builder_.Bind(&checkStackOver); + { + builder_.Branch(builder_.Int64LessThanOrEqual(spValue, stackLimit), &stackOverflow, &dispatch, + BranchWeight::ONE_WEIGHT, BranchWeight::DEOPT_WEIGHT); + builder_.Bind(&stackOverflow); + { + GateRef res = LowerCallRuntime(glue_, RTSTUB_ID(ThrowStackOverflowException), {}, true); + builder_.Return(res); + } } builder_.Bind(&dispatch); acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), Circuit::NullGate()); diff --git a/ecmascript/compiler/slowpath_lowering.h b/ecmascript/compiler/slowpath_lowering.h index 0f802b463c9b29958a549cce7bc762523e7c784b..9db30322546f00811d2384ded3b4e57458207011 100644 --- a/ecmascript/compiler/slowpath_lowering.h +++ b/ecmascript/compiler/slowpath_lowering.h @@ -291,6 +291,7 @@ private: void LowerAsyncGeneratorReject(GateRef gate); void LowerSetGeneratorState(GateRef gate); GateRef GetValueFromTaggedArray(GateRef arrayGate, GateRef indexOffset); + GateRef LowerUpdateArrayHClass(GateRef gate, GateRef array); void AddProfiling(GateRef gate, bool skipGenerator = true); GateRef FastStrictEqual(GateRef left, GateRef right); void LowerWideLdPatchVar(GateRef gate); @@ -299,7 +300,7 @@ private: void LowerConstruct(GateRef gate); void LowerTypedCall(GateRef gate); void LowerTypedFastCall(GateRef gate); - void LowerUpdateHotness(GateRef gate); + void LowerCheckSafePointAndStackOver(GateRef gate); void LowerNotifyConcurrentResult(GateRef gate); void LowerGetEnv(GateRef gate); void DeleteLoopExit(GateRef gate); diff --git a/ecmascript/compiler/state_split_linearizer.cpp b/ecmascript/compiler/state_split_linearizer.cpp index 4ffd2dded494e6c501a5ab63ac53f518a637549d..f2a5c4c1cf929eafe08684238bf55de25aa36c94 100644 --- a/ecmascript/compiler/state_split_linearizer.cpp +++ b/ecmascript/compiler/state_split_linearizer.cpp @@ -21,17 +21,6 @@ void StateSplitLinearizer::Run() { graphLinearizer_.SetScheduleJSOpcode(); graphLinearizer_.LinearizeGraph(); - if (IsLogEnabled()) { - LOG_COMPILER(INFO) << ""; - LOG_COMPILER(INFO) << "\033[34m" - << "====================" - << " Before state split linearizer " - << "[" << GetMethodName() << "]" - << "====================" - << "\033[0m"; - graphLinearizer_.PrintGraph("Build Basic Block"); - LOG_COMPILER(INFO) << "\033[34m" << "========================= End ==========================" << "\033[0m"; - } LinearizeStateSplit(); if (IsLogEnabled()) { LOG_COMPILER(INFO) << ""; @@ -134,7 +123,7 @@ public: void VisitRegion(GateRegion* curRegion) { replacement_.SetState(curRegion->GetState()); - currentIndex_ = curRegion->gateList_.size() - 1; // 1: -1 for size + currentIndex_ = static_cast(curRegion->gateList_.size() - 1); // 1: -1 for size TryLoadDependStart(curRegion); // 0: is state for (; currentIndex_ > 0; currentIndex_--) { @@ -191,11 +180,6 @@ public: ASSERT(replacement_.State() != Circuit::NullGate()); replacement_ = lowering.LowerConvert(replacement_, gate); break; - case OpCode::CHECK_AND_CONVERT: - ASSERT(replacement_.State() != Circuit::NullGate()); - ASSERT(frameState_ != Circuit::NullGate()); - replacement_ = lowering.LowerCheckAndConvert(replacement_, gate, frameState_); - break; default: break; } diff --git a/ecmascript/compiler/state_split_linearizer.h b/ecmascript/compiler/state_split_linearizer.h index ec51503088aef321bcead977a614d02865b1c6fb..26ae54690faafe6f84ebb93256969b256491616a 100644 --- a/ecmascript/compiler/state_split_linearizer.h +++ b/ecmascript/compiler/state_split_linearizer.h @@ -27,11 +27,11 @@ namespace panda::ecmascript::kungfu { class StateSplitLinearizer { public: - StateSplitLinearizer(Circuit *circuit, CompilationConfig *cmpCfg, + StateSplitLinearizer(Circuit *circuit, RPOVisitor *visitor, CompilationConfig *cmpCfg, bool enableLog, const std::string& name, Chunk* chunk) : enableLog_(enableLog), methodName_(name), circuit_(circuit), - graphLinearizer_(circuit, enableLog, name, chunk), - lcrLowering_(circuit, cmpCfg, enableLog, name) {} + graphLinearizer_(circuit, enableLog, name, chunk, false, true), + lcrLowering_(circuit, visitor, cmpCfg, chunk) {} void Run(); private: diff --git a/ecmascript/compiler/stub_builder-inl.h b/ecmascript/compiler/stub_builder-inl.h index 2be14b4c72ee5da7be1e1d8564354199e79c4118..e677d080e3f07d926f2d113975ce14609bc45fcf 100644 --- a/ecmascript/compiler/stub_builder-inl.h +++ b/ecmascript/compiler/stub_builder-inl.h @@ -26,16 +26,19 @@ #include "ecmascript/global_env.h" #include "ecmascript/global_env_constants.h" #include "ecmascript/ic/ic_handler.h" +#include "ecmascript/ic/profile_type_info.h" #include "ecmascript/ic/proto_change_details.h" #include "ecmascript/js_array.h" #include "ecmascript/js_function.h" #include "ecmascript/js_generator_object.h" #include "ecmascript/js_object.h" #include "ecmascript/js_tagged_value.h" +#include "ecmascript/jspandafile/program_object.h" #include "ecmascript/layout_info.h" #include "ecmascript/message_string.h" #include "ecmascript/mem/slots.h" #include "ecmascript/mem/visitor.h" +#include "ecmascript/property_attributes.h" namespace panda::ecmascript::kungfu { using JSFunction = panda::ecmascript::JSFunction; @@ -61,7 +64,7 @@ inline GateRef StubBuilder::Int64(int64_t value) return env_->GetBuilder()->Int64(value); } -inline GateRef StubBuilder::StringPtr(const std::string &str) +inline GateRef StubBuilder::StringPtr(std::string_view str) { return env_->GetBuilder()->StringPtr(str); } @@ -550,6 +553,12 @@ inline GateRef StubBuilder::BinaryOp(GateRef x, GateRef y) return env_->GetBuilder()->BinaryOp(x, y); } +template +inline GateRef StubBuilder::BinaryOpWithOverflow(GateRef x, GateRef y) +{ + return env_->GetBuilder()->BinaryOpWithOverflow(x, y); +} + inline GateRef StubBuilder::TaggedIsInt(GateRef x) { return env_->GetBuilder()->TaggedIsInt(x); @@ -575,6 +584,11 @@ inline GateRef StubBuilder::TaggedIsStringOrSymbol(GateRef obj) return env_->GetBuilder()->TaggedIsStringOrSymbol(obj); } +inline GateRef StubBuilder::TaggedIsSymbol(GateRef obj) +{ + return env_->GetBuilder()->TaggedIsSymbol(obj); +} + inline GateRef StubBuilder::BothAreString(GateRef x, GateRef y) { auto allHeapObject = BoolAnd(TaggedIsHeapObject(x), TaggedIsHeapObject(y)); @@ -627,6 +641,11 @@ inline GateRef StubBuilder::TaggedIsGeneratorObject(GateRef x) return env_->GetBuilder()->TaggedIsGeneratorObject(x); } +inline GateRef StubBuilder::TaggedIsJSArray(GateRef x) +{ + return env_->GetBuilder()->TaggedIsJSArray(x); +} + inline GateRef StubBuilder::TaggedIsAsyncGeneratorObject(GateRef x) { return env_->GetBuilder()->TaggedIsAsyncGeneratorObject(x); @@ -743,6 +762,16 @@ inline GateRef StubBuilder::DoubleToTaggedDoublePtr(GateRef x) return env_->GetBuilder()->DoubleToTaggedDoublePtr(x); } +inline GateRef StubBuilder::TaggedPtrToTaggedDoublePtr(GateRef x) +{ + return DoubleToTaggedDoublePtr(CastInt64ToFloat64(ChangeTaggedPointerToInt64(x))); +} + +inline GateRef StubBuilder::TaggedPtrToTaggedIntPtr(GateRef x) +{ + return IntToTaggedPtr(TruncInt64ToInt32(ChangeTaggedPointerToInt64(x))); +} + inline GateRef StubBuilder::CastDoubleToInt64(GateRef x) { return env_->GetBuilder()->CastDoubleToInt64(x); @@ -758,6 +787,11 @@ inline GateRef StubBuilder::TaggedFalse() return env_->GetBuilder()->TaggedFalse(); } +inline GateRef StubBuilder::TaggedUndefined() +{ + return env_->GetBuilder()->UndefineConstant(); +} + // compare operation inline GateRef StubBuilder::Int8Equal(GateRef x, GateRef y) { @@ -854,6 +888,11 @@ inline GateRef StubBuilder::Int32UnsignedGreaterThanOrEqual(GateRef x, GateRef y return env_->GetBuilder()->Int32UnsignedGreaterThanOrEqual(x, y); } +inline GateRef StubBuilder::Int32UnsignedLessThanOrEqual(GateRef x, GateRef y) +{ + return env_->GetBuilder()->Int32UnsignedLessThanOrEqual(x, y); +} + inline GateRef StubBuilder::Int64GreaterThan(GateRef x, GateRef y) { return env_->GetBuilder()->Int64GreaterThan(x, y); @@ -890,6 +929,16 @@ inline GateRef StubBuilder::TruncInt16ToInt8(GateRef val) return env_->GetBuilder()->TruncInt16ToInt8(val); } +inline GateRef StubBuilder::TruncInt32ToInt16(GateRef val) +{ + return env_->GetBuilder()->TruncInt32ToInt16(val); +} + +inline GateRef StubBuilder::TruncInt32ToInt8(GateRef val) +{ + return env_->GetBuilder()->TruncInt32ToInt8(val); +} + inline GateRef StubBuilder::ChangeInt64ToIntPtr(GateRef val) { if (env_->IsArch32Bit()) { @@ -1003,10 +1052,7 @@ inline GateRef StubBuilder::IsDictionaryElement(GateRef hClass) inline GateRef StubBuilder::IsClassConstructorFromBitField(GateRef bitfield) { // decode - return Int32NotEqual( - Int32And(Int32LSR(bitfield, Int32(JSHClass::ClassConstructorBit::START_BIT)), - Int32((1LU << JSHClass::ClassConstructorBit::SIZE) - 1)), - Int32(0)); + return env_->GetBuilder()->IsClassConstructorWithBitField(bitfield); } inline GateRef StubBuilder::IsClassConstructor(GateRef object) @@ -1043,7 +1089,7 @@ inline GateRef StubBuilder::TaggedObjectIsEcmaObject(GateRef obj) inline GateRef StubBuilder::IsEcmaObject(GateRef obj) { - auto isHeapObject = TaggedIsHeapObject(obj); + auto isHeapObject = TaggedIsHeapObject(obj); return env_->GetBuilder()->LogicAnd(isHeapObject, TaggedObjectIsEcmaObject(obj)); } @@ -1102,6 +1148,12 @@ inline GateRef StubBuilder::IsLineString(GateRef obj) return Int32Equal(objectType, Int32(static_cast(JSType::LINE_STRING))); } +inline GateRef StubBuilder::IsSlicedString(GateRef obj) +{ + GateRef objectType = GetObjectType(LoadHClass(obj)); + return Int32Equal(objectType, Int32(static_cast(JSType::SLICED_STRING))); +} + inline GateRef StubBuilder::IsConstantString(GateRef obj) { GateRef objectType = GetObjectType(LoadHClass(obj)); @@ -1110,15 +1162,12 @@ inline GateRef StubBuilder::IsConstantString(GateRef obj) inline GateRef StubBuilder::IsTreeString(GateRef obj) { - GateRef objectType = GetObjectType(LoadHClass(obj)); - return Int32Equal(objectType, Int32(static_cast(JSType::TREE_STRING))); + return env_->GetBuilder()->IsTreeString(obj); } inline GateRef StubBuilder::TreeStringIsFlat(GateRef string) { - GateRef second = GetSecondFromTreeString(string); - GateRef len = GetLengthFromString(second); - return Int32Equal(len, Int32(0)); + return env_->GetBuilder()->TreeStringIsFlat(string); } inline GateRef StubBuilder::TaggedObjectIsBigInt(GateRef obj) @@ -1223,6 +1272,26 @@ inline GateRef StubBuilder::IsJSAPIArrayList(GateRef obj) return Int32Equal(objectType, Int32(static_cast(JSType::JS_API_ARRAY_LIST))); } +inline GateRef StubBuilder::IsJSObjectType(GateRef obj, JSType jsType) +{ + auto env = GetEnvironment(); + Label entryPass(env); + env->SubCfgEntry(&entryPass); + DEFVARIABLE(result, VariableType::BOOL(), False()); + Label heapObj(env); + Label exit(env); + GateRef isHeapObject = TaggedIsHeapObject(obj); + Branch(isHeapObject, &heapObj, &exit); + Bind(&heapObj); + GateRef objectType = GetObjectType(LoadHClass(obj)); + result = env_->GetBuilder()->LogicAnd(isHeapObject, Int32Equal(objectType, Int32(static_cast(jsType)))); + Jump(&exit); + Bind(&exit); + auto ret = *result; + env->SubCfgExit(); + return ret; +} + inline GateRef StubBuilder::GetTarget(GateRef proxyObj) { GateRef offset = IntPtr(JSProxy::TARGET_OFFSET); @@ -1329,6 +1398,33 @@ inline GateRef StubBuilder::IsField(GateRef attr) Int32(HandlerBase::HandlerKind::FIELD)); } +inline GateRef StubBuilder::IsElement(GateRef attr) +{ + return Int32Equal( + Int32And( + Int32LSR(attr, Int32(HandlerBase::KindBit::START_BIT)), + Int32((1LLU << HandlerBase::KindBit::SIZE) - 1)), + Int32(HandlerBase::HandlerKind::ELEMENT)); +} + +inline GateRef StubBuilder::IsStringElement(GateRef attr) +{ + return Int32Equal( + Int32And( + Int32LSR(attr, Int32(HandlerBase::KindBit::START_BIT)), + Int32((1LLU << HandlerBase::KindBit::SIZE) - 1)), + Int32(HandlerBase::HandlerKind::STRING)); +} + +inline GateRef StubBuilder::IsStringLength(GateRef attr) +{ + return Int32Equal( + Int32And( + Int32LSR(attr, Int32(HandlerBase::KindBit::START_BIT)), + Int32((1LLU << HandlerBase::KindBit::SIZE) - 1)), + Int32(HandlerBase::HandlerKind::STRING_LENGTH)); +} + inline GateRef StubBuilder::IsNonExist(GateRef attr) { return Int32Equal( @@ -1380,6 +1476,12 @@ inline GateRef StubBuilder::HandlerBaseGetAttrIndex(GateRef attr) Int32((1LLU << HandlerBase::AttrIndexBit::SIZE) - 1)); } +inline GateRef StubBuilder::HandlerBaseGetRep(GateRef attr) +{ + return Int32And(Int32LSR(attr, Int32(HandlerBase::RepresentationBit::START_BIT)), + Int32((1LLU << HandlerBase::RepresentationBit::SIZE) - 1)); +} + inline GateRef StubBuilder::IsInternalAccessor(GateRef attr) { return Int32NotEqual( @@ -1471,19 +1573,25 @@ inline GateRef StubBuilder::GetBitFieldFromHClass(GateRef hClass) inline GateRef StubBuilder::GetLengthFromString(GateRef value) { GateRef len = Load(VariableType::INT32(), value, IntPtr(EcmaString::MIX_LENGTH_OFFSET)); - return Int32LSR(len, Int32(2)); // 2 : 2 means len must be right shift 2 bits + return Int32LSR(len, Int32(EcmaString::STRING_LENGTH_SHIFT_COUNT)); } inline GateRef StubBuilder::GetFirstFromTreeString(GateRef string) { - GateRef offset = IntPtr(TreeEcmaString::FIRST_OFFSET); - return Load(VariableType::JS_POINTER(), string, offset); + return env_->GetBuilder()->GetFirstFromTreeString(string); } inline GateRef StubBuilder::GetSecondFromTreeString(GateRef string) { - GateRef offset = IntPtr(TreeEcmaString::SECOND_OFFSET); - return Load(VariableType::JS_POINTER(), string, offset); + return env_->GetBuilder()->GetSecondFromTreeString(string); +} + +inline GateRef StubBuilder::GetIsAllTaggedPropFromHClass(GateRef hclass) +{ + GateRef bitfield = Load(VariableType::INT32(), hclass, IntPtr(JSHClass::BIT_FIELD1_OFFSET)); + return Int32And(Int32LSR(bitfield, + Int32(JSHClass::IsAllTaggedPropBit::START_BIT)), + Int32((1LLU << JSHClass::IsAllTaggedPropBit::SIZE) - 1)); } inline void StubBuilder::SetBitFieldToHClass(GateRef glue, GateRef hClass, GateRef bitfield) @@ -1492,6 +1600,17 @@ inline void StubBuilder::SetBitFieldToHClass(GateRef glue, GateRef hClass, GateR Store(VariableType::INT32(), glue, hClass, offset, bitfield); } +inline void StubBuilder::SetIsAllTaggedProp(GateRef glue, GateRef hclass, GateRef hasRep) +{ + GateRef bitfield1 = Load(VariableType::INT32(), hclass, IntPtr(JSHClass::BIT_FIELD1_OFFSET)); + GateRef mask = Int32LSL( + Int32((1LU << JSHClass::IsAllTaggedPropBit::SIZE) - 1), + Int32(JSHClass::IsAllTaggedPropBit::START_BIT)); + GateRef newVal = Int32Or(Int32And(bitfield1, Int32Not(mask)), + Int32LSL(hasRep, Int32(JSHClass::IsAllTaggedPropBit::START_BIT))); + Store(VariableType::INT32(), glue, hclass, IntPtr(JSHClass::BIT_FIELD1_OFFSET), newVal); +} + inline void StubBuilder::SetPrototypeToHClass(VariableType type, GateRef glue, GateRef hClass, GateRef proto) { GateRef offset = IntPtr(JSHClass::PROTOTYPE_OFFSET); @@ -1522,6 +1641,12 @@ inline void StubBuilder::SetTransitionsToHClass(VariableType type, GateRef glue, Store(type, glue, hClass, offset, transition); } +inline void StubBuilder::SetParentToHClass(VariableType type, GateRef glue, GateRef hClass, GateRef parent) +{ + GateRef offset = IntPtr(JSHClass::PARENT_OFFSET); + Store(type, glue, hClass, offset, parent); +} + inline void StubBuilder::SetIsProtoTypeToHClass(GateRef glue, GateRef hClass, GateRef value) { GateRef oldValue = ZExtInt1ToInt32(value); @@ -1568,6 +1693,14 @@ inline GateRef StubBuilder::GetPropertyInlinedProps(GateRef obj, GateRef hClass, return Load(VariableType::JS_ANY(), obj, ZExtInt32ToInt64(propOffset)); } +inline GateRef StubBuilder::GetInlinedPropOffsetFromHClass(GateRef hclass, GateRef index) +{ + GateRef inlinedPropsStart = GetInlinedPropsStartFromHClass(hclass); + GateRef propOffset = Int32Mul( + Int32Add(inlinedPropsStart, index), Int32(JSTaggedValue::TaggedTypeSize())); + return ZExtInt32ToInt64(propOffset); +} + inline void StubBuilder::IncNumberOfProps(GateRef glue, GateRef hClass) { GateRef propNums = GetNumberOfPropsFromHClass(hClass); @@ -1613,6 +1746,21 @@ inline GateRef StubBuilder::GetInlinedPropertiesFromHClass(GateRef hClass) return Int32Sub(objectSizeInWords, inlinedPropsStart); } +inline void StubBuilder::SetElementsKindToTrackInfo(GateRef glue, GateRef trackInfo, GateRef elementsKind) +{ + GateRef bitfield = Load(VariableType::INT32(), trackInfo, IntPtr(TrackInfo::BIT_FIELD_OFFSET)); + GateRef oldWithMask = Int32And(bitfield, + Int32(~static_cast(TrackInfo::ElementsKindBits::Mask()))); + GateRef newValue = Int32LSR(elementsKind, Int32(TrackInfo::ElementsKindBits::START_BIT)); + GateRef newBitfield = Int32Or(oldWithMask, newValue); + Store(VariableType::INT32(), glue, trackInfo, IntPtr(TrackInfo::BIT_FIELD_OFFSET), newBitfield); +} + +inline GateRef StubBuilder::GetElementsKindFromHClass(GateRef hClass) +{ + return env_->GetBuilder()->GetElementsKindByHClass(hClass); +} + inline GateRef StubBuilder::GetObjectSizeFromHClass(GateRef hClass) { return env_->GetBuilder()->GetObjectSizeFromHClass(hClass); @@ -1626,6 +1774,22 @@ inline GateRef StubBuilder::GetInlinedPropsStartFromHClass(GateRef hClass) Int32((1LU << JSHClass::InlinedPropsStartBits::SIZE) - 1)); } +inline void StubBuilder::SetValueToTaggedArrayWithAttr( + GateRef glue, GateRef array, GateRef index, GateRef key, GateRef val, GateRef attr) +{ + GateRef offset = PtrMul(ZExtInt32ToPtr(index), IntPtr(JSTaggedValue::TaggedTypeSize())); + GateRef dataOffset = PtrAdd(offset, IntPtr(TaggedArray::DATA_OFFSET)); + SetValueWithAttr(glue, array, dataOffset, key, val, attr); +} + +inline void StubBuilder::SetValueToTaggedArrayWithRep( + GateRef glue, GateRef array, GateRef index, GateRef val, GateRef rep, Label *repChange) +{ + GateRef offset = PtrMul(ZExtInt32ToPtr(index), IntPtr(JSTaggedValue::TaggedTypeSize())); + GateRef dataOffset = PtrAdd(offset, IntPtr(TaggedArray::DATA_OFFSET)); + SetValueWithRep(glue, array, dataOffset, val, rep, repChange); +} + inline void StubBuilder::SetValueToTaggedArray(VariableType valType, GateRef glue, GateRef array, GateRef index, GateRef val) { @@ -1715,6 +1879,11 @@ inline GateRef StubBuilder::TaggedCastToIntPtr(GateRef x) return env_->Is32Bit() ? TruncInt64ToInt32(GetInt64OfTInt(x)) : GetInt64OfTInt(x); } +inline GateRef StubBuilder::GetDoubleOfTInt(GateRef x) +{ + return ChangeInt32ToFloat64(GetInt32OfTInt(x)); +} + inline GateRef StubBuilder::GetDoubleOfTDouble(GateRef x) { return env_->GetBuilder()->GetDoubleOfTDouble(x); @@ -1950,6 +2119,7 @@ inline GateRef StubBuilder::SetIsInlinePropsFieldInPropAttr(GateRef attr, GateRe return newVal; } + inline GateRef StubBuilder::SetTrackTypeInPropAttr(GateRef attr, GateRef type) { GateRef mask = Int32LSL( @@ -1967,6 +2137,34 @@ inline GateRef StubBuilder::GetTrackTypeInPropAttr(GateRef attr) Int32((1LLU << PropertyAttributes::TrackTypeField::SIZE) - 1)); } +inline GateRef StubBuilder::GetRepInPropAttr(GateRef attr) +{ + return Int32And( + Int32LSR(attr, Int32(PropertyAttributes::RepresentationField::START_BIT)), + Int32((1LLU << PropertyAttributes::RepresentationField::SIZE) - 1)); +} + +inline GateRef StubBuilder::IsIntRepInPropAttr(GateRef rep) +{ + return Int32Equal(rep, Int32(static_cast(Representation::INT))); +} + +inline GateRef StubBuilder::IsDoubleRepInPropAttr(GateRef rep) +{ + return Int32Equal(rep, Int32(static_cast(Representation::DOUBLE))); +} + +inline GateRef StubBuilder::SetTaggedRepInPropAttr(GateRef attr) +{ + GateRef mask = Int32LSL( + Int32((1LU << PropertyAttributes::RepresentationField::SIZE) - 1), + Int32(PropertyAttributes::RepresentationField::START_BIT)); + GateRef targetType = Int32(static_cast(Representation::TAGGED)); + GateRef newVal = Int32Or(Int32And(attr, Int32Not(mask)), + Int32LSL(targetType, Int32(PropertyAttributes::RepresentationField::START_BIT))); + return newVal; +} + inline void StubBuilder::SetHasConstructorToHClass(GateRef glue, GateRef hClass, GateRef value) { GateRef bitfield = Load(VariableType::INT32(), hClass, IntPtr(JSHClass::BIT_FIELD_OFFSET)); @@ -2190,7 +2388,7 @@ inline GateRef StubBuilder::ComputeTaggedArraySize(GateRef length) inline GateRef StubBuilder::GetGlobalConstantValue(VariableType type, GateRef glue, ConstantIndex index) { - GateRef gConstAddr = PtrAdd(glue, + GateRef gConstAddr = Load(VariableType::JS_ANY(), glue, IntPtr(JSThread::GlueData::GetGlobalConstOffset(env_->Is32Bit()))); auto constantIndex = IntPtr(JSTaggedValue::TaggedTypeSize() * static_cast(index)); return Load(type, gConstAddr, constantIndex); @@ -2250,7 +2448,7 @@ inline GateRef StubBuilder::AlignUp(GateRef x, GateRef alignment) inline void StubBuilder::SetLength(GateRef glue, GateRef str, GateRef length, bool compressed) { - GateRef len = Int32LSL(length, Int32(2)); + GateRef len = Int32LSL(length, Int32(EcmaString::STRING_LENGTH_SHIFT_COUNT)); GateRef mixLength; if (compressed) { mixLength = Int32Or(len, Int32(EcmaString::STRING_COMPRESSED)); @@ -2260,6 +2458,13 @@ inline void StubBuilder::SetLength(GateRef glue, GateRef str, GateRef length, bo Store(VariableType::INT32(), glue, str, IntPtr(EcmaString::MIX_LENGTH_OFFSET), mixLength); } +inline void StubBuilder::SetLength(GateRef glue, GateRef str, GateRef length, GateRef isCompressed) +{ + GateRef len = Int32LSL(length, Int32(EcmaString::STRING_LENGTH_SHIFT_COUNT)); + GateRef mixLength = Int32Or(len, isCompressed); + Store(VariableType::INT32(), glue, str, IntPtr(EcmaString::MIX_LENGTH_OFFSET), mixLength); +} + inline void StubBuilder::SetRawHashcode(GateRef glue, GateRef str, GateRef rawHashcode) { Store(VariableType::INT32(), glue, str, IntPtr(EcmaString::HASHCODE_OFFSET), rawHashcode); @@ -2292,6 +2497,16 @@ inline GateRef StubBuilder::IsStableElements(GateRef hClass) return env_->GetBuilder()->IsStableElements(hClass); } +inline GateRef StubBuilder::HasConstructorByHClass(GateRef hClass) +{ + return env_->GetBuilder()->HasConstructorByHClass(hClass); +} + +inline GateRef StubBuilder::HasConstructor(GateRef object) +{ + return env_->GetBuilder()->HasConstructor(object); +} + inline GateRef StubBuilder::IsStableArguments(GateRef hClass) { return env_->GetBuilder()->IsStableArguments(hClass); @@ -2320,5 +2535,82 @@ inline GateRef StubBuilder::LoadObjectFromConstPool(GateRef jsFunc, GateRef inde { return env_->GetBuilder()->LoadObjectFromConstPool(jsFunc, index); } + +inline void StubBuilder::CheckDetectorName(GateRef glue, GateRef key, Label *fallthrough, Label *slow) +{ + GateRef glueGlobalEnvOffset = IntPtr(JSThread::GlueData::GetGlueGlobalEnvOffset(env_->Is32Bit())); + GateRef glueGlobalEnv = Load(VariableType::NATIVE_POINTER(), glue, glueGlobalEnvOffset); + GateRef keyAddr = ChangeTaggedPointerToInt64(key); + GateRef firstDetectorName = GetGlobalEnvValue( + VariableType::INT64(), glueGlobalEnv, GlobalEnv::FIRST_DETECTOR_SYMBOL_INDEX); + GateRef lastDetectorName = GetGlobalEnvValue( + VariableType::INT64(), glueGlobalEnv, GlobalEnv::LAST_DETECTOR_SYMBOL_INDEX); + GateRef isDetectorName = BoolAnd(Int64UnsignedLessThanOrEqual(firstDetectorName, keyAddr), + Int64UnsignedLessThanOrEqual(keyAddr, lastDetectorName)); + Branch(isDetectorName, slow, fallthrough); +} + +inline GateRef StubBuilder::LoadPfHeaderFromConstPool(GateRef jsFunc) +{ + GateRef method = Load(VariableType::JS_ANY(), jsFunc, IntPtr(JSFunctionBase::METHOD_OFFSET)); + GateRef constPool = Load(VariableType::JS_ANY(), method, IntPtr(Method::CONSTANT_POOL_OFFSET)); + auto length = GetLengthOfTaggedArray(constPool); + auto index = Int32Sub(length, Int32(ConstantPool::JS_PANDA_FILE_INDEX)); + auto jsPandaFile = GetValueFromTaggedArray(constPool, index); + auto jsPfAddr = ChangeInt64ToIntPtr(ChangeTaggedPointerToInt64(jsPandaFile)); + auto pfAddr = Load(VariableType::NATIVE_POINTER(), jsPfAddr, Int32(JSPandaFile::PF_OFFSET)); + auto pfHeader = Load(VariableType::NATIVE_POINTER(), pfAddr, Int32(0)); + return pfHeader; +} + +inline GateRef StubBuilder::LoadHCIndexInfosFromConstPool(GateRef jsFunc) +{ + GateRef method = Load(VariableType::JS_ANY(), jsFunc, IntPtr(JSFunctionBase::METHOD_OFFSET)); + GateRef constPool = Load(VariableType::JS_ANY(), method, IntPtr(Method::CONSTANT_POOL_OFFSET)); + auto length = GetLengthOfTaggedArray(constPool); + auto index = Int32Sub(length, Int32(ConstantPool::CONSTANT_INDEX_INFO_INDEX)); + return GetValueFromTaggedArray(constPool, index); +} + +inline GateRef StubBuilder::LoadHCIndexFromConstPool( + GateRef cachedArray, GateRef cachedLength, GateRef traceId, Label *miss) +{ + auto env = GetEnvironment(); + Label subEntry(env); + env->SubCfgEntry(&subEntry); + + DEFVARIABLE(bcOffset, VariableType::INT32(), Int32(0)); + DEFVARIABLE(constantIndex, VariableType::INT32(), + Int32(static_cast(ConstantIndex::ELEMENT_HOLE_TAGGED_HCLASS_INDEX))); + DEFVARIABLE(i, VariableType::INT32(), Int32(0)); + + Label loopHead(env); + Label loopEnd(env); + Label afterLoop(env); + Label matchSuccess(env); + Label afterUpdate(env); + Branch(Int32LessThan(*i, cachedLength), &loopHead, miss); + LoopBegin(&loopHead); + bcOffset = GetInt32OfTInt(GetValueFromTaggedArray(cachedArray, *i)); + Branch(Int32Equal(*bcOffset, traceId), &matchSuccess, &afterUpdate); + Bind(&matchSuccess); + constantIndex = GetInt32OfTInt(GetValueFromTaggedArray(cachedArray, Int32Add(*i, Int32(1)))); + Jump(&afterLoop); + Bind(&afterUpdate); + i = Int32Add(*i, Int32(2)); // 2 : skip traceId and constantIndex + Branch(Int32LessThan(*i, cachedLength), &loopEnd, miss); + Bind(&loopEnd); + LoopEnd(&loopHead); + Bind(&afterLoop); + auto ret = *constantIndex; + + env->SubCfgExit(); + return ret; +} + +inline GateRef StubBuilder::RemoveTaggedWeakTag(GateRef weak) +{ + return Int64ToTaggedPtr(IntPtrAnd(ChangeTaggedPointerToInt64(weak), IntPtr(~JSTaggedValue::TAG_WEAK))); +} } // namespace panda::ecmascript::kungfu #endif // ECMASCRIPT_COMPILER_STUB_INL_H diff --git a/ecmascript/compiler/stub_builder.cpp b/ecmascript/compiler/stub_builder.cpp index 276d5f27e24eab2a36fb3f533f0cdcc44e15f00a..beaf2fe0d653740611ae510a60cd0d8a211eee94 100644 --- a/ecmascript/compiler/stub_builder.cpp +++ b/ecmascript/compiler/stub_builder.cpp @@ -17,19 +17,22 @@ #include "ecmascript/compiler/assembler_module.h" #include "ecmascript/compiler/access_object_stub_builder.h" +#include "ecmascript/compiler/builtins/builtins_string_stub_builder.h" #include "ecmascript/compiler/interpreter_stub.h" #include "ecmascript/compiler/llvm_ir_builder.h" #include "ecmascript/compiler/new_object_stub_builder.h" #include "ecmascript/compiler/profiler_stub_builder.h" #include "ecmascript/compiler/rt_call_signature.h" #include "ecmascript/compiler/typed_array_stub_builder.h" +#include "ecmascript/global_env_constants.h" #include "ecmascript/js_api/js_api_arraylist.h" #include "ecmascript/js_api/js_api_vector.h" #include "ecmascript/js_object.h" #include "ecmascript/js_arguments.h" #include "ecmascript/mem/remembered_set.h" #include "ecmascript/message_string.h" -#include "ecmascript/pgo_profiler/pgo_profiler_type.h" +#include "ecmascript/pgo_profiler/types/pgo_profiler_type.h" +#include "ecmascript/property_attributes.h" #include "ecmascript/tagged_dictionary.h" #include "ecmascript/tagged_hash_table.h" @@ -453,13 +456,15 @@ GateRef StubBuilder::JSObjectGetProperty(GateRef obj, GateRef hclass, GateRef at DEFVARIABLE(result, VariableType::JS_ANY(), Undefined()); Label inlinedProp(env); Label notInlinedProp(env); + Label post(env); GateRef attrOffset = GetOffsetFieldInPropAttr(attr); + GateRef rep = GetRepInPropAttr(attr); Branch(IsInlinedProperty(attr), &inlinedProp, ¬InlinedProp); { Bind(&inlinedProp); { result = GetPropertyInlinedProps(obj, hclass, attrOffset); - Jump(&exit); + Jump(&post); } Bind(¬InlinedProp); { @@ -468,8 +473,29 @@ GateRef StubBuilder::JSObjectGetProperty(GateRef obj, GateRef hclass, GateRef at Load(VariableType::INT64(), obj, IntPtr(JSObject::PROPERTIES_OFFSET)); result = GetValueFromTaggedArray(array, Int32Sub(attrOffset, GetInlinedPropertiesFromHClass(hclass))); + Jump(&post); + } + } + Bind(&post); + { + Label nonDoubleToTagged(env); + Label doubleToTagged(env); + Branch(IsDoubleRepInPropAttr(rep), &doubleToTagged, &nonDoubleToTagged); + Bind(&doubleToTagged); + { + result = TaggedPtrToTaggedDoublePtr(*result); Jump(&exit); } + Bind(&nonDoubleToTagged); + { + Label intToTagged(env); + Branch(IsIntRepInPropAttr(rep), &intToTagged, &exit); + Bind(&intToTagged); + { + result = TaggedPtrToTaggedIntPtr(*result); + Jump(&exit); + } + } } Bind(&exit); auto ret = *result; @@ -477,7 +503,8 @@ GateRef StubBuilder::JSObjectGetProperty(GateRef obj, GateRef hclass, GateRef at return ret; } -void StubBuilder::JSObjectSetProperty(GateRef glue, GateRef obj, GateRef hclass, GateRef attr, GateRef value) +void StubBuilder::JSObjectSetProperty( + GateRef glue, GateRef obj, GateRef hclass, GateRef attr, GateRef key, GateRef value) { auto env = GetEnvironment(); Label subEntry(env); @@ -485,12 +512,13 @@ void StubBuilder::JSObjectSetProperty(GateRef glue, GateRef obj, GateRef hclass, Label exit(env); Label inlinedProp(env); Label notInlinedProp(env); - GateRef attrOffset = GetOffsetFieldInPropAttr(attr); + GateRef attrIndex = GetOffsetFieldInPropAttr(attr); Branch(IsInlinedProperty(attr), &inlinedProp, ¬InlinedProp); { Bind(&inlinedProp); { - SetPropertyInlinedProps(glue, obj, hclass, value, attrOffset); + GateRef offset = GetInlinedPropOffsetFromHClass(hclass, attrIndex); + SetValueWithAttr(glue, obj, offset, key, value, attr); Jump(&exit); } Bind(¬InlinedProp); @@ -498,8 +526,8 @@ void StubBuilder::JSObjectSetProperty(GateRef glue, GateRef obj, GateRef hclass, // compute outOfLineProp offset, get it and return GateRef array = Load(VariableType::JS_POINTER(), obj, IntPtr(JSObject::PROPERTIES_OFFSET)); - SetValueToTaggedArray(VariableType::JS_ANY(), glue, array, Int32Sub(attrOffset, - GetInlinedPropertiesFromHClass(hclass)), value); + GateRef offset = Int32Sub(attrIndex, GetInlinedPropertiesFromHClass(hclass)); + SetValueToTaggedArrayWithAttr(glue, array, offset, key, value, attr); Jump(&exit); } } @@ -508,7 +536,7 @@ void StubBuilder::JSObjectSetProperty(GateRef glue, GateRef obj, GateRef hclass, return; } -GateRef StubBuilder::ComputePropertyCapacityInJSObj(GateRef oldLength) +GateRef StubBuilder::ComputeNonInlinedFastPropsCapacity(GateRef oldLength, GateRef maxNonInlinedFastPropsCapacity) { auto env = GetEnvironment(); Label subEntry(env); @@ -518,11 +546,10 @@ GateRef StubBuilder::ComputePropertyCapacityInJSObj(GateRef oldLength) GateRef newL = Int32Add(oldLength, Int32(JSObject::PROPERTIES_GROW_SIZE)); Label reachMax(env); Label notReachMax(env); - Branch(Int32GreaterThan(newL, Int32(JSHClass::MAX_CAPACITY_OF_OUT_OBJECTS)), - &reachMax, ¬ReachMax); + Branch(Int32GreaterThan(newL, maxNonInlinedFastPropsCapacity), &reachMax, ¬ReachMax); { Bind(&reachMax); - result = Int32(JSHClass::MAX_CAPACITY_OF_OUT_OBJECTS); + result = maxNonInlinedFastPropsCapacity; Jump(&exit); Bind(¬ReachMax); result = newL; @@ -534,7 +561,8 @@ GateRef StubBuilder::ComputePropertyCapacityInJSObj(GateRef oldLength) return ret; } -GateRef StubBuilder::CallGetterHelper(GateRef glue, GateRef receiver, GateRef holder, GateRef accessor) +GateRef StubBuilder::CallGetterHelper( + GateRef glue, GateRef receiver, GateRef holder, GateRef accessor, ProfileOperation callback) { auto env = GetEnvironment(); Label subEntry(env); @@ -554,9 +582,23 @@ GateRef StubBuilder::CallGetterHelper(GateRef glue, GateRef receiver, GateRef ho Branch(Equal(accessor, lengthAccessor), &arrayLength, &tryContinue); Bind(&arrayLength); { - result = Load(VariableType::JS_ANY(), holder, - IntPtr(JSArray::LENGTH_OFFSET)); - Jump(&exit); + auto length = Load(VariableType::INT32(), holder, IntPtr(JSArray::LENGTH_OFFSET)); + // TaggedInt supports up to INT32_MAX. + // If length is greater than Int32_MAX, needs to be converted to TaggedDouble. + auto condition = Int32UnsignedGreaterThan(length, Int32(INT32_MAX)); + Label overflow(env); + Label notOverflow(env); + Branch(condition, &overflow, ¬Overflow); + Bind(&overflow); + { + result = DoubleToTaggedDoublePtr(ChangeUInt32ToFloat64(length)); + Jump(&exit); + } + Bind(¬Overflow); + { + result = IntToTaggedPtr(length); + Jump(&exit); + } } Bind(&tryContinue); result = CallRuntime(glue, RTSTUB_ID(CallInternalGetter), { accessor, holder }); @@ -578,7 +620,7 @@ GateRef StubBuilder::CallGetterHelper(GateRef glue, GateRef receiver, GateRef ho Bind(&objNotUndefined); { auto retValue = JSCallDispatch(glue, getter, Int32(0), 0, Circuit::NullGate(), - JSCallMode::CALL_GETTER, { receiver }); + JSCallMode::CALL_GETTER, { receiver }, callback); Label noPendingException(env); Branch(HasPendingException(glue), &exit, &noPendingException); Bind(&noPendingException); @@ -594,7 +636,8 @@ GateRef StubBuilder::CallGetterHelper(GateRef glue, GateRef receiver, GateRef ho return ret; } -GateRef StubBuilder::CallSetterHelper(GateRef glue, GateRef receiver, GateRef accessor, GateRef value) +GateRef StubBuilder::CallSetterHelper( + GateRef glue, GateRef receiver, GateRef accessor, GateRef value, ProfileOperation callback) { auto env = GetEnvironment(); Label subEntry(env); @@ -626,7 +669,7 @@ GateRef StubBuilder::CallSetterHelper(GateRef glue, GateRef receiver, GateRef ac Bind(&objNotUndefined); { auto retValue = JSCallDispatch(glue, setter, Int32(1), 0, Circuit::NullGate(), - JSCallMode::CALL_SETTER, { receiver, value }); + JSCallMode::CALL_SETTER, { receiver, value }, callback); Label noPendingException(env); Branch(HasPendingException(glue), &exit, &noPendingException); Bind(&noPendingException); @@ -722,8 +765,9 @@ void StubBuilder::JSHClassAddProperty(GateRef glue, GateRef receiver, GateRef ke // keyHandle.GetTaggedValue() == thread->GlobalConstants()->GetConstructorString() GateRef StubBuilder::SetHasConstructorCondition(GateRef glue, GateRef receiver, GateRef key) { - GateRef gConstOffset = PtrAdd(glue, - IntPtr(JSThread::GlueData::GetGlobalConstOffset(env_->Is32Bit()))); + GateRef gConstOffset = Load(VariableType::JS_ANY(), glue, + IntPtr(JSThread::GlueData::GetGlobalConstOffset(env_->Is32Bit()))); + GateRef gCtorStr = Load(VariableType::JS_ANY(), gConstOffset, Int64Mul(Int64(sizeof(JSTaggedValue)), @@ -770,9 +814,9 @@ GateRef StubBuilder::AddPropertyByName(GateRef glue, GateRef receiver, GateRef k SetPropertyInlinedProps(glue, receiver, hclass, value, numberOfProps); attr = SetOffsetFieldInPropAttr(*attr, numberOfProps); attr = SetIsInlinePropsFieldInPropAttr(*attr, Int32(1)); // 1: set inInlineProps true + attr = SetTaggedRepInPropAttr(*attr); attr = ProfilerStubBuilder(env).UpdateTrackTypeInPropAttr(*attr, value, callback); JSHClassAddProperty(glue, receiver, key, *attr); - callback.ProfileObjLayoutByStore(receiver); result = Undefined(); Jump(&exit); } @@ -821,13 +865,15 @@ GateRef StubBuilder::AddPropertyByName(GateRef glue, GateRef receiver, GateRef k Label ChangeToDict(env); Label notChangeToDict(env); Label afterDictChangeCon(env); - Branch(Int32Equal(*length, Int32(JSHClass::MAX_CAPACITY_OF_OUT_OBJECTS)), + GateRef maxNonInlinedFastPropsCapacity = + Int32Sub(Int32(PropertyAttributes::MAX_FAST_PROPS_CAPACITY), inlinedProperties); + Branch(Int32GreaterThanOrEqual(*length, maxNonInlinedFastPropsCapacity), &ChangeToDict, ¬ChangeToDict); { Bind(&ChangeToDict); { attr = SetDictionaryOrderFieldInPropAttr(*attr, - Int32(PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES)); + Int32(PropertyAttributes::MAX_FAST_PROPS_CAPACITY)); GateRef res = CallRuntime(glue, RTSTUB_ID(NameDictPutIfAbsent), { receiver, *array, key, value, IntToTaggedInt(*attr), TaggedTrue() }); SetPropertiesArray(VariableType::JS_POINTER(), glue, receiver, res); @@ -838,7 +884,7 @@ GateRef StubBuilder::AddPropertyByName(GateRef glue, GateRef receiver, GateRef k Jump(&afterDictChangeCon); } Bind(&afterDictChangeCon); - GateRef capacity = ComputePropertyCapacityInJSObj(*length); + GateRef capacity = ComputeNonInlinedFastPropsCapacity(*length, maxNonInlinedFastPropsCapacity); array = CallRuntime(glue, RTSTUB_ID(CopyArray), { *array, IntToTaggedInt(*length), IntToTaggedInt(capacity) }); SetPropertiesArray(VariableType::JS_POINTER(), glue, receiver, *array); @@ -850,10 +896,10 @@ GateRef StubBuilder::AddPropertyByName(GateRef glue, GateRef receiver, GateRef k Bind(&afterArrLenCon); { attr = SetOffsetFieldInPropAttr(*attr, numberOfProps); + attr = SetTaggedRepInPropAttr(*attr); attr = ProfilerStubBuilder(env).UpdateTrackTypeInPropAttr(*attr, value, callback); JSHClassAddProperty(glue, receiver, key, *attr); SetValueToTaggedArray(VariableType::JS_ANY(), glue, *array, outProps, value); - callback.ProfileObjLayoutByStore(receiver); Jump(&exit); } } @@ -878,7 +924,7 @@ GateRef StubBuilder::TaggedToRepresentation(GateRef value) env->SubCfgEntry(&entry); Label exit(env); DEFVARIABLE(resultRep, VariableType::INT64(), - Int64(static_cast(Representation::OBJECT))); + Int64(static_cast(Representation::TAGGED))); Label isInt(env); Label notInt(env); @@ -900,7 +946,7 @@ GateRef StubBuilder::TaggedToRepresentation(GateRef value) } Bind(¬Double); { - resultRep = Int64(static_cast(Representation::OBJECT)); + resultRep = Int64(static_cast(Representation::TAGGED)); Jump(&exit); } } @@ -910,6 +956,60 @@ GateRef StubBuilder::TaggedToRepresentation(GateRef value) return ret; } +GateRef StubBuilder::TaggedToElementKind(GateRef value) +{ + auto env = GetEnvironment(); + Label entry(env); + env->SubCfgEntry(&entry); + Label exit(env); + + DEFVARIABLE(result, VariableType::INT32(), Int32(static_cast(ElementsKind::TAGGED))); + Label isInt(env); + Label isNotInt(env); + Branch(TaggedIsInt(value), &isInt, &isNotInt); + Bind(&isInt); + { + result = Int32(static_cast(ElementsKind::INT)); + Jump(&exit); + } + Bind(&isNotInt); + { + Label isObject(env); + Label isDouble(env); + Branch(TaggedIsObject(value), &isObject, &isDouble); + Bind(&isDouble); + { + result = Int32(static_cast(ElementsKind::NUMBER)); + Jump(&exit); + } + Bind(&isObject); + { + Label isHeapObject(env); + Branch(TaggedIsHeapObject(value), &isHeapObject, &exit); + Bind(&isHeapObject); + { + Label isString(env); + Label isNonString(env); + Branch(TaggedIsString(value), &isString, &isNonString); + Bind(&isString); + { + result = Int32(static_cast(ElementsKind::STRING)); + Jump(&exit); + } + Bind(&isNonString); + { + result = Int32(static_cast(ElementsKind::OBJECT)); + Jump(&exit); + } + } + } + } + Bind(&exit); + auto ret = *result; + env->SubCfgExit(); + return ret; +} + void StubBuilder::Store(VariableType type, GateRef glue, GateRef base, GateRef offset, GateRef value) { if (!env_->IsAsmInterp()) { @@ -940,6 +1040,102 @@ void StubBuilder::Store(VariableType type, GateRef glue, GateRef base, GateRef o } } +void StubBuilder::SetValueWithAttr(GateRef glue, GateRef obj, GateRef offset, GateRef key, GateRef value, GateRef attr) +{ + auto env = GetEnvironment(); + Label entry(env); + env->SubCfgEntry(&entry); + + Label exit(env); + Label repChange(env); + GateRef rep = GetRepInPropAttr(attr); + SetValueWithRep(glue, obj, offset, value, rep, &repChange); + Jump(&exit); + Bind(&repChange); + { + attr = SetTaggedRepInPropAttr(attr); + TransitionForRepChange(glue, obj, key, attr); + Store(VariableType::JS_ANY(), glue, obj, offset, value); + Jump(&exit); + } + Bind(&exit); + env->SubCfgExit(); +} + +void StubBuilder::SetValueWithRep( + GateRef glue, GateRef obj, GateRef offset, GateRef value, GateRef rep, Label *repChange) +{ + auto env = GetEnvironment(); + Label entry(env); + env->SubCfgEntry(&entry); + + Label exit(env); + Label repIsDouble(env); + Label repIsNonDouble(env); + Branch(IsDoubleRepInPropAttr(rep), &repIsDouble, &repIsNonDouble); + Bind(&repIsDouble); + { + Label valueIsInt(env); + Label valueIsNotInt(env); + Branch(TaggedIsInt(value), &valueIsInt, &valueIsNotInt); + Bind(&valueIsInt); + { + GateRef result = GetDoubleOfTInt(value); + Store(VariableType::FLOAT64(), glue, obj, offset, result); + Jump(&exit); + } + Bind(&valueIsNotInt); + { + Label valueIsObject(env); + Label valueIsDouble(env); + Branch(TaggedIsObject(value), &valueIsObject, &valueIsDouble); + Bind(&valueIsDouble); + { + // TaggedDouble to double + GateRef result = GetDoubleOfTDouble(value); + Store(VariableType::FLOAT64(), glue, obj, offset, result); + Jump(&exit); + } + Bind(&valueIsObject); + { + Jump(repChange); + } + } + } + Bind(&repIsNonDouble); + { + Label repIsInt(env); + Label repIsTagged(env); + Branch(IsIntRepInPropAttr(rep), &repIsInt, &repIsTagged); + Bind(&repIsInt); + { + Label valueIsInt(env); + Label valueIsNotInt(env); + Branch(TaggedIsInt(value), &valueIsInt, &valueIsNotInt); + Bind(&valueIsInt); + { + GateRef result = GetInt32OfTInt(value); + Store(VariableType::INT32(), glue, obj, offset, result); + Jump(&exit); + } + Bind(&valueIsNotInt); + { + Jump(repChange); + } + } + Bind(&repIsTagged); + { + Store(VariableType::JS_ANY(), glue, obj, offset, value); + Jump(&exit); + } + } + + Bind(&exit); + env->SubCfgExit(); + return; +} + + void StubBuilder::SetValueWithBarrier(GateRef glue, GateRef obj, GateRef offset, GateRef value) { auto env = GetEnvironment(); @@ -1113,9 +1309,10 @@ GateRef StubBuilder::StringToElementIndex(GateRef glue, GateRef string) Label entry(env); env->SubCfgEntry(&entry); Label exit(env); - DEFVARIABLE(result, VariableType::INT32(), Int32(-1)); + DEFVARIABLE(result, VariableType::INT64(), Int64(-1)); Label greatThanZero(env); Label inRange(env); + Label flattenFastPath(env); auto len = GetLengthFromString(string); Branch(Int32Equal(len, Int32(0)), &exit, &greatThanZero); Bind(&greatThanZero); @@ -1127,8 +1324,12 @@ GateRef StubBuilder::StringToElementIndex(GateRef glue, GateRef string) Branch(isUtf16String, &exit, &isUtf8); Bind(&isUtf8); { - GateRef dataUtf8 = GetNormalStringData(FlattenString(glue, string)); DEFVARIABLE(c, VariableType::INT32(), Int32(0)); + FlatStringStubBuilder thisFlat(this); + thisFlat.FlattenString(glue, string, &flattenFastPath); + Bind(&flattenFastPath); + StringInfoGateRef stringInfoGate(&thisFlat); + GateRef dataUtf8 = GetNormalStringData(stringInfoGate); c = ZExtInt8ToInt32(Load(VariableType::INT8(), dataUtf8)); Label isDigitZero(env); Label notDigitZero(env); @@ -1139,7 +1340,7 @@ GateRef StubBuilder::StringToElementIndex(GateRef glue, GateRef string) Branch(Int32Equal(len, Int32(1)), &lengthIsOne, &exit); Bind(&lengthIsOne); { - result = Int32(0); + result = Int64(0); Jump(&exit); } } @@ -1147,7 +1348,7 @@ GateRef StubBuilder::StringToElementIndex(GateRef glue, GateRef string) { Label isDigit(env); DEFVARIABLE(i, VariableType::INT32(), Int32(1)); - DEFVARIABLE(n, VariableType::INT32(), Int32Sub(*c, Int32('0'))); + DEFVARIABLE(n, VariableType::INT64(), Int64Sub(ZExtInt32ToInt64(*c), Int64('0'))); Branch(IsDigit(*c), &isDigit, &exit); Label loopHead(env); Label loopEnd(env); @@ -1163,7 +1364,7 @@ GateRef StubBuilder::StringToElementIndex(GateRef glue, GateRef string) Bind(&isDigit2); { // 10 means the base of digit is 10. - n = Int32Add(Int32Mul(*n, Int32(10)), Int32Sub(*c, Int32('0'))); + n = Int64Add(Int64Mul(*n, Int64(10)), Int64Sub(ZExtInt32ToInt64(*c), Int64('0'))); i = Int32Add(*i, Int32(1)); Branch(Int32UnsignedLessThan(*i, len), &loopEnd, &afterLoop); } @@ -1175,7 +1376,7 @@ GateRef StubBuilder::StringToElementIndex(GateRef glue, GateRef string) Bind(&afterLoop); { Label lessThanMaxIndex(env); - Branch(Int32UnsignedLessThan(*n, Int32(JSObject::MAX_ELEMENT_INDEX)), + Branch(Int64LessThan(*n, Int64(JSObject::MAX_ELEMENT_INDEX)), &lessThanMaxIndex, &exit); Bind(&lessThanMaxIndex); { @@ -1201,11 +1402,11 @@ GateRef StubBuilder::TryToElementsIndex(GateRef glue, GateRef key) Label isKeyInt(env); Label notKeyInt(env); - DEFVARIABLE(resultKey, VariableType::INT32(), Int32(-1)); + DEFVARIABLE(resultKey, VariableType::INT64(), Int64(-1)); Branch(TaggedIsInt(key), &isKeyInt, ¬KeyInt); Bind(&isKeyInt); { - resultKey = GetInt32OfTInt(key); + resultKey = GetInt64OfTInt(key); Jump(&exit); } Bind(¬KeyInt); @@ -1230,7 +1431,7 @@ GateRef StubBuilder::TryToElementsIndex(GateRef glue, GateRef key) Branch(DoubleEqual(number, ChangeInt32ToFloat64(integer)), &isEqual, &exit); Bind(&isEqual); { - resultKey = integer; + resultKey = SExtInt32ToInt64(integer); Jump(&exit); } } @@ -1275,6 +1476,7 @@ GateRef StubBuilder::LoadFromField(GateRef receiver, GateRef handlerInfo) Label exit(env); Label handlerInfoIsInlinedProps(env); Label handlerInfoNotInlinedProps(env); + Label handlerPost(env); DEFVARIABLE(result, VariableType::JS_ANY(), Undefined()); GateRef index = HandlerBaseGetOffset(handlerInfo); Branch(HandlerBaseIsInlinedProperty(handlerInfo), &handlerInfoIsInlinedProps, &handlerInfoNotInlinedProps); @@ -1282,12 +1484,34 @@ GateRef StubBuilder::LoadFromField(GateRef receiver, GateRef handlerInfo) { result = Load(VariableType::JS_ANY(), receiver, PtrMul(ZExtInt32ToPtr(index), IntPtr(JSTaggedValue::TaggedTypeSize()))); - Jump(&exit); + Jump(&handlerPost); } Bind(&handlerInfoNotInlinedProps); { result = GetValueFromTaggedArray(GetPropertiesArray(receiver), index); - Jump(&exit); + Jump(&handlerPost); + } + Bind(&handlerPost); + { + Label nonDoubleToTagged(env); + Label doubleToTagged(env); + GateRef rep = HandlerBaseGetRep(handlerInfo); + Branch(IsDoubleRepInPropAttr(rep), &doubleToTagged, &nonDoubleToTagged); + Bind(&doubleToTagged); + { + result = TaggedPtrToTaggedDoublePtr(*result); + Jump(&exit); + } + Bind(&nonDoubleToTagged); + { + Label intToTagged(env); + Branch(IsIntRepInPropAttr(rep), &intToTagged, &exit); + Bind(&intToTagged); + { + result = TaggedPtrToTaggedIntPtr(*result); + Jump(&exit); + } + } } Bind(&exit); auto ret = *result; @@ -1361,7 +1585,8 @@ GateRef StubBuilder::CheckPolyHClass(GateRef cachedValue, GateRef hclass) return ret; } -GateRef StubBuilder::LoadICWithHandler(GateRef glue, GateRef receiver, GateRef argHolder, GateRef argHandler) +GateRef StubBuilder::LoadICWithHandler( + GateRef glue, GateRef receiver, GateRef argHolder, GateRef argHandler, ProfileOperation callback) { auto env = GetEnvironment(); Label entry(env); @@ -1373,6 +1598,10 @@ GateRef StubBuilder::LoadICWithHandler(GateRef glue, GateRef receiver, GateRef a Label handlerInfoNotField(env); Label handlerInfoIsNonExist(env); Label handlerInfoNotNonExist(env); + Label handlerInfoIsString(env); + Label handlerInfoNotString(env); + Label handlerInfoIsStringLength(env); + Label handlerInfoNotStringLength(env); Label handlerIsPrototypeHandler(env); Label handlerNotPrototypeHandler(env); Label cellHasChanged(env); @@ -1397,13 +1626,37 @@ GateRef StubBuilder::LoadICWithHandler(GateRef glue, GateRef receiver, GateRef a } Bind(&handlerInfoNotField); { - Branch(IsNonExist(handlerInfo), &handlerInfoIsNonExist, &handlerInfoNotNonExist); - Bind(&handlerInfoIsNonExist); - Jump(&exit); - Bind(&handlerInfoNotNonExist); - GateRef accessor = LoadFromField(*holder, handlerInfo); - result = CallGetterHelper(glue, receiver, *holder, accessor); - Jump(&exit); + Branch(IsStringElement(handlerInfo), &handlerInfoIsString, &handlerInfoNotString); + Bind(&handlerInfoIsString); + { + GateRef glueGlobalEnvOffset = IntPtr(JSThread::GlueData::GetGlueGlobalEnvOffset(env->Is32Bit())); + GateRef glueGlobalEnv = Load(VariableType::NATIVE_POINTER(), glue, glueGlobalEnvOffset); + auto stringProto = GetGlobalEnvValue(VariableType::JS_ANY(), + glueGlobalEnv, GlobalEnv::STRING_PROTOTYPE_INDEX); + result = LoadFromField(stringProto, handlerInfo); + Jump(&exit); + } + Bind(&handlerInfoNotString); + { + Branch(IsNonExist(handlerInfo), &handlerInfoIsNonExist, &handlerInfoNotNonExist); + Bind(&handlerInfoIsNonExist); + Jump(&exit); + Bind(&handlerInfoNotNonExist); + { + Branch(IsStringLength(handlerInfo), &handlerInfoIsStringLength, &handlerInfoNotStringLength); + Bind(&handlerInfoNotStringLength); + { + GateRef accessor = LoadFromField(*holder, handlerInfo); + result = CallGetterHelper(glue, receiver, *holder, accessor, callback); + Jump(&exit); + } + Bind(&handlerInfoIsStringLength); + { + result = IntToTaggedPtr(GetLengthFromString(receiver)); + Jump(&exit); + } + } + } } } Bind(&handlerNotInt); @@ -1443,8 +1696,17 @@ GateRef StubBuilder::LoadElement(GateRef glue, GateRef receiver, GateRef key) Label indexNotLessZero(env); Label lengthLessIndex(env); Label lengthNotLessIndex(env); + Label greaterThanInt32Max(env); + Label notGreaterThanInt32Max(env); DEFVARIABLE(result, VariableType::JS_ANY(), Hole()); - GateRef index = TryToElementsIndex(glue, key); + GateRef index64 = TryToElementsIndex(glue, key); + Branch(Int64GreaterThanOrEqual(index64, Int64(INT32_MAX)), &greaterThanInt32Max, ¬GreaterThanInt32Max); + Bind(&greaterThanInt32Max); + { + Jump(&exit); + } + Bind(¬GreaterThanInt32Max); + GateRef index = TruncInt64ToInt32(index64); Branch(Int32LessThan(index, Int32(0)), &indexLessZero, &indexNotLessZero); Bind(&indexLessZero); { @@ -1466,6 +1728,54 @@ GateRef StubBuilder::LoadElement(GateRef glue, GateRef receiver, GateRef key) return ret; } +GateRef StubBuilder::LoadStringElement(GateRef glue, GateRef receiver, GateRef key) +{ + auto env = GetEnvironment(); + Label entry(env); + env->SubCfgEntry(&entry); + Label exit(env); + Label indexLessZero(env); + Label indexNotLessZero(env); + Label lengthLessIndex(env); + Label lengthNotLessIndex(env); + Label greaterThanInt32Max(env); + Label notGreaterThanInt32Max(env); + Label flattenFastPath(env); + + DEFVARIABLE(result, VariableType::JS_ANY(), Hole()); + GateRef index64 = TryToElementsIndex(glue, key); + Branch(Int64GreaterThanOrEqual(index64, Int64(INT32_MAX)), &greaterThanInt32Max, ¬GreaterThanInt32Max); + Bind(&greaterThanInt32Max); + { + Jump(&exit); + } + Bind(¬GreaterThanInt32Max); + GateRef index = TruncInt64ToInt32(index64); + Branch(Int32LessThan(index, Int32(0)), &indexLessZero, &indexNotLessZero); + Bind(&indexLessZero); + { + Jump(&exit); + } + Bind(&indexNotLessZero); + { + FlatStringStubBuilder thisFlat(this); + thisFlat.FlattenString(glue, receiver, &flattenFastPath); + Bind(&flattenFastPath); + Branch(Int32LessThanOrEqual(GetLengthFromString(receiver), index), &lengthLessIndex, &lengthNotLessIndex); + Bind(&lengthLessIndex); + Jump(&exit); + Bind(&lengthNotLessIndex); + BuiltinsStringStubBuilder stringBuilder(this); + StringInfoGateRef stringInfoGate(&thisFlat); + result = stringBuilder.CreateFromEcmaString(glue, index, stringInfoGate); + Jump(&exit); + } + Bind(&exit); + auto ret = *result; + env->SubCfgExit(); + return ret; +} + GateRef StubBuilder::ICStoreElement(GateRef glue, GateRef receiver, GateRef key, GateRef value, GateRef handler) { auto env = GetEnvironment(); @@ -1489,9 +1799,18 @@ GateRef StubBuilder::ICStoreElement(GateRef glue, GateRef receiver, GateRef key, Label cellHasNotChanged(env); Label loopHead(env); Label loopEnd(env); + Label greaterThanInt32Max(env); + Label notGreaterThanInt32Max(env); DEFVARIABLE(result, VariableType::JS_ANY(), Hole()); DEFVARIABLE(varHandler, VariableType::JS_ANY(), handler); - GateRef index = TryToElementsIndex(glue, key); + GateRef index64 = TryToElementsIndex(glue, key); + Branch(Int64GreaterThanOrEqual(index64, Int64(INT32_MAX)), &greaterThanInt32Max, ¬GreaterThanInt32Max); + Bind(&greaterThanInt32Max); + { + Jump(&exit); + } + Bind(¬GreaterThanInt32Max); + GateRef index = TruncInt64ToInt32(index64); Branch(Int32LessThan(index, Int32(0)), &indexLessZero, &indexNotLessZero); Bind(&indexLessZero); { @@ -1523,9 +1842,9 @@ GateRef StubBuilder::ICStoreElement(GateRef glue, GateRef receiver, GateRef key, GateRef oldLength = GetArrayLength(receiver); Branch(Int32GreaterThanOrEqual(index, oldLength), &indexGreaterLength, &handerInfoNotJSArray); Bind(&indexGreaterLength); - Store(VariableType::INT64(), glue, receiver, + Store(VariableType::INT32(), glue, receiver, IntPtr(panda::ecmascript::JSArray::LENGTH_OFFSET), - IntToTaggedInt(Int32Add(index, Int32(1)))); + Int32Add(index, Int32(1))); } Jump(&handerInfoNotJSArray); } @@ -1540,11 +1859,31 @@ GateRef StubBuilder::ICStoreElement(GateRef glue, GateRef receiver, GateRef key, RTSTUB_ID(TaggedArraySetValue), { receiver, value, elements, IntToTaggedInt(index), IntToTaggedInt(capacity) }); - Jump(&exit); + Label transition(env); + Branch(TaggedIsHole(*result), &exit, &transition); + Bind(&transition); + { + Label hole(env); + Label notHole(env); + DEFVARIABLE(kind, VariableType::INT32(), Int32(static_cast(ElementsKind::NONE))); + Branch(Int32GreaterThan(index, capacity), &hole, ¬Hole); + Bind(&hole); + { + kind = Int32(static_cast(ElementsKind::HOLE)); + Jump(¬Hole); + } + Bind(¬Hole); + { + TransitToElementsKind(glue, receiver, value, *kind); + Jump(&exit); + } + } } Bind(&storeElement); { SetValueToTaggedArray(VariableType::JS_ANY(), glue, elements, index, value); + TransitToElementsKind( + glue, receiver, value, Int32(static_cast(ElementsKind::NONE))); result = Undefined(); Jump(&exit); } @@ -1573,30 +1912,15 @@ GateRef StubBuilder::ICStoreElement(GateRef glue, GateRef receiver, GateRef key, GateRef StubBuilder::GetArrayLength(GateRef object) { - auto env = GetEnvironment(); - Label entry(env); - env->SubCfgEntry(&entry); - Label exit(env); - Label lengthIsInt(env); - Label lengthNotInt(env); - DEFVARIABLE(result, VariableType::INT32(), Int32(0)); GateRef lengthOffset = IntPtr(panda::ecmascript::JSArray::LENGTH_OFFSET); - GateRef length = Load(VariableType::INT64(), object, lengthOffset); - Branch(TaggedIsInt(length), &lengthIsInt, &lengthNotInt); - Bind(&lengthIsInt); - { - result = GetInt32OfTInt(length); - Jump(&exit); - } - Bind(&lengthNotInt); - { - result = ChangeFloat64ToInt32(GetDoubleOfTDouble(length)); - Jump(&exit); - } - Bind(&exit); - auto ret = *result; - env->SubCfgExit(); - return ret; + GateRef result = Load(VariableType::INT32(), object, lengthOffset); + return result; +} + +void StubBuilder::SetArrayLength(GateRef glue, GateRef object, GateRef len) +{ + GateRef lengthOffset = IntPtr(panda::ecmascript::JSArray::LENGTH_OFFSET); + Store(VariableType::INT32(), glue, object, lengthOffset, len); } GateRef StubBuilder::StoreICWithHandler(GateRef glue, GateRef receiver, GateRef argHolder, @@ -1640,13 +1964,13 @@ GateRef StubBuilder::StoreICWithHandler(GateRef glue, GateRef receiver, GateRef Branch(IsField(handlerInfo), &handlerInfoIsField, &handlerInfoNotField); Bind(&handlerInfoIsField); { - StoreField(glue, receiver, value, handlerInfo, callback); + result = StoreField(glue, receiver, value, handlerInfo, callback); Jump(&exit); } Bind(&handlerInfoNotField); { GateRef accessor = LoadFromField(*holder, handlerInfo); - result = CallSetterHelper(glue, receiver, accessor, value); + result = CallSetterHelper(glue, receiver, accessor, value, callback); Jump(&exit); } } @@ -1655,7 +1979,7 @@ GateRef StubBuilder::StoreICWithHandler(GateRef glue, GateRef receiver, GateRef Branch(TaggedIsTransitionHandler(*handler), &handlerIsTransitionHandler, &handlerNotTransitionHandler); Bind(&handlerIsTransitionHandler); { - StoreWithTransition(glue, receiver, value, *handler, callback); + result = StoreWithTransition(glue, receiver, value, *handler, callback); Jump(&exit); } Bind(&handlerNotTransitionHandler); @@ -1668,7 +1992,7 @@ GateRef StubBuilder::StoreICWithHandler(GateRef glue, GateRef receiver, GateRef Branch(GetHasChanged(cellValue), &cellHasChanged, &cellNotChanged); Bind(&cellNotChanged); { - StoreWithTransition(glue, receiver, value, *handler, callback, true); + result = StoreWithTransition(glue, receiver, value, *handler, callback, true); Jump(&exit); } } @@ -1711,13 +2035,13 @@ GateRef StubBuilder::StoreICWithHandler(GateRef glue, GateRef receiver, GateRef Branch(IsField(handlerInfo), &aotHandlerInfoIsField, &aotHandlerInfoNotField); Bind(&aotHandlerInfoIsField); { - StoreField(glue, receiver, value, handlerInfo, callback); + result = StoreField(glue, receiver, value, handlerInfo, callback); Jump(&exit); } Bind(&aotHandlerInfoNotField); { GateRef accessor = LoadFromField(*holder, handlerInfo); - result = CallSetterHelper(glue, receiver, accessor, value); + result = CallSetterHelper(glue, receiver, accessor, value, callback); Jump(&exit); } } @@ -1737,7 +2061,8 @@ GateRef StubBuilder::StoreICWithHandler(GateRef glue, GateRef receiver, GateRef return ret; } -void StubBuilder::StoreField(GateRef glue, GateRef receiver, GateRef value, GateRef handler, ProfileOperation callback) +GateRef StubBuilder::StoreField(GateRef glue, GateRef receiver, GateRef value, GateRef handler, + ProfileOperation callback) { auto env = GetEnvironment(); Label entry(env); @@ -1747,28 +2072,36 @@ void StubBuilder::StoreField(GateRef glue, GateRef receiver, GateRef value, Gate Label handlerIsInlinedProperty(env); Label handlerNotInlinedProperty(env); GateRef index = HandlerBaseGetOffset(handler); + GateRef rep = HandlerBaseGetRep(handler); + DEFVARIABLE(result, VariableType::JS_ANY(), Undefined()); + Label repChange(env); Branch(HandlerBaseIsInlinedProperty(handler), &handlerIsInlinedProperty, &handlerNotInlinedProperty); Bind(&handlerIsInlinedProperty); { - Store(VariableType::JS_ANY(), - glue, - receiver, - PtrMul(ZExtInt32ToPtr(index), IntPtr(JSTaggedValue::TaggedTypeSize())), - value); + GateRef toOffset = PtrMul(ZExtInt32ToPtr(index), IntPtr(JSTaggedValue::TaggedTypeSize())); + SetValueWithRep(glue, receiver, toOffset, value, rep, &repChange); Jump(&exit); } Bind(&handlerNotInlinedProperty); { GateRef array = GetPropertiesArray(receiver); - SetValueToTaggedArray(VariableType::JS_ANY(), glue, array, index, value); + SetValueToTaggedArrayWithRep(glue, array, index, value, rep, &repChange); + Jump(&exit); + } + Bind(&repChange); + { + result = Hole(); Jump(&exit); } + Bind(&exit); + auto ret = *result; env->SubCfgExit(); + return ret; } -void StubBuilder::StoreWithTransition(GateRef glue, GateRef receiver, GateRef value, GateRef handler, - ProfileOperation callback, bool withPrototype) +GateRef StubBuilder::StoreWithTransition(GateRef glue, GateRef receiver, GateRef value, GateRef handler, + ProfileOperation callback, bool withPrototype) { auto env = GetEnvironment(); Label entry(env); @@ -1779,6 +2112,7 @@ void StubBuilder::StoreWithTransition(GateRef glue, GateRef receiver, GateRef va Label handlerInfoNotInlinedProps(env); Label indexMoreCapacity(env); Label indexLessCapacity(env); + DEFVARIABLE(result, VariableType::JS_ANY(), Undefined()); GateRef newHClass; GateRef handlerInfo; if (withPrototype) { @@ -1794,6 +2128,7 @@ void StubBuilder::StoreWithTransition(GateRef glue, GateRef receiver, GateRef va Bind(&handlerInfoNotInlinedProps); { ProfilerStubBuilder(env).UpdatePropAttrIC(glue, receiver, value, handlerInfo, callback); + Label repChange(env); GateRef array = GetPropertiesArray(receiver); GateRef capacity = GetLengthOfTaggedArray(array); GateRef index = HandlerBaseGetOffset(handlerInfo); @@ -1808,21 +2143,27 @@ void StubBuilder::StoreWithTransition(GateRef glue, GateRef receiver, GateRef va } Bind(&indexLessCapacity); { - Store(VariableType::JS_ANY(), - glue, - PtrAdd(array, IntPtr(TaggedArray::DATA_OFFSET)), - PtrMul(ZExtInt32ToPtr(index), IntPtr(JSTaggedValue::TaggedTypeSize())), - value); + GateRef rep = HandlerBaseGetRep(handlerInfo); + GateRef base = PtrAdd(array, IntPtr(TaggedArray::DATA_OFFSET)); + GateRef toIndex = PtrMul(ZExtInt32ToPtr(index), IntPtr(JSTaggedValue::TaggedTypeSize())); + SetValueWithRep(glue, base, toIndex, value, rep, &repChange); + Jump(&exit); + } + Bind(&repChange); + { + result = Hole(); Jump(&exit); } } Bind(&handlerInfoIsInlinedProps); { - StoreField(glue, receiver, value, handlerInfo, callback); + result = StoreField(glue, receiver, value, handlerInfo, callback); Jump(&exit); } Bind(&exit); + auto ret = *result; env->SubCfgExit(); + return ret; } GateRef StubBuilder::StoreGlobal(GateRef glue, GateRef value, GateRef cell) @@ -1932,14 +2273,13 @@ inline void StubBuilder::UpdateValueInDict(GateRef glue, GateRef elements, GateR SetValueToTaggedArray(VariableType::JS_ANY(), glue, elements, valueIndex, value); } -GateRef StubBuilder::GetPropertyByIndex(GateRef glue, GateRef receiver, GateRef index) +GateRef StubBuilder::GetPropertyByIndex(GateRef glue, GateRef receiver, GateRef index, ProfileOperation callback) { auto env = GetEnvironment(); Label entry(env); env->SubCfgEntry(&entry); DEFVARIABLE(result, VariableType::JS_ANY(), Hole()); DEFVARIABLE(holder, VariableType::JS_ANY(), receiver); - DEFVARIABLE(proto, VariableType::JS_ANY(), Hole()); Label exit(env); Label loopHead(env); Label loopEnd(env); @@ -1958,13 +2298,12 @@ GateRef StubBuilder::GetPropertyByIndex(GateRef glue, GateRef receiver, GateRef // TypeArray Label isFastTypeArray(env); Label notFastTypeArray(env); + Label notTypedArrayProto(env); + Branch(Int32Equal(jsType, Int32(static_cast(JSType::JS_TYPED_ARRAY))), &exit, ¬TypedArrayProto); + Bind(¬TypedArrayProto); Branch(IsFastTypeArray(jsType), &isFastTypeArray, ¬FastTypeArray); Bind(&isFastTypeArray); { - proto = GetPrototypeFromHClass(LoadHClass(receiver)); - Label notOnProtoChain(env); - Branch(Int64NotEqual(*proto, *holder), &exit, ¬OnProtoChain); - Bind(¬OnProtoChain); TypedArrayStubBuilder typedArrayStubBuilder(this); result = typedArrayStubBuilder.FastGetPropertyByIndex(glue, *holder, index, jsType); Jump(&exit); @@ -2035,7 +2374,7 @@ GateRef StubBuilder::GetPropertyByIndex(GateRef glue, GateRef receiver, GateRef Branch(IsAccessor(attr), &isAccessor, ¬Accessor); Bind(&isAccessor); { - result = CallGetterHelper(glue, receiver, *holder, value); + result = CallGetterHelper(glue, receiver, *holder, value, callback); Jump(&exit); } Bind(¬Accessor); @@ -2067,7 +2406,7 @@ GateRef StubBuilder::GetPropertyByIndex(GateRef glue, GateRef receiver, GateRef return ret; } -GateRef StubBuilder::GetPropertyByValue(GateRef glue, GateRef receiver, GateRef keyValue) +GateRef StubBuilder::GetPropertyByValue(GateRef glue, GateRef receiver, GateRef keyValue, ProfileOperation callback) { auto env = GetEnvironment(); Label entry(env); @@ -2092,13 +2431,22 @@ GateRef StubBuilder::GetPropertyByValue(GateRef glue, GateRef receiver, GateRef } Bind(&isNumberOrStringSymbol); { - GateRef index = TryToElementsIndex(glue, *key); + GateRef index64 = TryToElementsIndex(glue, *key); Label validIndex(env); Label notValidIndex(env); + Label greaterThanInt32Max(env); + Label notGreaterThanInt32Max(env); + Branch(Int64GreaterThanOrEqual(index64, Int64(INT32_MAX)), &greaterThanInt32Max, ¬GreaterThanInt32Max); + Bind(&greaterThanInt32Max); + { + Jump(&exit); + } + Bind(¬GreaterThanInt32Max); + GateRef index = TruncInt64ToInt32(index64); Branch(Int32GreaterThanOrEqual(index, Int32(0)), &validIndex, ¬ValidIndex); Bind(&validIndex); { - result = GetPropertyByIndex(glue, receiver, index); + result = GetPropertyByIndex(glue, receiver, index, callback); Jump(&exit); } Bind(¬ValidIndex); @@ -2131,7 +2479,7 @@ GateRef StubBuilder::GetPropertyByValue(GateRef glue, GateRef receiver, GateRef } Bind(&getByName); { - result = GetPropertyByName(glue, receiver, *key); + result = GetPropertyByName(glue, receiver, *key, callback); Jump(&exit); } } @@ -2142,7 +2490,7 @@ GateRef StubBuilder::GetPropertyByValue(GateRef glue, GateRef receiver, GateRef return ret; } -GateRef StubBuilder::GetPropertyByName(GateRef glue, GateRef receiver, GateRef key) +GateRef StubBuilder::GetPropertyByName(GateRef glue, GateRef receiver, GateRef key, ProfileOperation callback) { auto env = GetEnvironment(); Label entry(env); @@ -2209,21 +2557,12 @@ GateRef StubBuilder::GetPropertyByName(GateRef glue, GateRef receiver, GateRef k GateRef propAttr = GetPropAttrFromLayoutInfo(layOutInfo, entryA); GateRef attr = GetInt32OfTInt(propAttr); GateRef value = JSObjectGetProperty(*holder, hclass, attr); - Label isPropertyBox(env); - Label notPropertyBox(env); - Branch(TaggedIsPropertyBox(value), &isPropertyBox, ¬PropertyBox); - Bind(&isPropertyBox); - { - result = GetValueFromPropertyBox(value); - Jump(&exit); - } - Bind(¬PropertyBox); Label isAccessor(env); Label notAccessor(env); Branch(IsAccessor(attr), &isAccessor, ¬Accessor); Bind(&isAccessor); { - result = CallGetterHelper(glue, receiver, *holder, value); + result = CallGetterHelper(glue, receiver, *holder, value, callback); Jump(&exit); } Bind(¬Accessor); @@ -2262,7 +2601,7 @@ GateRef StubBuilder::GetPropertyByName(GateRef glue, GateRef receiver, GateRef k Branch(IsAccessor(attr), &isAccessor1, ¬Accessor1); Bind(&isAccessor1); { - result = CallGetterHelper(glue, receiver, *holder, value); + result = CallGetterHelper(glue, receiver, *holder, value, callback); Jump(&exit); } Bind(¬Accessor1); @@ -2302,8 +2641,10 @@ void StubBuilder::CopyAllHClass(GateRef glue, GateRef dstHClass, GateRef srcHCla auto proto = GetPrototypeFromHClass(srcHClass); SetPrototypeToHClass(VariableType::JS_POINTER(), glue, dstHClass, proto); SetBitFieldToHClass(glue, dstHClass, GetBitFieldFromHClass(srcHClass)); + SetIsAllTaggedProp(glue, dstHClass, GetIsAllTaggedPropFromHClass(srcHClass)); SetNumberOfPropsToHClass(glue, dstHClass, GetNumberOfPropsFromHClass(srcHClass)); SetTransitionsToHClass(VariableType::INT64(), glue, dstHClass, Undefined()); + SetParentToHClass(VariableType::INT64(), glue, dstHClass, Undefined()); SetProtoChangeDetailsToHClass(VariableType::INT64(), glue, dstHClass, Null()); SetEnumCacheToHClass(VariableType::INT64(), glue, dstHClass, Null()); SetLayoutToHClass(VariableType::JS_POINTER(), glue, dstHClass, GetLayoutFromHClass(srcHClass)); @@ -2311,6 +2652,59 @@ void StubBuilder::CopyAllHClass(GateRef glue, GateRef dstHClass, GateRef srcHCla return; } +void StubBuilder::TransitionForRepChange(GateRef glue, GateRef receiver, GateRef key, GateRef attr) +{ + auto env = GetEnvironment(); + Label subEntry(env); + env->SubCfgEntry(&subEntry); + GateRef hclass = LoadHClass(receiver); + GateRef type = GetObjectType(hclass); + GateRef size = Int32Mul(GetInlinedPropsStartFromHClass(hclass), + Int32(JSTaggedValue::TaggedTypeSize())); + GateRef inlineProps = GetInlinedPropertiesFromHClass(hclass); + GateRef newJshclass = CallRuntime(glue, RTSTUB_ID(NewEcmaHClass), + { IntToTaggedInt(size), IntToTaggedInt(type), + IntToTaggedInt(inlineProps) }); + CopyAllHClass(glue, newJshclass, hclass); + CallRuntime(glue, RTSTUB_ID(CopyAndUpdateObjLayout), + { hclass, newJshclass, key, IntToTaggedInt(attr) }); +#if ECMASCRIPT_ENABLE_IC + NotifyHClassChanged(glue, hclass, newJshclass); +#endif + StoreHClass(glue, receiver, newJshclass); + env->SubCfgExit(); +} + +void StubBuilder::TransitToElementsKind(GateRef glue, GateRef receiver, GateRef value, GateRef kind) +{ + auto env = GetEnvironment(); + Label subEntry(env); + env->SubCfgEntry(&subEntry); + Label exit(env); + + GateRef hclass = LoadHClass(receiver); + GateRef elementsKind = GetElementsKindFromHClass(hclass); + + Label isNoneDefault(env); + Branch(Int32Equal(elementsKind, Int32(static_cast(ElementsKind::GENERIC))), &exit, &isNoneDefault); + Bind(&isNoneDefault); + { + GateRef newKind = TaggedToElementKind(value); + newKind = Int32Or(newKind, kind); + newKind = Int32Or(newKind, elementsKind); + Label change(env); + Branch(Int32Equal(elementsKind, newKind), &exit, &change); + Bind(&change); + { + CallRuntime(glue, RTSTUB_ID(UpdateHClassForElementsKind), { receiver, newKind }); + Jump(&exit); + } + } + + Bind(&exit); + env->SubCfgExit(); +} + GateRef StubBuilder::FindTransitions(GateRef glue, GateRef receiver, GateRef hclass, GateRef key, GateRef metaData) { auto env = GetEnvironment(); @@ -2429,6 +2823,9 @@ GateRef StubBuilder::SetPropertyByIndex(GateRef glue, GateRef receiver, GateRef Label isFastTypeArray(env); Label notFastTypeArray(env); Label checkIsOnPrototypeChain(env); + Label notTypedArrayProto(env); + Branch(Int32Equal(jsType, Int32(static_cast(JSType::JS_TYPED_ARRAY))), &exit, ¬TypedArrayProto); + Bind(¬TypedArrayProto); Branch(IsFastTypeArray(jsType), &isFastTypeArray, ¬FastTypeArray); Bind(&isFastTypeArray); { @@ -2483,6 +2880,8 @@ GateRef StubBuilder::SetPropertyByIndex(GateRef glue, GateRef receiver, GateRef { GateRef newElements = CallRuntime(glue, RTSTUB_ID(CheckAndCopyArray), {*holder}); SetValueToTaggedArray(VariableType::JS_ANY(), glue, newElements, index, value); + TransitToElementsKind( + glue, receiver, value, Int32(static_cast(ElementsKind::NONE))); returnValue = Undefined(); Jump(&exit); } @@ -2493,6 +2892,8 @@ GateRef StubBuilder::SetPropertyByIndex(GateRef glue, GateRef receiver, GateRef Bind(&setElementsArray); { SetValueToTaggedArray(VariableType::JS_ANY(), glue, elements, index, value); + TransitToElementsKind( + glue, receiver, value, Int32(static_cast(ElementsKind::NONE))); returnValue = Undefined(); Jump(&exit); } @@ -2657,7 +3058,7 @@ GateRef StubBuilder::SetPropertyByName(GateRef glue, GateRef receiver, GateRef k Branch(ShouldCallSetter(receiver, *holder, accessor, attr), &shouldCall, ¬Accessor); Bind(&shouldCall); { - result = CallSetterHelper(glue, receiver, accessor, value); + result = CallSetterHelper(glue, receiver, accessor, value, callback); Jump(&exit); } } @@ -2715,9 +3116,9 @@ GateRef StubBuilder::SetPropertyByName(GateRef glue, GateRef receiver, GateRef k { // JSObject::Cast(holder)->SetProperty(thread, hclass, attr, value) // return JSTaggedValue::Undefined() - JSObjectSetProperty(glue, *holder, hclass, attr, value); + JSObjectSetProperty(glue, *holder, hclass, attr, key, value); ProfilerStubBuilder(env).UpdatePropAttrWithValue( - glue, *holder, layOutInfo, attr, entry, value, callback); + glue, layOutInfo, attr, entry, value, callback); result = Undefined(); Jump(&exit); } @@ -2753,7 +3154,7 @@ GateRef StubBuilder::SetPropertyByName(GateRef glue, GateRef receiver, GateRef k Branch(ShouldCallSetter(receiver, *holder, accessor1, attr1), &shouldCall1, ¬Accessor1); Bind(&shouldCall1); { - result = CallSetterHelper(glue, receiver, accessor1, value); + result = CallSetterHelper(glue, receiver, accessor1, value, callback); Jump(&exit); } } @@ -2814,9 +3215,9 @@ GateRef StubBuilder::SetPropertyByName(GateRef glue, GateRef receiver, GateRef k GateRef receiverLayoutInfo = GetLayoutFromHClass(receiverHClass); GateRef holePropAttr = GetPropAttrFromLayoutInfo(receiverLayoutInfo, *receiverHoleEntry); GateRef holeAttr = GetInt32OfTInt(holePropAttr); - JSObjectSetProperty(glue, receiver, receiverHClass, holeAttr, value); + JSObjectSetProperty(glue, receiver, receiverHClass, holeAttr, key, value); ProfilerStubBuilder(env).UpdatePropAttrWithValue( - glue, receiver, receiverLayoutInfo, holeAttr, *receiverHoleEntry, value, callback); + glue, receiverLayoutInfo, holeAttr, *receiverHoleEntry, value, callback); result = Undefined(); Jump(&exit); } @@ -2869,9 +3270,18 @@ GateRef StubBuilder::SetPropertyByValue(GateRef glue, GateRef receiver, GateRef } Bind(&isNumberOrStringSymbol); { - GateRef index = TryToElementsIndex(glue, *varKey); + GateRef index64 = TryToElementsIndex(glue, *varKey); Label validIndex(env); Label notValidIndex(env); + Label greaterThanInt32Max(env); + Label notGreaterThanInt32Max(env); + Branch(Int64GreaterThanOrEqual(index64, Int64(INT32_MAX)), &greaterThanInt32Max, ¬GreaterThanInt32Max); + Bind(&greaterThanInt32Max); + { + Jump(&exit); + } + Bind(¬GreaterThanInt32Max); + GateRef index = TruncInt64ToInt32(index64); Branch(Int32GreaterThanOrEqual(index, Int32(0)), &validIndex, ¬ValidIndex); Bind(&validIndex); { @@ -2889,11 +3299,12 @@ GateRef StubBuilder::SetPropertyByValue(GateRef glue, GateRef receiver, GateRef result = Hole(); Jump(&exit); } + Label isString(env); + Label notString(env); Bind(¬Number1); { - Label isString(env); Label notIntenalString(env); - Branch(TaggedIsString(*varKey), &isString, &setByName); + Branch(TaggedIsString(*varKey), &isString, ¬String); Bind(&isString); { Branch(IsInternalString(*varKey), &setByName, ¬IntenalString); @@ -2904,6 +3315,8 @@ GateRef StubBuilder::SetPropertyByValue(GateRef glue, GateRef receiver, GateRef } } } + Bind(¬String); + CheckDetectorName(glue, *varKey, &setByName, &exit); Bind(&setByName); { result = SetPropertyByName(glue, receiver, *varKey, value, useOwn, callback); @@ -2975,8 +3388,8 @@ GateRef StubBuilder::FastTypeOf(GateRef glue, GateRef obj) env->SubCfgEntry(&entry); Label exit(env); - GateRef gConstAddr = PtrAdd(glue, - IntPtr(JSThread::GlueData::GetGlobalConstOffset(env->Is32Bit()))); + GateRef gConstAddr = Load(VariableType::JS_ANY(), glue, + IntPtr(JSThread::GlueData::GetGlobalConstOffset(env_->Is32Bit()))); GateRef undefinedIndex = GetGlobalConstantString(ConstantIndex::UNDEFINED_STRING_INDEX); GateRef gConstUndefinedStr = Load(VariableType::JS_POINTER(), gConstAddr, undefinedIndex); DEFVARIABLE(result, VariableType::JS_POINTER(), gConstUndefinedStr); @@ -3108,7 +3521,8 @@ GateRef StubBuilder::FastTypeOf(GateRef glue, GateRef obj) return ret; } -GateRef StubBuilder::InstanceOf(GateRef glue, GateRef object, GateRef target, GateRef profileTypeInfo, GateRef slotId) +GateRef StubBuilder::InstanceOf( + GateRef glue, GateRef object, GateRef target, GateRef profileTypeInfo, GateRef slotId, ProfileOperation callback) { auto env = GetEnvironment(); Label entry(env); @@ -3158,7 +3572,7 @@ GateRef StubBuilder::InstanceOf(GateRef glue, GateRef object, GateRef target, Ga Branch(TaggedIsUndefined(instof), &instOfIsUndefined, &instOfNotUndefined); Bind(&instOfNotUndefined); { - TryFastHasInstance(glue, instof, target, object, &fastPath, &exit, &result); + TryFastHasInstance(glue, instof, target, object, &fastPath, &exit, &result, callback); } Bind(&instOfIsUndefined); { @@ -3186,7 +3600,7 @@ GateRef StubBuilder::InstanceOf(GateRef glue, GateRef object, GateRef target, Ga } void StubBuilder::TryFastHasInstance(GateRef glue, GateRef instof, GateRef target, GateRef object, Label *fastPath, - Label *exit, Variable *result) + Label *exit, Variable *result, ProfileOperation callback) { auto env = GetEnvironment(); @@ -3203,7 +3617,7 @@ void StubBuilder::TryFastHasInstance(GateRef glue, GateRef instof, GateRef targe Bind(&slowPath); { GateRef retValue = JSCallDispatch(glue, instof, Int32(1), 0, Circuit::NullGate(), - JSCallMode::CALL_SETTER, { target, object }); + JSCallMode::CALL_SETTER, { target, object }, callback); result->WriteVariable(FastToBoolean(retValue)); Jump(exit); } @@ -3262,7 +3676,7 @@ GateRef StubBuilder::GetMethod(GateRef glue, GateRef obj, GateRef key, GateRef p return ret; } -GateRef StubBuilder::FastGetPropertyByName(GateRef glue, GateRef obj, GateRef key) +GateRef StubBuilder::FastGetPropertyByName(GateRef glue, GateRef obj, GateRef key, ProfileOperation callback) { auto env = GetEnvironment(); Label entry(env); @@ -3276,7 +3690,7 @@ GateRef StubBuilder::FastGetPropertyByName(GateRef glue, GateRef obj, GateRef ke Branch(TaggedIsHeapObject(obj), &fastpath, &slowpath); Bind(&fastpath); { - result = GetPropertyByName(glue, obj, key); + result = GetPropertyByName(glue, obj, key, callback); Branch(TaggedIsHole(*result), &slowpath, &exit); } Bind(&slowpath); @@ -3291,7 +3705,7 @@ GateRef StubBuilder::FastGetPropertyByName(GateRef glue, GateRef obj, GateRef ke return ret; } -GateRef StubBuilder::FastGetPropertyByIndex(GateRef glue, GateRef obj, GateRef index) +GateRef StubBuilder::FastGetPropertyByIndex(GateRef glue, GateRef obj, GateRef index, ProfileOperation callback) { auto env = GetEnvironment(); Label entry(env); @@ -3304,7 +3718,7 @@ GateRef StubBuilder::FastGetPropertyByIndex(GateRef glue, GateRef obj, GateRef i Branch(TaggedIsHeapObject(obj), &fastPath, &slowPath); Bind(&fastPath); { - result = GetPropertyByIndex(glue, obj, index); + result = GetPropertyByIndex(glue, obj, index, callback); Label notHole(env); Branch(TaggedIsHole(*result), &slowPath, &exit); } @@ -3372,7 +3786,7 @@ GateRef StubBuilder::OrdinaryHasInstance(GateRef glue, GateRef target, GateRef o auto prototypeString = GetGlobalConstantValue( VariableType::JS_POINTER(), glue, ConstantIndex::PROTOTYPE_STRING_INDEX); - GateRef constructorPrototype = FastGetPropertyByName(glue, target, prototypeString); + GateRef constructorPrototype = FastGetPropertyByName(glue, target, prototypeString, ProfileOperation()); // 5. ReturnIfAbrupt(P). // no throw exception, so needn't return @@ -3637,6 +4051,117 @@ GateRef StubBuilder::SameValue(GateRef glue, GateRef left, GateRef right) return ret; } +GateRef StubBuilder::SameValueZero(GateRef glue, GateRef left, GateRef right) +{ + auto env = GetEnvironment(); + Label entry(env); + env->SubCfgEntry(&entry); + DEFVARIABLE(result, VariableType::BOOL(), False()); + Label exit(env); + DEFVARIABLE(doubleLeft, VariableType::FLOAT64(), Double(0.0)); + DEFVARIABLE(doubleRight, VariableType::FLOAT64(), Double(0.0)); + Label strictEqual(env); + Label stringEqualCheck(env); + Label stringCompare(env); + Label bigIntEqualCheck(env); + Label numberEqualCheck1(env); + + Branch(Equal(left, right), &strictEqual, &numberEqualCheck1); + Bind(&strictEqual); + { + result = True(); + Jump(&exit); + } + Bind(&numberEqualCheck1); + { + Label leftIsNumber(env); + Label leftIsNotNumber(env); + Branch(TaggedIsNumber(left), &leftIsNumber, &leftIsNotNumber); + Bind(&leftIsNumber); + { + Label rightIsNumber(env); + Branch(TaggedIsNumber(right), &rightIsNumber, &exit); + Bind(&rightIsNumber); + { + Label numberEqualCheck2(env); + Label leftIsInt(env); + Label leftNotInt(env); + Label getRight(env); + Branch(TaggedIsInt(left), &leftIsInt, &leftNotInt); + Bind(&leftIsInt); + { + doubleLeft = ChangeInt32ToFloat64(GetInt32OfTInt(left)); + Jump(&getRight); + } + Bind(&leftNotInt); + { + doubleLeft = GetDoubleOfTDouble(left); + Jump(&getRight); + } + Bind(&getRight); + { + Label rightIsInt(env); + Label rightNotInt(env); + Branch(TaggedIsInt(right), &rightIsInt, &rightNotInt); + Bind(&rightIsInt); + { + doubleRight = ChangeInt32ToFloat64(GetInt32OfTInt(right)); + Jump(&numberEqualCheck2); + } + Bind(&rightNotInt); + { + doubleRight = GetDoubleOfTDouble(right); + Jump(&numberEqualCheck2); + } + } + Bind(&numberEqualCheck2); + { + Label nanCheck(env); + Label doubleEqual(env); + Branch(DoubleEqual(*doubleLeft, *doubleRight), &doubleEqual, &nanCheck); + Bind(&doubleEqual); + { + result = True(); + Jump(&exit); + } + Bind(&nanCheck); + { + result = BoolAnd(DoubleIsNAN(*doubleLeft), DoubleIsNAN(*doubleRight)); + Jump(&exit); + } + } + } + } + Bind(&leftIsNotNumber); + Branch(TaggedIsNumber(right), &exit, &stringEqualCheck); + Bind(&stringEqualCheck); + Branch(BothAreString(left, right), &stringCompare, &bigIntEqualCheck); + Bind(&stringCompare); + { + result = FastStringEqual(glue, left, right); + Jump(&exit); + } + Bind(&bigIntEqualCheck); + { + Label leftIsBigInt(env); + Label leftIsNotBigInt(env); + Branch(TaggedIsBigInt(left), &leftIsBigInt, &exit); + Bind(&leftIsBigInt); + { + Label rightIsBigInt(env); + Branch(TaggedIsBigInt(right), &rightIsBigInt, &exit); + Bind(&rightIsBigInt); + result = CallNGCRuntime(glue, RTSTUB_ID(BigIntSameValueZero), { left, right }); + Jump(&exit); + } + } + } + Bind(&exit); + auto ret = *result; + env->SubCfgExit(); + return ret; +} + GateRef StubBuilder::FastStringEqual(GateRef glue, GateRef left, GateRef right) { auto env = GetEnvironment(); @@ -3644,16 +4169,35 @@ GateRef StubBuilder::FastStringEqual(GateRef glue, GateRef left, GateRef right) env->SubCfgEntry(&entry); DEFVARIABLE(result, VariableType::BOOL(), False()); Label exit(env); - Label lengthCompare(env); Label hashcodeCompare(env); Label contentsCompare(env); - - Branch(Int32Equal(ZExtInt1ToInt32(IsUtf16String(left)), ZExtInt1ToInt32(IsUtf16String(right))), - &lengthCompare, &exit); - - Bind(&lengthCompare); - Branch(Int32Equal(GetLengthFromString(left), GetLengthFromString(right)), &hashcodeCompare, - &exit); + Label lenEqualOneCheck(env); + Label lenIsOne(env); + Branch(Int32Equal(GetLengthFromString(left), GetLengthFromString(right)), &lenEqualOneCheck, &exit); + Bind(&lenEqualOneCheck); + Branch(Int32Equal(GetLengthFromString(left), Int32(1)), &lenIsOne, &hashcodeCompare); + Bind(&lenIsOne); + { + Label leftFlattenFastPath(env); + FlatStringStubBuilder leftFlat(this); + leftFlat.FlattenString(glue, left, &leftFlattenFastPath); + Bind(&leftFlattenFastPath); + { + Label rightFlattenFastPath(env); + FlatStringStubBuilder rightFlat(this); + rightFlat.FlattenString(glue, right, &rightFlattenFastPath); + Bind(&rightFlattenFastPath); + { + BuiltinsStringStubBuilder stringBuilder(this); + StringInfoGateRef leftStrInfoGate(&leftFlat); + StringInfoGateRef rightStrInfoGate(&rightFlat); + GateRef leftStrToInt = stringBuilder.StringAt(leftStrInfoGate, Int32(0)); + GateRef rightStrToInt = stringBuilder.StringAt(rightStrInfoGate, Int32(0)); + result = Equal(leftStrToInt, rightStrToInt); + Jump(&exit); + } + } + } Bind(&hashcodeCompare); Label leftNotNeg(env); @@ -3793,7 +4337,7 @@ GateRef StubBuilder::FastStrictEqual(GateRef glue, GateRef left, GateRef right, return ret; } -GateRef StubBuilder::FastEqual(GateRef left, GateRef right, ProfileOperation callback) +GateRef StubBuilder::FastEqual(GateRef glue, GateRef left, GateRef right, ProfileOperation callback) { auto env = GetEnvironment(); Label entry(env); @@ -3917,6 +4461,23 @@ GateRef StubBuilder::FastEqual(GateRef left, GateRef right, ProfileOperation cal } Bind(&leftNotBoolOrRightNotSpecial); { + Label bothString(env); + Label eitherNotString(env); + Branch(BothAreString(left, right), &bothString, &eitherNotString); + Bind(&bothString); + { + callback.ProfileOpType(Int32(PGOSampleType::StringType())); + Label stringEqual(env); + Label stringNotEqual(env); + Branch(FastStringEqual(glue, left, right), &stringEqual, &stringNotEqual); + Bind(&stringEqual); + result = TaggedTrue(); + Jump(&exit); + Bind(&stringNotEqual); + result = TaggedFalse(); + Jump(&exit); + } + Bind(&eitherNotString); callback.ProfileOpType(Int32(PGOSampleType::AnyType())); Jump(&exit); } @@ -4076,14 +4637,14 @@ GateRef StubBuilder::FastDiv(GateRef left, GateRef right, ProfileOperation callb Bind(&rightIsInt); { GateRef type = Int32(PGOSampleType::IntType()); - COMBINE_TYPE_CALL_BACK(curType, type) + COMBINE_TYPE_CALL_BACK(curType, type); doubleRight = ChangeInt32ToFloat64(GetInt32OfTInt(right)); Jump(&leftIsDoubleAndRightIsDouble); } Bind(&rightNotInt); { GateRef type = Int32(PGOSampleType::DoubleType()); - COMBINE_TYPE_CALL_BACK(curType, type) + COMBINE_TYPE_CALL_BACK(curType, type); doubleRight = GetDoubleOfTDouble(right); Jump(&leftIsDoubleAndRightIsDouble); } @@ -4140,7 +4701,8 @@ GateRef StubBuilder::FastDiv(GateRef left, GateRef right, ProfileOperation callb return ret; } -GateRef StubBuilder::FastBinaryOp(GateRef left, GateRef right, +template +GateRef StubBuilder::FastBinaryOp(GateRef glue, GateRef left, GateRef right, const BinaryOperation& intOp, const BinaryOperation& floatOp, ProfileOperation callback) @@ -4160,8 +4722,12 @@ GateRef StubBuilder::FastBinaryOp(GateRef left, GateRef right, Label leftIsIntRightIsDouble(env); Label rightIsInt(env); Label rightIsDouble(env); - - Branch(TaggedIsNumber(left), &leftIsNumber, &exit); + Label checkString(env); + if (Op == OpCode::ADD) { + Branch(TaggedIsNumber(left), &leftIsNumber, &checkString); + } else { + Branch(TaggedIsNumber(left), &leftIsNumber, &exit); + } Bind(&leftIsNumber); { Branch(TaggedIsNumber(right), &rightIsNumber, &exit); @@ -4201,6 +4767,24 @@ GateRef StubBuilder::FastBinaryOp(GateRef left, GateRef right, } } } + if (Op == OpCode::ADD) { + Bind(&checkString); + { + Label stringAdd(env); + Label hasPendingException(env); + GateRef bothString = BoolAnd(TaggedIsString(left), TaggedIsString(right)); + Branch(bothString, &stringAdd, &exit); + Bind(&stringAdd); + { + BuiltinsStringStubBuilder builtinsStringStubBuilder(this); + result = builtinsStringStubBuilder.StringConcat(glue, left, right); + Branch(HasPendingException(glue), &hasPendingException, &exit); + Bind(&hasPendingException); + result = Exception(); + Jump(&exit); + } + } + } Bind(&doIntOp); { result = intOp(env, left, right); @@ -4218,7 +4802,7 @@ GateRef StubBuilder::FastBinaryOp(GateRef left, GateRef right, } template -GateRef StubBuilder::FastAddSubAndMul(GateRef left, GateRef right, ProfileOperation callback) +GateRef StubBuilder::FastAddSubAndMul(GateRef glue, GateRef left, GateRef right, ProfileOperation callback) { auto intOperation = [=](Environment *env, GateRef left, GateRef right) { Label entry(env); @@ -4227,10 +4811,9 @@ GateRef StubBuilder::FastAddSubAndMul(GateRef left, GateRef right, ProfileOperat Label exit(env); Label overflow(env); Label notOverflow(env); - auto res = BinaryOp(GetInt64OfTInt(left), GetInt64OfTInt(right)); - auto condition1 = Int64GreaterThan(res, Int64(INT32_MAX)); - auto condition2 = Int64LessThan(res, Int64(INT32_MIN)); - Branch(BoolOr(condition1, condition2), &overflow, ¬Overflow); + auto res = BinaryOpWithOverflow(GetInt32OfTInt(left), GetInt32OfTInt(right)); + GateRef condition = env->GetBuilder()->ExtractValue(MachineType::I1, res, Int32(1)); + Branch(condition, &overflow, ¬Overflow); Bind(&overflow); { auto doubleLeft = ChangeInt32ToFloat64(GetInt32OfTInt(left)); @@ -4242,11 +4825,12 @@ GateRef StubBuilder::FastAddSubAndMul(GateRef left, GateRef right, ProfileOperat } Bind(¬Overflow); { + res = env->GetBuilder()->ExtractValue(MachineType::I32, res, Int32(0)); if (Op == OpCode::MUL) { Label resultIsZero(env); Label returnNegativeZero(env); Label returnResult(env); - Branch(Int64Equal(res, Int64(0)), &resultIsZero, &returnResult); + Branch(Int32Equal(res, Int32(0)), &resultIsZero, &returnResult); Bind(&resultIsZero); GateRef leftNegative = Int32LessThan(GetInt32OfTInt(left), Int32(0)); GateRef rightNegative = Int32LessThan(GetInt32OfTInt(right), Int32(0)); @@ -4256,11 +4840,11 @@ GateRef StubBuilder::FastAddSubAndMul(GateRef left, GateRef right, ProfileOperat callback.ProfileOpType(Int32(PGOSampleType::DoubleType())); Jump(&exit); Bind(&returnResult); - result = IntToTaggedPtr(TruncInt64ToInt32(res)); + result = IntToTaggedPtr(res); callback.ProfileOpType(Int32(PGOSampleType::IntType())); Jump(&exit); } else { - result = IntToTaggedPtr(TruncInt64ToInt32(res)); + result = IntToTaggedPtr(res); callback.ProfileOpType(Int32(PGOSampleType::IntType())); Jump(&exit); } @@ -4274,7 +4858,7 @@ GateRef StubBuilder::FastAddSubAndMul(GateRef left, GateRef right, ProfileOperat auto res = BinaryOp(left, right); return DoubleToTaggedDoublePtr(res); }; - return FastBinaryOp(left, right, intOperation, floatOperation, callback); + return FastBinaryOp(glue, left, right, intOperation, floatOperation, callback); } GateRef StubBuilder::FastIntDiv(GateRef left, GateRef right, Label *bailout, ProfileOperation callback) @@ -4312,19 +4896,19 @@ GateRef StubBuilder::FastIntDiv(GateRef left, GateRef right, Label *bailout, Pro return ret; } -GateRef StubBuilder::FastAdd(GateRef left, GateRef right, ProfileOperation callback) +GateRef StubBuilder::FastAdd(GateRef glue, GateRef left, GateRef right, ProfileOperation callback) { - return FastAddSubAndMul(left, right, callback); + return FastAddSubAndMul(glue, left, right, callback); } -GateRef StubBuilder::FastSub(GateRef left, GateRef right, ProfileOperation callback) +GateRef StubBuilder::FastSub(GateRef glue, GateRef left, GateRef right, ProfileOperation callback) { - return FastAddSubAndMul(left, right, callback); + return FastAddSubAndMul(glue, left, right, callback); } -GateRef StubBuilder::FastMul(GateRef left, GateRef right, ProfileOperation callback) +GateRef StubBuilder::FastMul(GateRef glue, GateRef left, GateRef right, ProfileOperation callback) { - return FastAddSubAndMul(left, right, callback); + return FastAddSubAndMul(glue, left, right, callback); } GateRef StubBuilder::FastMod(GateRef glue, GateRef left, GateRef right, ProfileOperation callback) @@ -4407,14 +4991,14 @@ GateRef StubBuilder::FastMod(GateRef glue, GateRef left, GateRef right, ProfileO Bind(&rightIsInt1); { GateRef type = Int32(PGOSampleType::IntType()); - COMBINE_TYPE_CALL_BACK(curType, type) + COMBINE_TYPE_CALL_BACK(curType, type); doubleRight = ChangeInt32ToFloat64(GetInt32OfTInt(right)); Jump(&leftIsDoubleAndRightIsDouble); } Bind(&rightNotInt1); { GateRef type = Int32(PGOSampleType::DoubleType()); - COMBINE_TYPE_CALL_BACK(curType, type) + COMBINE_TYPE_CALL_BACK(curType, type); doubleRight = GetDoubleOfTDouble(right); Jump(&leftIsDoubleAndRightIsDouble); } @@ -4456,7 +5040,8 @@ GateRef StubBuilder::FastMod(GateRef glue, GateRef left, GateRef right, ProfileO Branch(DoubleIsINF(*doubleRight), &leftIsZeroOrRightIsInf, &rightNotInf); Bind(&rightNotInf); { - result = DoubleToTaggedDoublePtr(CallNGCRuntime(glue, RTSTUB_ID(FloatMod), { *doubleLeft, *doubleRight })); + result = DoubleToTaggedDoublePtr(CallNGCRuntime(glue, RTSTUB_ID(FloatMod), + { *doubleLeft, *doubleRight })); Jump(&exit); } } @@ -4474,7 +5059,7 @@ GateRef StubBuilder::FastMod(GateRef glue, GateRef left, GateRef right, ProfileO return ret; } -GateRef StubBuilder::GetGlobalOwnProperty(GateRef glue, GateRef receiver, GateRef key) +GateRef StubBuilder::GetGlobalOwnProperty(GateRef glue, GateRef receiver, GateRef key, ProfileOperation callback) { auto env = GetEnvironment(); Label entryLabel(env); @@ -4492,7 +5077,7 @@ GateRef StubBuilder::GetGlobalOwnProperty(GateRef glue, GateRef receiver, GateRe Branch(TaggedIsAccessor(*result), &callGetter, &exit); Bind(&callGetter); { - result = CallGetterHelper(glue, receiver, receiver, *result); + result = CallGetterHelper(glue, receiver, receiver, *result, callback); Jump(&exit); } } @@ -4502,6 +5087,11 @@ GateRef StubBuilder::GetGlobalOwnProperty(GateRef glue, GateRef receiver, GateRe return ret; } +GateRef StubBuilder::GetConstPoolFromFunction(GateRef jsFunc) +{ + return env_->GetBuilder()->GetConstPoolFromFunction(jsFunc); +} + GateRef StubBuilder::GetStringFromConstPool(GateRef glue, GateRef constpool, GateRef index) { GateRef module = Circuit::NullGate(); @@ -4509,9 +5099,8 @@ GateRef StubBuilder::GetStringFromConstPool(GateRef glue, GateRef constpool, Gat return env_->GetBuilder()->GetObjectFromConstPool(glue, hirGate, constpool, module, index, ConstPoolType::STRING); } -GateRef StubBuilder::GetMethodFromConstPool(GateRef glue, GateRef constpool, GateRef index) +GateRef StubBuilder::GetMethodFromConstPool(GateRef glue, GateRef constpool, GateRef module, GateRef index) { - GateRef module = Circuit::NullGate(); GateRef hirGate = Circuit::NullGate(); return env_->GetBuilder()->GetObjectFromConstPool(glue, hirGate, constpool, module, index, ConstPoolType::METHOD); } @@ -4530,121 +5119,6 @@ GateRef StubBuilder::GetObjectLiteralFromConstPool(GateRef glue, GateRef constpo ConstPoolType::OBJECT_LITERAL); } -// return elements -GateRef StubBuilder::BuildArgumentsListFastElements(GateRef glue, GateRef arrayObj) -{ - auto env = GetEnvironment(); - Label subentry(env); - env->SubCfgEntry(&subentry); - DEFVARIABLE(res, VariableType::JS_ANY(), Hole()); - Label exit(env); - Label hasStableElements(env); - Label targetIsStableJSArguments(env); - Label targetNotStableJSArguments(env); - Label targetIsInt(env); - Label hClassEqual(env); - Label targetIsStableJSArray(env); - Label targetNotStableJSArray(env); - - Branch(HasStableElements(glue, arrayObj), &hasStableElements, &exit); - Bind(&hasStableElements); - { - Branch(IsStableJSArguments(glue, arrayObj), &targetIsStableJSArguments, &targetNotStableJSArguments); - Bind(&targetIsStableJSArguments); - { - GateRef hClass = LoadHClass(arrayObj); - GateRef glueGlobalEnvOffset = IntPtr(JSThread::GlueData::GetGlueGlobalEnvOffset(env->Is32Bit())); - GateRef glueGlobalEnv = Load(VariableType::NATIVE_POINTER(), glue, glueGlobalEnvOffset); - GateRef argmentsClass = GetGlobalEnvValue(VariableType::JS_ANY(), glueGlobalEnv, - GlobalEnv::ARGUMENTS_CLASS); - Branch(Int32Equal(hClass, argmentsClass), &hClassEqual, &exit); - Bind(&hClassEqual); - { - GateRef PropertyInlinedPropsOffset = IntPtr(JSArguments::LENGTH_INLINE_PROPERTY_INDEX); - GateRef result = GetPropertyInlinedProps(arrayObj, hClass, PropertyInlinedPropsOffset); - Branch(TaggedIsInt(result), &targetIsInt, &exit); - Bind(&targetIsInt); - { - res = GetElementsArray(arrayObj); - Jump(&exit); - } - } - } - Bind(&targetNotStableJSArguments); - { - Branch(IsStableJSArray(glue, arrayObj), &targetIsStableJSArray, &targetNotStableJSArray); - Bind(&targetIsStableJSArray); - { - res = GetElementsArray(arrayObj); - Jump(&exit); - } - Bind(&targetNotStableJSArray); - { - FatalPrint(glue, { Int32(GET_MESSAGE_STRING_ID(ThisBranchIsUnreachable)) }); - Jump(&exit); - } - } - } - Bind(&exit); - auto ret = *res; - env->SubCfgExit(); - return ret; -} - -GateRef StubBuilder::MakeArgListWithHole(GateRef glue, GateRef argv, GateRef length) -{ - auto env = GetEnvironment(); - Label subentry(env); - env->SubCfgEntry(&subentry); - DEFVARIABLE(res, VariableType::INT32(), length); - DEFVARIABLE(i, VariableType::INT32(), Int32(0)); - Label exit(env); - - GateRef argsLength = GetLengthOfTaggedArray(argv); - - Label lengthGreaterThanArgsLength(env); - Label lengthLessThanArgsLength(env); - Branch(Int32GreaterThan(length, argsLength), &lengthGreaterThanArgsLength, &lengthLessThanArgsLength); - Bind(&lengthGreaterThanArgsLength); - { - res = argsLength; - Jump(&lengthLessThanArgsLength); - } - Bind(&lengthLessThanArgsLength); - { - Label loopHead(env); - Label loopEnd(env); - Label afterLoop(env); - Label targetIsHole(env); - Label targetNotHole(env); - Branch(Int32UnsignedLessThan(*i, *res), &loopHead, &afterLoop); - LoopBegin(&loopHead); - { - GateRef value = GetValueFromTaggedArray(argv, *i); - Branch(TaggedIsHole(value), &targetIsHole, &targetNotHole); - Bind(&targetIsHole); - { - SetValueToTaggedArray(VariableType::JS_ANY(), glue, argv, *i, Undefined()); - Jump(&targetNotHole); - } - Bind(&targetNotHole); - i = Int32Add(*i, Int32(1)); - Branch(Int32UnsignedLessThan(*i, *res), &loopEnd, &afterLoop); - } - Bind(&loopEnd); - LoopEnd(&loopHead); - Bind(&afterLoop); - { - res = length; - Jump(&exit); - } - } - Bind(&exit); - auto ret = *res; - env->SubCfgExit(); - return ret; -} - GateRef StubBuilder::JSAPIContainerGet(GateRef glue, GateRef receiver, GateRef index) { auto env = GetEnvironment(); @@ -4679,7 +5153,7 @@ GateRef StubBuilder::JSAPIContainerGet(GateRef glue, GateRef receiver, GateRef i return ret; } -GateRef StubBuilder::DoubleToInt(GateRef glue, GateRef x) +GateRef StubBuilder::DoubleToInt(GateRef glue, GateRef x, size_t typeBits) { auto env = GetEnvironment(); Label entry(env); @@ -4699,13 +5173,13 @@ GateRef StubBuilder::DoubleToInt(GateRef glue, GateRef x) GateRef exp = Int64And(xInt64, Int64(base::DOUBLE_EXPONENT_MASK)); exp = TruncInt64ToInt32(Int64LSR(exp, Int64(base::DOUBLE_SIGNIFICAND_SIZE))); exp = Int32Sub(exp, Int32(base::DOUBLE_EXPONENT_BIAS)); - GateRef bits = Int32(base::INT32_BITS - 1); + GateRef bits = Int32(typeBits - 1); // exp < 32 - 1 Branch(Int32LessThan(exp, bits), &exit, &overflow); } Bind(&overflow); { - result = CallNGCRuntime(glue, RTSTUB_ID(DoubleToInt), { x }); + result = CallNGCRuntime(glue, RTSTUB_ID(DoubleToInt), { x, IntPtr(typeBits) }); Jump(&exit); } Bind(&exit); @@ -4954,7 +5428,9 @@ GateRef StubBuilder::JSCallDispatch(GateRef glue, GateRef func, GateRef actualNu // 4. call nonNative Bind(&methodNotNative); - callback.ProfileCall(func); + if (mode != JSCallMode::CALL_GETTER && mode != JSCallMode::CALL_SETTER) { + callback.ProfileCall(func); + } Label funcIsClassConstructor(env); Label funcNotClassConstructor(env); Label methodNotAot(env); @@ -5464,6 +5940,7 @@ GateRef StubBuilder::TryStringOrSymbolToElementIndex(GateRef glue, GateRef key) Label greatThanZero(env); Label inRange(env); + Label flattenFastPath(env); auto len = GetLengthFromString(key); Branch(Int32Equal(len, Int32(0)), &exit, &greatThanZero); Bind(&greatThanZero); @@ -5471,10 +5948,14 @@ GateRef StubBuilder::TryStringOrSymbolToElementIndex(GateRef glue, GateRef key) Bind(&inRange); { Label isUtf8(env); + DEFVARIABLE(c, VariableType::INT32(), Int32(0)); Branch(IsUtf16String(key), &exit, &isUtf8); Bind(&isUtf8); - GateRef data = GetNormalStringData(FlattenString(glue, key)); - DEFVARIABLE(c, VariableType::INT32(), Int32(0)); + FlatStringStubBuilder thisFlat(this); + thisFlat.FlattenString(glue, key, &flattenFastPath); + Bind(&flattenFastPath); + StringInfoGateRef stringInfoGate(&thisFlat); + GateRef data = GetNormalStringData(stringInfoGate); c = ZExtInt8ToInt32(Load(VariableType::INT8(), data)); Label isDigitZero(env); Label notDigitZero(env); @@ -5689,38 +6170,7 @@ void StubBuilder::Assert(int messageId, int line, GateRef glue, GateRef conditio } } -GateRef StubBuilder::FlattenString(GateRef glue, GateRef str) -{ - auto env = GetEnvironment(); - Label entry(env); - env->SubCfgEntry(&entry); - Label exit(env); - DEFVARIABLE(result, VariableType::JS_POINTER(), str); - Label isTreeString(env); - Branch(IsTreeString(str), &isTreeString, &exit); - Bind(&isTreeString); - { - Label isFlat(env); - Label notFlat(env); - Branch(TreeStringIsFlat(str), &isFlat, ¬Flat); - Bind(&isFlat); - { - result = GetFirstFromTreeString(str); - Jump(&exit); - } - Bind(¬Flat); - { - result = CallRuntime(glue, RTSTUB_ID(SlowFlattenString), { str }); - Jump(&exit); - } - } - Bind(&exit); - auto ret = *result; - env->SubCfgExit(); - return ret; -} - -GateRef StubBuilder::GetNormalStringData(GateRef str) +GateRef StubBuilder::GetNormalStringData(const StringInfoGateRef &stringInfoGate) { auto env = GetEnvironment(); Label entry(env); @@ -5728,18 +6178,33 @@ GateRef StubBuilder::GetNormalStringData(GateRef str) Label exit(env); Label isConstantString(env); Label isLineString(env); - DEFVARIABLE(result, VariableType::JS_ANY(), Undefined()); - Branch(IsConstantString(str), &isConstantString, &isLineString); + Label isUtf8(env); + Label isUtf16(env); + DEFVARIABLE(result, VariableType::NATIVE_POINTER(), Undefined()); + Branch(IsConstantString(stringInfoGate.GetString()), &isConstantString, &isLineString); Bind(&isConstantString); { - GateRef address = PtrAdd(str, IntPtr(ConstantString::CONSTANT_DATA_OFFSET)); - result = Load(VariableType::JS_ANY(), address, IntPtr(0)); + GateRef address = PtrAdd(stringInfoGate.GetString(), IntPtr(ConstantString::CONSTANT_DATA_OFFSET)); + result = PtrAdd(Load(VariableType::NATIVE_POINTER(), address, IntPtr(0)), + ZExtInt32ToPtr(stringInfoGate.GetStartIndex())); Jump(&exit); } Bind(&isLineString); { - result = PtrAdd(str, IntPtr(LineEcmaString::DATA_OFFSET)); - Jump(&exit); + GateRef data = ChangeTaggedPointerToInt64( + PtrAdd(stringInfoGate.GetString(), IntPtr(LineEcmaString::DATA_OFFSET))); + Branch(IsUtf8String(stringInfoGate.GetString()), &isUtf8, &isUtf16); + Bind(&isUtf8); + { + result = PtrAdd(data, ZExtInt32ToPtr(stringInfoGate.GetStartIndex())); + Jump(&exit); + } + Bind(&isUtf16); + { + GateRef offset = PtrMul(ZExtInt32ToPtr(stringInfoGate.GetStartIndex()), IntPtr(sizeof(uint16_t))); + result = PtrAdd(data, offset); + Jump(&exit); + } } Bind(&exit); auto ret = *result; @@ -5747,35 +6212,6 @@ GateRef StubBuilder::GetNormalStringData(GateRef str) return ret; } -void StubBuilder::FlattenString(GateRef str, Variable *flatStr, Label *fastPath, Label *slowPath) -{ - auto env = GetEnvironment(); - Label notLineString(env); - Label exit(env); - DEFVARIABLE(result, VariableType::JS_POINTER(), str); - Branch(BoolOr(IsLineString(str), IsConstantString(str)), &exit, ¬LineString); - Bind(¬LineString); - { - Label isTreeString(env); - Branch(IsTreeString(str), &isTreeString, &exit); - Bind(&isTreeString); - { - Label isFlat(env); - Branch(TreeStringIsFlat(str), &isFlat, slowPath); - Bind(&isFlat); - { - result = GetFirstFromTreeString(str); - Jump(&exit); - } - } - } - Bind(&exit); - { - flatStr->WriteVariable(*result); - Jump(fastPath); - } -} - GateRef StubBuilder::ToNumber(GateRef glue, GateRef tagged) { auto env = GetEnvironment(); @@ -5784,7 +6220,6 @@ GateRef StubBuilder::ToNumber(GateRef glue, GateRef tagged) Label exit(env); Label isNumber(env); Label notNumber(env); - Label defaultLabel(env); DEFVARIABLE(result, VariableType::JS_ANY(), Hole()); Branch(TaggedIsNumber(tagged), &isNumber, ¬Number); Bind(&isNumber); @@ -5794,204 +6229,15 @@ GateRef StubBuilder::ToNumber(GateRef glue, GateRef tagged) } Bind(¬Number); { - Label returnNan(env); - Label notNan(env); - Label returnNumber1(env); - Label notNumber1(env); - Label returnNumber0(env); - auto isHole = TaggedIsHole(tagged); - auto isUndefined = TaggedIsUndefined(tagged); - Branch(BoolOr(isHole, isUndefined), &returnNan, ¬Nan); - Bind(&returnNan); - { - result = DoubleToTaggedDoublePtr(Double(base::NAN_VALUE)); - Jump(&exit); - } - Bind(¬Nan); - Branch(TaggedIsTrue(tagged), &returnNumber1, ¬Number1); - Bind(&returnNumber1); - { - result = Int64ToTaggedPtr(Int32(1)); - Jump(&exit); - } - Bind(¬Number1); - auto isFalse = TaggedIsFalse(tagged); - auto isNull = TaggedIsNull(tagged); - Branch(BoolOr(isFalse, isNull), &returnNumber0, &defaultLabel); - Bind(&returnNumber0); - { - result = Int64ToTaggedPtr(Int32(0)); - Jump(&exit); - } - Bind(&defaultLabel); - { - CallRuntime(glue, RTSTUB_ID(OtherToNumber), { tagged }); - Jump(&exit); - } - } - Bind(&exit); - auto ret = *result; - env->SubCfgExit(); - return ret; -} - -GateRef StubBuilder::GetLengthOfJsArray(GateRef glue, GateRef array) -{ - auto env = GetEnvironment(); - Label entry(env); - env->SubCfgEntry(&entry); - Label exit(env); - Label isInt(env); - Label notInt(env); - Label notDouble(env); - Label isDouble(env); - DEFVARIABLE(result, VariableType::INT32(), Int32(0)); - GateRef len = Load(VariableType::JS_ANY(), array, IntPtr(JSArray::LENGTH_OFFSET)); - Branch(TaggedIsInt(len), &isInt, ¬Int); - Bind(&isInt); - { - result = TaggedGetInt(len); + result = CallRuntime(glue, RTSTUB_ID(ToNumber), { tagged }); Jump(&exit); } - Bind(¬Int); - { - Branch(TaggedIsDouble(len), &isDouble, ¬Double); - Bind(&isDouble); - { - result = DoubleToInt(glue, GetDoubleOfTDouble(len)); - Jump(&exit); - } - Bind(¬Double); - { - FatalPrint(glue, { Int32(GET_MESSAGE_STRING_ID(ThisBranchIsUnreachable)) }); - Jump(&exit); - } - } Bind(&exit); auto ret = *result; env->SubCfgExit(); return ret; } -GateRef StubBuilder::CreateListFromArrayLike(GateRef glue, GateRef arrayObj) -{ - auto env = GetEnvironment(); - Label entry(env); - env->SubCfgEntry(&entry); - DEFVARIABLE(res, VariableType::JS_ANY(), Hole()); - DEFVARIABLE(index, VariableType::INT32(), Int32(0)); - Label exit(env); - - // 3. If Type(obj) is Object, throw a TypeError exception. - Label targetIsHeapObject(env); - Label targetIsEcmaObject(env); - Label targetNotEcmaObject(env); - Branch(TaggedIsHeapObject(arrayObj), &targetIsHeapObject, &targetNotEcmaObject); - Bind(&targetIsHeapObject); - Branch(TaggedObjectIsEcmaObject(arrayObj), &targetIsEcmaObject, &targetNotEcmaObject); - Bind(&targetNotEcmaObject); - { - GateRef taggedId = Int32(GET_MESSAGE_STRING_ID(TargetTypeNotObject)); - CallRuntime(glue, RTSTUB_ID(ThrowTypeError), { IntToTaggedInt(taggedId) }); - Jump(&exit); - } - Bind(&targetIsEcmaObject); - { - // 4. Let len be ToLength(Get(obj, "length")). - GateRef lengthString = GetGlobalConstantValue(VariableType::JS_POINTER(), glue, - ConstantIndex::LENGTH_STRING_INDEX); - GateRef value = FastGetPropertyByName(glue, arrayObj, lengthString); - GateRef number = ToLength(glue, value); - // 5. ReturnIfAbrupt(len). - Label isPendingException1(env); - Label noPendingException1(env); - Branch(HasPendingException(glue), &isPendingException1, &noPendingException1); - Bind(&isPendingException1); - { - Jump(&exit); - } - Bind(&noPendingException1); - { - Label indexInRange(env); - Label indexOutRange(env); - GateRef doubleLen = GetDoubleOfTNumber(number); - Branch(DoubleGreaterThan(doubleLen, Double(JSObject::MAX_ELEMENT_INDEX)), &indexOutRange, &indexInRange); - Bind(&indexOutRange); - { - GateRef taggedId = Int32(GET_MESSAGE_STRING_ID(LenGreaterThanMax)); - CallRuntime(glue, RTSTUB_ID(ThrowTypeError), { IntToTaggedInt(taggedId) }); - Jump(&exit); - } - Bind(&indexInRange); - { - GateRef int32Len = DoubleToInt(glue, doubleLen); - // 6. Let list be an empty List. - NewObjectStubBuilder newBuilder(this); - GateRef array = newBuilder.NewTaggedArray(glue, int32Len); - Label targetIsTypeArray(env); - Label targetNotTypeArray(env); - Branch(IsTypedArray(arrayObj), &targetIsTypeArray, &targetNotTypeArray); - Bind(&targetIsTypeArray); - { - TypedArrayStubBuilder arrayStubBuilder(this); - arrayStubBuilder.FastCopyElementToArray(glue, arrayObj, array); - // c. ReturnIfAbrupt(next). - Label isPendingException2(env); - Label noPendingException2(env); - Branch(HasPendingException(glue), &isPendingException2, &noPendingException2); - Bind(&isPendingException2); - { - Jump(&exit); - } - Bind(&noPendingException2); - { - res = array; - Jump(&exit); - } - } - Bind(&targetNotTypeArray); - // 8. Repeat while index < len - Label loopHead(env); - Label loopEnd(env); - Label afterLoop(env); - Label isPendingException3(env); - Label noPendingException3(env); - Label storeValue(env); - Jump(&loopHead); - LoopBegin(&loopHead); - { - Branch(Int32UnsignedLessThan(*index, int32Len), &storeValue, &afterLoop); - Bind(&storeValue); - { - GateRef next = FastGetPropertyByIndex(glue, arrayObj, *index); - // c. ReturnIfAbrupt(next). - Branch(HasPendingException(glue), &isPendingException3, &noPendingException3); - Bind(&isPendingException3); - { - Jump(&exit); - } - Bind(&noPendingException3); - SetValueToTaggedArray(VariableType::JS_ANY(), glue, array, *index, next); - index = Int32Add(*index, Int32(1)); - Jump(&loopEnd); - } - } - Bind(&loopEnd); - LoopEnd(&loopHead); - Bind(&afterLoop); - { - res = array; - Jump(&exit); - } - } - } - } - Bind(&exit); - GateRef ret = *res; - env->SubCfgExit(); - return ret; -} - GateRef StubBuilder::ToLength(GateRef glue, GateRef target) { auto env = GetEnvironment(); @@ -6084,21 +6330,10 @@ GateRef StubBuilder::HasStableElements(GateRef glue, GateRef obj) Branch(IsStableElements(jsHclass), &targetIsStableElements, &exit); Bind(&targetIsStableElements); { - GateRef guardiansOffset = IntPtr(JSThread::GlueData::GetStableArrayElementsGuardiansOffset(env->Is32Bit())); - GateRef guardians = Load(VariableType::JS_ANY(), glue, guardiansOffset); - Label targetIsTaggedTrue(env); - Label targetIsTaggedFalse(env); - Branch(TaggedIsTrue(guardians), &targetIsTaggedTrue, &targetIsTaggedFalse); - Bind(&targetIsTaggedTrue); - { - result = True(); - Jump(&exit); - } - Bind(&targetIsTaggedFalse); - { - result = False(); - Jump(&exit); - } + GateRef guardiansOffset = + IntPtr(JSThread::GlueData::GetStableArrayElementsGuardiansOffset(env->Is32Bit())); + result = Load(VariableType::BOOL(), glue, guardiansOffset); + Jump(&exit); } } Bind(&exit); @@ -6124,22 +6359,10 @@ GateRef StubBuilder::IsStableJSArguments(GateRef glue, GateRef obj) Branch(IsStableArguments(jsHclass), &targetIsStableArguments, &exit); Bind(&targetIsStableArguments); { - GateRef guardiansOffset = IntPtr(JSThread::GlueData::GetStableArrayElementsGuardiansOffset(env->Is32Bit())); - GateRef guardians = Load(VariableType::JS_ANY(), glue, guardiansOffset); - - Label targetIsTaggedTrue(env); - Label targetIsTaggedFalse(env); - Branch(TaggedIsTrue(guardians), &targetIsTaggedTrue, &targetIsTaggedFalse); - Bind(&targetIsTaggedTrue); - { - result = True(); - Jump(&exit); - } - Bind(&targetIsTaggedFalse); - { - result = False(); - Jump(&exit); - } + GateRef guardiansOffset = + IntPtr(JSThread::GlueData::GetStableArrayElementsGuardiansOffset(env->Is32Bit())); + result = Load(VariableType::BOOL(), glue, guardiansOffset); + Jump(&exit); } } Bind(&exit); @@ -6165,22 +6388,11 @@ GateRef StubBuilder::IsStableJSArray(GateRef glue, GateRef obj) Branch(IsStableArray(jsHclass), &targetIsStableArray, &exit); Bind(&targetIsStableArray); { - GateRef guardiansOffset = IntPtr(JSThread::GlueData::GetStableArrayElementsGuardiansOffset(env->Is32Bit())); - GateRef guardians = Load(VariableType::JS_ANY(), glue, guardiansOffset); - - Label targetIsTaggedTrue(env); - Label targetIsTaggedFalse(env); - Branch(TaggedIsTrue(guardians), &targetIsTaggedTrue, &targetIsTaggedFalse); - Bind(&targetIsTaggedTrue); - { - result = True(); - Jump(&exit); - } - Bind(&targetIsTaggedFalse); - { - result = False(); - Jump(&exit); - } + GateRef guardiansOffset = + IntPtr(JSThread::GlueData::GetStableArrayElementsGuardiansOffset(env->Is32Bit())); + GateRef guardians = Load(VariableType::BOOL(), glue, guardiansOffset); + result.WriteVariable(guardians); + Jump(&exit); } } Bind(&exit); diff --git a/ecmascript/compiler/stub_builder.h b/ecmascript/compiler/stub_builder.h index 28284e754ef487d9330f20970e8fbd7e89afee8b..e353ac05e1dd98c63a512cce9dbebe3333a613fa 100644 --- a/ecmascript/compiler/stub_builder.h +++ b/ecmascript/compiler/stub_builder.h @@ -23,6 +23,7 @@ #include "ecmascript/compiler/variable_type.h" namespace panda::ecmascript::kungfu { +struct StringInfoGateRef; using namespace panda::ecmascript; // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define DEFVARIABLE(varname, type, val) Variable varname(GetEnvironment(), type, NextVariableId(), val) @@ -97,7 +98,7 @@ public: GateRef Int16(int16_t value); GateRef Int32(int32_t value); GateRef Int64(int64_t value); - GateRef StringPtr(const std::string &str); + GateRef StringPtr(std::string_view str); GateRef IntPtr(int64_t value); GateRef IntPtrSize(); GateRef RelocatableData(uint64_t value); @@ -209,6 +210,7 @@ public: GateRef ObjectAddressToRange(GateRef x); GateRef InYoungGeneration(GateRef region); GateRef TaggedIsGeneratorObject(GateRef x); + GateRef TaggedIsJSArray(GateRef x); GateRef TaggedIsAsyncGeneratorObject(GateRef x); GateRef TaggedIsJSGlobalObject(GateRef x); GateRef TaggedIsWeak(GateRef x); @@ -219,6 +221,7 @@ public: GateRef TaggedIsString(GateRef obj); GateRef BothAreString(GateRef x, GateRef y); GateRef TaggedIsStringOrSymbol(GateRef obj); + GateRef TaggedIsSymbol(GateRef obj); GateRef GetNextPositionForHash(GateRef last, GateRef count, GateRef size); GateRef DoubleIsNAN(GateRef x); GateRef DoubleIsINF(GateRef x); @@ -235,9 +238,12 @@ public: GateRef IntToTaggedInt(GateRef x); GateRef Int64ToTaggedInt(GateRef x); GateRef DoubleToTaggedDoublePtr(GateRef x); + GateRef TaggedPtrToTaggedDoublePtr(GateRef x); + GateRef TaggedPtrToTaggedIntPtr(GateRef x); GateRef CastDoubleToInt64(GateRef x); GateRef TaggedTrue(); GateRef TaggedFalse(); + GateRef TaggedUndefined(); // compare operation GateRef Int8Equal(GateRef x, GateRef y); GateRef Equal(GateRef x, GateRef y); @@ -258,6 +264,7 @@ public: GateRef Int32UnsignedGreaterThan(GateRef x, GateRef y); GateRef Int32UnsignedLessThan(GateRef x, GateRef y); GateRef Int32UnsignedGreaterThanOrEqual(GateRef x, GateRef y); + GateRef Int32UnsignedLessThanOrEqual(GateRef x, GateRef y); GateRef Int64GreaterThan(GateRef x, GateRef y); GateRef Int64LessThan(GateRef x, GateRef y); GateRef Int64LessThanOrEqual(GateRef x, GateRef y); @@ -280,7 +287,6 @@ public: void SetPropertiesArray(VariableType type, GateRef glue, GateRef object, GateRef propsArray); void SetHash(GateRef glue, GateRef object, GateRef hash); GateRef GetLengthOfTaggedArray(GateRef array); - GateRef GetLengthOfJsArray(GateRef glue, GateRef array); // object operation GateRef IsJSHClass(GateRef obj); GateRef LoadHClass(GateRef object); @@ -291,6 +297,8 @@ public: GateRef IsDictionaryModeByHClass(GateRef hClass); GateRef IsDictionaryElement(GateRef hClass); GateRef IsStableElements(GateRef hClass); + GateRef HasConstructorByHClass(GateRef hClass); + GateRef HasConstructor(GateRef object); GateRef IsClassConstructorFromBitField(GateRef bitfield); GateRef IsClassConstructor(GateRef object); GateRef IsClassPrototype(GateRef object); @@ -300,6 +308,7 @@ public: GateRef IsSymbol(GateRef obj); GateRef IsString(GateRef obj); GateRef IsLineString(GateRef obj); + GateRef IsSlicedString(GateRef obj); GateRef IsConstantString(GateRef obj); GateRef IsTreeString(GateRef obj); GateRef TreeStringIsFlat(GateRef string); @@ -318,6 +327,9 @@ public: GateRef IsAccessor(GateRef attr); GateRef IsInlinedProperty(GateRef attr); GateRef IsField(GateRef attr); + GateRef IsElement(GateRef attr); + GateRef IsStringElement(GateRef attr); + GateRef IsStringLength(GateRef attr); GateRef IsNonExist(GateRef attr); GateRef IsJSAPIVector(GateRef attr); GateRef IsJSAPIStack(GateRef obj); @@ -332,12 +344,14 @@ public: GateRef IsJSAPILinkedList(GateRef obj); GateRef IsJSAPIList(GateRef obj); GateRef IsJSAPIArrayList(GateRef obj); + GateRef IsJSObjectType(GateRef obj, JSType jsType); GateRef GetTarget(GateRef proxyObj); GateRef HandlerBaseIsAccessor(GateRef attr); GateRef HandlerBaseIsJSArray(GateRef attr); GateRef HandlerBaseIsInlinedProperty(GateRef attr); GateRef HandlerBaseGetOffset(GateRef attr); GateRef HandlerBaseGetAttrIndex(GateRef attr); + GateRef HandlerBaseGetRep(GateRef attr); GateRef IsInvalidPropertyBox(GateRef obj); GateRef GetValueFromPropertyBox(GateRef obj); void SetValueToPropertyBox(GateRef glue, GateRef obj, GateRef value); @@ -357,11 +371,13 @@ public: GateRef HclassIsTransitionHandler(GateRef hClass); GateRef HclassIsPropertyBox(GateRef hClass); GateRef PropAttrGetOffset(GateRef attr); - GateRef InstanceOf(GateRef glue, GateRef object, GateRef target, GateRef profileTypeInfo, GateRef slotId); + GateRef InstanceOf(GateRef glue, GateRef object, GateRef target, GateRef profileTypeInfo, GateRef slotId, + ProfileOperation callback); GateRef OrdinaryHasInstance(GateRef glue, GateRef target, GateRef obj); void TryFastHasInstance(GateRef glue, GateRef instof, GateRef target, GateRef object, Label *fastPath, - Label *exit, Variable *result); + Label *exit, Variable *result, ProfileOperation callback); GateRef SameValue(GateRef glue, GateRef left, GateRef right); + GateRef SameValueZero(GateRef glue, GateRef left, GateRef right); GateRef HasStableElements(GateRef glue, GateRef obj); GateRef IsStableJSArguments(GateRef glue, GateRef obj); GateRef IsStableJSArray(GateRef glue, GateRef obj); @@ -380,7 +396,9 @@ public: GateRef TryGetHashcodeFromString(GateRef string); GateRef GetFirstFromTreeString(GateRef string); GateRef GetSecondFromTreeString(GateRef string); + GateRef GetIsAllTaggedPropFromHClass(GateRef hclass); void SetBitFieldToHClass(GateRef glue, GateRef hClass, GateRef bitfield); + void SetIsAllTaggedProp(GateRef glue, GateRef hclass, GateRef hasRep); void SetPrototypeToHClass(VariableType type, GateRef glue, GateRef hClass, GateRef proto); void SetProtoChangeDetailsToHClass(VariableType type, GateRef glue, GateRef hClass, GateRef protoChange); @@ -388,22 +406,30 @@ public: void SetHClassTypeIDToHClass(GateRef glue, GateRef hClass, GateRef id); void SetEnumCacheToHClass(VariableType type, GateRef glue, GateRef hClass, GateRef key); void SetTransitionsToHClass(VariableType type, GateRef glue, GateRef hClass, GateRef transition); + void SetParentToHClass(VariableType type, GateRef glue, GateRef hClass, GateRef parent); void SetIsProtoTypeToHClass(GateRef glue, GateRef hClass, GateRef value); GateRef IsProtoTypeHClass(GateRef hClass); void SetPropertyInlinedProps(GateRef glue, GateRef obj, GateRef hClass, GateRef value, GateRef attrOffset, VariableType type = VariableType::JS_ANY()); GateRef GetPropertyInlinedProps(GateRef obj, GateRef hClass, GateRef index); + GateRef GetInlinedPropOffsetFromHClass(GateRef hclass, GateRef attrOffset); void IncNumberOfProps(GateRef glue, GateRef hClass); GateRef GetNumberOfPropsFromHClass(GateRef hClass); GateRef IsTSHClass(GateRef hClass); void SetNumberOfPropsToHClass(GateRef glue, GateRef hClass, GateRef value); + void SetElementsKindToTrackInfo(GateRef glue, GateRef trackInfo, GateRef elementsKind); + GateRef GetElementsKindFromHClass(GateRef hClass); GateRef GetObjectSizeFromHClass(GateRef hClass); GateRef GetInlinedPropsStartFromHClass(GateRef hClass); GateRef GetInlinedPropertiesFromHClass(GateRef hClass); void ThrowTypeAndReturn(GateRef glue, int messageId, GateRef val); GateRef GetValueFromTaggedArray(GateRef elements, GateRef index); + void SetValueToTaggedArrayWithAttr( + GateRef glue, GateRef array, GateRef index, GateRef key, GateRef val, GateRef attr); + void SetValueToTaggedArrayWithRep( + GateRef glue, GateRef array, GateRef index, GateRef val, GateRef rep, Label *repChange); void SetValueToTaggedArray(VariableType valType, GateRef glue, GateRef array, GateRef index, GateRef val); void UpdateValueAndAttributes(GateRef glue, GateRef elements, GateRef index, GateRef value, GateRef attr); GateRef IsSpecialIndexedObj(GateRef jsType); @@ -427,9 +453,9 @@ public: GateRef IsMatchInTransitionDictionary(GateRef element, GateRef key, GateRef metaData, GateRef attr); GateRef FindEntryFromTransitionDictionary(GateRef glue, GateRef elements, GateRef key, GateRef metaData); GateRef JSObjectGetProperty(GateRef obj, GateRef hClass, GateRef propAttr); - void JSObjectSetProperty(GateRef glue, GateRef obj, GateRef hClass, GateRef attr, GateRef value); + void JSObjectSetProperty(GateRef glue, GateRef obj, GateRef hClass, GateRef attr, GateRef key, GateRef value); GateRef ShouldCallSetter(GateRef receiver, GateRef holder, GateRef accessor, GateRef attr); - GateRef CallSetterHelper(GateRef glue, GateRef holder, GateRef accessor, GateRef value); + GateRef CallSetterHelper(GateRef glue, GateRef holder, GateRef accessor, GateRef value, ProfileOperation callback); GateRef SetHasConstructorCondition(GateRef glue, GateRef receiver, GateRef key); GateRef AddPropertyByName(GateRef glue, GateRef receiver, GateRef key, GateRef value, GateRef propertyAttributes, ProfileOperation callback); @@ -438,30 +464,36 @@ public: GateRef IsInternalString(GateRef string); GateRef IsDigit(GateRef ch); GateRef StringToElementIndex(GateRef glue, GateRef string); - GateRef ComputePropertyCapacityInJSObj(GateRef oldLength); + GateRef ComputeNonInlinedFastPropsCapacity(GateRef oldLength, GateRef maxNonInlinedFastPropsCapacity); GateRef FindTransitions(GateRef glue, GateRef receiver, GateRef hClass, GateRef key, GateRef attr); + void TransitionForRepChange(GateRef glue, GateRef receiver, GateRef key, GateRef attr); + void TransitToElementsKind(GateRef glue, GateRef receiver, GateRef value, GateRef kind); GateRef TaggedToRepresentation(GateRef value); + GateRef TaggedToElementKind(GateRef value); GateRef LdGlobalRecord(GateRef glue, GateRef key); GateRef LoadFromField(GateRef receiver, GateRef handlerInfo); GateRef LoadGlobal(GateRef cell); GateRef LoadElement(GateRef glue, GateRef receiver, GateRef key); + GateRef LoadStringElement(GateRef glue, GateRef receiver, GateRef key); GateRef TryToElementsIndex(GateRef glue, GateRef key); GateRef CheckPolyHClass(GateRef cachedValue, GateRef hClass); - GateRef LoadICWithHandler(GateRef glue, GateRef receiver, GateRef holder, GateRef handler); + GateRef LoadICWithHandler( + GateRef glue, GateRef receiver, GateRef holder, GateRef handler, ProfileOperation callback); GateRef StoreICWithHandler(GateRef glue, GateRef receiver, GateRef holder, GateRef value, GateRef handler, ProfileOperation callback = ProfileOperation()); - GateRef ICStoreElement(GateRef glue, GateRef receiver, GateRef key, - GateRef value, GateRef handlerInfo); + GateRef ICStoreElement(GateRef glue, GateRef receiver, GateRef key, GateRef value, GateRef handlerInfo); GateRef GetArrayLength(GateRef object); - GateRef DoubleToInt(GateRef glue, GateRef x); - void StoreField(GateRef glue, GateRef receiver, GateRef value, GateRef handler, ProfileOperation callback); - void StoreWithTransition(GateRef glue, GateRef receiver, GateRef value, GateRef handler, + GateRef DoubleToInt(GateRef glue, GateRef x, size_t bits = base::INT32_BITS); + void SetArrayLength(GateRef glue, GateRef object, GateRef len); + GateRef StoreField(GateRef glue, GateRef receiver, GateRef value, GateRef handler, ProfileOperation callback); + GateRef StoreWithTransition(GateRef glue, GateRef receiver, GateRef value, GateRef handler, ProfileOperation callback, bool withPrototype = false); GateRef StoreGlobal(GateRef glue, GateRef value, GateRef cell); void JSHClassAddProperty(GateRef glue, GateRef receiver, GateRef key, GateRef attr); void NotifyHClassChanged(GateRef glue, GateRef oldHClass, GateRef newHClass); GateRef GetInt64OfTInt(GateRef x); GateRef GetInt32OfTInt(GateRef x); + GateRef GetDoubleOfTInt(GateRef x); GateRef GetDoubleOfTDouble(GateRef x); GateRef GetDoubleOfTNumber(GateRef x); GateRef LoadObjectFromWeakRef(GateRef x); @@ -472,6 +504,8 @@ public: GateRef ChangeFloat64ToInt32(GateRef x); GateRef Int64ToTaggedPtr(GateRef x); GateRef TruncInt16ToInt8(GateRef x); + GateRef TruncInt32ToInt16(GateRef x); + GateRef TruncInt32ToInt8(GateRef x); GateRef CastInt32ToFloat32(GateRef x); GateRef CastInt64ToFloat64(GateRef x); GateRef SExtInt32ToInt64(GateRef x); @@ -505,16 +539,22 @@ public: GateRef SetIsInlinePropsFieldInPropAttr(GateRef attr, GateRef value); GateRef SetTrackTypeInPropAttr(GateRef attr, GateRef type); GateRef GetTrackTypeInPropAttr(GateRef attr); + GateRef GetRepInPropAttr(GateRef attr); + GateRef IsIntRepInPropAttr(GateRef attr); + GateRef IsDoubleRepInPropAttr(GateRef attr); + GateRef SetTaggedRepInPropAttr(GateRef attr); void SetHasConstructorToHClass(GateRef glue, GateRef hClass, GateRef value); void UpdateValueInDict(GateRef glue, GateRef elements, GateRef index, GateRef value); GateRef GetBitMask(GateRef bitoffset); GateRef IntPtrEuqal(GateRef x, GateRef y); + void SetValueWithAttr(GateRef glue, GateRef obj, GateRef offset, GateRef key, GateRef value, GateRef attr); + void SetValueWithRep(GateRef glue, GateRef obj, GateRef offset, GateRef value, GateRef rep, Label *repChange); void SetValueWithBarrier(GateRef glue, GateRef obj, GateRef offset, GateRef value); - GateRef GetPropertyByIndex(GateRef glue, GateRef receiver, GateRef index); - GateRef GetPropertyByName(GateRef glue, GateRef receiver, GateRef key); - GateRef FastGetPropertyByName(GateRef glue, GateRef obj, GateRef key); - GateRef FastGetPropertyByIndex(GateRef glue, GateRef obj, GateRef index); - GateRef GetPropertyByValue(GateRef glue, GateRef receiver, GateRef keyValue); + GateRef GetPropertyByIndex(GateRef glue, GateRef receiver, GateRef index, ProfileOperation callback); + GateRef GetPropertyByName(GateRef glue, GateRef receiver, GateRef key, ProfileOperation callback); + GateRef FastGetPropertyByName(GateRef glue, GateRef obj, GateRef key, ProfileOperation callback); + GateRef FastGetPropertyByIndex(GateRef glue, GateRef obj, GateRef index, ProfileOperation callback); + GateRef GetPropertyByValue(GateRef glue, GateRef receiver, GateRef keyValue, ProfileOperation callback); GateRef SetPropertyByIndex(GateRef glue, GateRef receiver, GateRef index, GateRef value, bool useOwn); GateRef SetPropertyByName(GateRef glue, GateRef receiver, GateRef key, GateRef value, bool useOwn, ProfileOperation callback = ProfileOperation()); // Crawl prototype chain @@ -536,28 +576,28 @@ public: GateRef GetPropertiesFromJSObject(GateRef object); template GateRef BinaryOp(GateRef x, GateRef y); - GateRef GetGlobalOwnProperty(GateRef glue, GateRef receiver, GateRef key); + template + GateRef BinaryOpWithOverflow(GateRef x, GateRef y); + GateRef GetGlobalOwnProperty(GateRef glue, GateRef receiver, GateRef key, ProfileOperation callback); inline GateRef GetObjectFromConstPool(GateRef constpool, GateRef index); + GateRef GetConstPoolFromFunction(GateRef jsFunc); GateRef GetStringFromConstPool(GateRef glue, GateRef constpool, GateRef index); - GateRef GetMethodFromConstPool(GateRef glue, GateRef constpool, GateRef index); + GateRef GetMethodFromConstPool(GateRef glue, GateRef constpool, GateRef index, GateRef module); GateRef GetArrayLiteralFromConstPool(GateRef glue, GateRef constpool, GateRef index, GateRef module); GateRef GetObjectLiteralFromConstPool(GateRef glue, GateRef constpool, GateRef index, GateRef module); - GateRef CreateListFromArrayLike(GateRef glue, GateRef arrayObj); - GateRef BuildArgumentsListFastElements(GateRef glue, GateRef arrayObj); - GateRef MakeArgListWithHole(GateRef glue, GateRef argv, GateRef length); void SetExtensibleToBitfield(GateRef glue, GateRef obj, bool isExtensible); // fast path - GateRef FastEqual(GateRef left, GateRef right, ProfileOperation callback); + GateRef FastEqual(GateRef glue, GateRef left, GateRef right, ProfileOperation callback); GateRef FastStrictEqual(GateRef glue, GateRef left, GateRef right, ProfileOperation callback); GateRef FastStringEqual(GateRef glue, GateRef left, GateRef right); GateRef FastMod(GateRef gule, GateRef left, GateRef right, ProfileOperation callback); GateRef FastTypeOf(GateRef left, GateRef right); - GateRef FastMul(GateRef left, GateRef right, ProfileOperation callback); + GateRef FastMul(GateRef glue, GateRef left, GateRef right, ProfileOperation callback); GateRef FastDiv(GateRef left, GateRef right, ProfileOperation callback); - GateRef FastAdd(GateRef left, GateRef right, ProfileOperation callback); - GateRef FastSub(GateRef left, GateRef right, ProfileOperation callback); + GateRef FastAdd(GateRef glue, GateRef left, GateRef right, ProfileOperation callback); + GateRef FastSub(GateRef glue, GateRef left, GateRef right, ProfileOperation callback); GateRef FastToBoolean(GateRef value); // Add SpecialContainer @@ -589,7 +629,8 @@ public: inline GateRef GetGlobalConstantValue( VariableType type, GateRef glue, ConstantIndex index); inline GateRef GetGlobalEnvValue(VariableType type, GateRef env, size_t index); - GateRef CallGetterHelper(GateRef glue, GateRef receiver, GateRef holder, GateRef accessor); + GateRef CallGetterHelper( + GateRef glue, GateRef receiver, GateRef holder, GateRef accessor, ProfileOperation callback); GateRef ConstructorCheck(GateRef glue, GateRef ctor, GateRef outPut, GateRef thisObj); GateRef JSCallDispatch(GateRef glue, GateRef func, GateRef actualNumArgs, GateRef jumpSize, GateRef hotnessCounter, JSCallMode mode, std::initializer_list args, @@ -609,25 +650,31 @@ public: GateRef callField, GateRef method, Label* notFastBuiltins, Label* exit, Variable* result, std::initializer_list args, JSCallMode mode); inline void SetLength(GateRef glue, GateRef str, GateRef length, bool compressed); + inline void SetLength(GateRef glue, GateRef str, GateRef length, GateRef isCompressed); inline void SetRawHashcode(GateRef glue, GateRef str, GateRef rawHashcode); void Assert(int messageId, int line, GateRef glue, GateRef condition, Label *nextLabel); - GateRef FlattenString(GateRef glue, GateRef str); - void FlattenString(GateRef str, Variable *flatStr, Label *fastPath, Label *slowPath); - GateRef GetNormalStringData(GateRef str); + GateRef GetNormalStringData(const StringInfoGateRef &stringInfoGate); void Comment(GateRef glue, const std::string &str); GateRef ToNumber(GateRef glue, GateRef tagged); inline GateRef LoadObjectFromConstPool(GateRef jsFunc, GateRef index); + inline GateRef LoadPfHeaderFromConstPool(GateRef jsFunc); + GateRef RemoveTaggedWeakTag(GateRef weak); + inline GateRef LoadHCIndexFromConstPool(GateRef cachedArray, GateRef cachedLength, GateRef traceId, Label *miss); + inline GateRef LoadHCIndexInfosFromConstPool(GateRef jsFunc); private: using BinaryOperation = std::function; GateRef ChangeTaggedPointerToInt64(GateRef x); template - GateRef FastAddSubAndMul(GateRef left, GateRef right, ProfileOperation callback); + GateRef FastAddSubAndMul(GateRef glue, GateRef left, GateRef right, ProfileOperation callback); GateRef FastIntDiv(GateRef left, GateRef right, Label *bailout, ProfileOperation callback); - GateRef FastBinaryOp(GateRef left, GateRef right, + template + GateRef FastBinaryOp(GateRef glue, GateRef left, GateRef right, const BinaryOperation& intOp, const BinaryOperation& floatOp, ProfileOperation callback); void InitializeArguments(); + void CheckDetectorName(GateRef glue, GateRef key, Label *fallthrough, Label *slow); + CallSignature *callSignature_ {nullptr}; Environment *env_; }; diff --git a/ecmascript/compiler/test_stubs.cpp b/ecmascript/compiler/test_stubs.cpp deleted file mode 100644 index 9d45f46cc63abd6eeb85251da9a399ab6725f785..0000000000000000000000000000000000000000 --- a/ecmascript/compiler/test_stubs.cpp +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (c) 2021 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ecmascript/compiler/test_stubs.h" - -#include "ecmascript/compiler/llvm_ir_builder.h" -#include "ecmascript/compiler/stub_builder-inl.h" -#include "ecmascript/compiler/variable_type.h" -#include "ecmascript/message_string.h" - -namespace panda::ecmascript::kungfu { -using namespace panda::ecmascript; -#ifndef NDEBUG -void FooAOTStubBuilder::GenerateCircuit() -{ - GateRef glue = PtrArgument(0); - GateRef argc = Int32Argument(1); - GateRef calltarget = TaggedArgument(2); - GateRef newtarget = TaggedArgument(3); - GateRef thisObj = TaggedArgument(4); - GateRef a = TaggedArgument(5); - GateRef b = TaggedArgument(6); - GateRef pcOffset = Int32(1); - (void)calltarget; - GateRef barIndex = IntToTaggedInt(Int32(CommonStubCSigns::BarAOT)); - GateRef numArgs = IntToTaggedInt(Int32(2)); - GateRef barfunc = CallRuntime(glue, RTSTUB_ID(DefineAotFunc), {barIndex, numArgs}); - GateRef result = - CallNGCRuntime(glue, RTSTUB_ID(JSCall), {glue, argc, barfunc, newtarget, thisObj, a, b, pcOffset}); - Return(result); -} - -void BarAOTStubBuilder::GenerateCircuit() -{ - GateRef glue = PtrArgument(0); - [[maybe_unused]] GateRef argc = Int32Argument(1); - [[maybe_unused]] GateRef calltarget = TaggedArgument(2); - [[maybe_unused]] GateRef newtarget = TaggedArgument(3); - [[maybe_unused]] GateRef thisObj = TaggedArgument(4); - GateRef a = TaggedArgument(5); - GateRef b = TaggedArgument(6); - GateRef result = CallRuntime(glue, RTSTUB_ID(Add2), {a, b}); - Return(result); -} - -void Foo1AOTStubBuilder::GenerateCircuit() -{ - GateRef glue = PtrArgument(0); - GateRef argc = Int32Argument(1); - GateRef calltarget = TaggedArgument(2); - GateRef newtarget = TaggedArgument(3); - GateRef thisObj = TaggedArgument(4); - GateRef a = TaggedArgument(5); - GateRef b = TaggedArgument(6); - GateRef pcOffset = Int32(1); - (void)calltarget; - GateRef barIndex = IntToTaggedInt(Int32(CommonStubCSigns::Bar1AOT)); - GateRef numArgs = IntToTaggedInt(Int32(3)); - GateRef barfunc = CallRuntime(glue, RTSTUB_ID(DefineAotFunc), {barIndex, numArgs}); - GateRef result = - CallNGCRuntime(glue, RTSTUB_ID(JSCall), {glue, argc, barfunc, newtarget, thisObj, a, b, pcOffset}); - Return(result); -} - -void Bar1AOTStubBuilder::GenerateCircuit() -{ - GateRef glue = PtrArgument(0); - GateRef argc = Int32Argument(1); - GateRef calltarget = TaggedArgument(2); - GateRef newtarget = TaggedArgument(3); - GateRef thisObj = TaggedArgument(4); - (void)argc; - (void)calltarget; - (void)newtarget; - (void)thisObj; - GateRef a = TaggedArgument(5); - GateRef b = TaggedArgument(6); - GateRef c = TaggedArgument(7); - GateRef result = CallRuntime(glue, RTSTUB_ID(Add2), {a, b}); - GateRef result2 = CallRuntime(glue, RTSTUB_ID(Add2), {result, c}); - Return(result2); -} - -void Foo2AOTStubBuilder::GenerateCircuit() -{ - GateRef glue = PtrArgument(0); - GateRef argc = Int32Argument(1); - GateRef calltarget = TaggedArgument(2); - GateRef newtarget = TaggedArgument(3); - GateRef thisObj = TaggedArgument(4); - GateRef a = TaggedArgument(5); - GateRef b = TaggedArgument(6); - GateRef pcOffset = Int32(1); - (void)calltarget; - GateRef actualArgC = Int64Add(argc, Int64(1)); - GateRef barIndex = IntToTaggedInt(Int32(CommonStubCSigns::BarAOT)); - GateRef numArgs = IntToTaggedInt(Int32(2)); - GateRef barfunc = CallRuntime(glue, RTSTUB_ID(DefineAotFunc), {barIndex, numArgs}); - GateRef result = CallNGCRuntime(glue, RTSTUB_ID(JSCall), {glue, actualArgC, barfunc, newtarget, thisObj, - a, b, Undefined(), pcOffset}); - Return(result); -} - -void FooNativeAOTStubBuilder::GenerateCircuit() -{ - GateRef glue = PtrArgument(0); - GateRef argc = Int32Argument(1); - GateRef calltarget = TaggedArgument(2); - GateRef newtarget = TaggedArgument(3); - GateRef thisObj = TaggedArgument(4); - GateRef a = TaggedArgument(5); - GateRef b = TaggedArgument(6); - GateRef pcOffset = Int32(1); - (void)calltarget; - GateRef actualArgC = Int64Add(argc, Int64(1)); - GateRef printfunc = CallRuntime(glue, RTSTUB_ID(GetPrintFunc), {}); - GateRef result = CallNGCRuntime(glue, RTSTUB_ID(JSCall), {glue, actualArgC, printfunc, newtarget, thisObj, - a, b, Undefined(), pcOffset}); - Return(result); -} - -void FooBoundAOTStubBuilder::GenerateCircuit() -{ - GateRef glue = PtrArgument(0); - GateRef argc = Int32Argument(1); - GateRef calltarget = TaggedArgument(2); - GateRef newtarget = TaggedArgument(3); - GateRef thisObj = TaggedArgument(4); - GateRef a = TaggedArgument(5); - GateRef b = TaggedArgument(6); - GateRef bindArguments = IntToTaggedInt(Int32(37)); - GateRef pcOffset = Int32(1); - (void)calltarget; - GateRef numArgs = IntToTaggedInt(Int32(2)); - GateRef barIndex = IntToTaggedInt(Int32(CommonStubCSigns::BarAOT)); - GateRef barfunc = CallRuntime(glue, RTSTUB_ID(DefineAotFunc), {barIndex, numArgs}); - GateRef bindfunc = CallRuntime(glue, RTSTUB_ID(GetBindFunc), {barfunc}); - GateRef newjsfunc = CallNGCRuntime(glue, RTSTUB_ID(JSCall), {glue, Int64(5), bindfunc, newtarget, barfunc, - Int64(0x02), bindArguments, pcOffset}); - GateRef result = CallNGCRuntime(glue, RTSTUB_ID(JSCall), {glue, argc, newjsfunc, newtarget, thisObj, - a, b, pcOffset}); - Return(result); -} - -void FooProxyAOTStubBuilder::GenerateCircuit() -{ - GateRef glue = PtrArgument(0); - GateRef argc = Int32Argument(1); - [[maybe_unused]] GateRef calltarget = TaggedArgument(2); - GateRef newtarget = TaggedArgument(3); - GateRef thisObj = TaggedArgument(4); - GateRef a = TaggedArgument(5); - GateRef b = TaggedArgument(6); - GateRef pcOffset = Int32(1); - - GateRef barIndex = IntToTaggedInt(Int32(CommonStubCSigns::BarAOT)); - GateRef numArgs = IntToTaggedInt(Int32(2)); - GateRef barfunc = CallRuntime(glue, RTSTUB_ID(DefineAotFunc), {barIndex, numArgs}); - - GateRef proxyfunc = CallRuntime(glue, RTSTUB_ID(DefineProxyFunc), {barfunc}); - GateRef result = - CallNGCRuntime(glue, RTSTUB_ID(JSCall), {glue, argc, proxyfunc, newtarget, thisObj, a, b, pcOffset}); - Return(result); -} - -void FooProxy2AOTStubBuilder::GenerateCircuit() -{ - GateRef glue = PtrArgument(0); - GateRef argc = Int32Argument(1); - [[maybe_unused]] GateRef calltarget = TaggedArgument(2); - GateRef newtarget = TaggedArgument(3); - GateRef thisObj = TaggedArgument(4); - GateRef a = TaggedArgument(5); - GateRef b = TaggedArgument(6); - GateRef pcOffset = Int32(1); - - GateRef barIndex = IntToTaggedInt(Int32(CommonStubCSigns::Bar2AOT)); - GateRef numArgs = IntToTaggedInt(Int32(2)); - GateRef barfunc = CallRuntime(glue, RTSTUB_ID(DefineAotFunc), {barIndex, numArgs}); - GateRef proxyHandler = CallRuntime(glue, RTSTUB_ID(DefineProxyHandler), {barfunc}); - - GateRef proxyfunc = CallRuntime(glue, RTSTUB_ID(DefineProxyFunc2), {barfunc, proxyHandler}); - GateRef result = - CallNGCRuntime(glue, RTSTUB_ID(JSCall), {glue, argc, proxyfunc, newtarget, thisObj, a, b, pcOffset}); - Return(result); -} - -void Bar2AOTStubBuilder::GenerateCircuit() -{ - [[maybe_unused]] GateRef glue = PtrArgument(0); - [[maybe_unused]] GateRef argc = Int32Argument(1); - [[maybe_unused]] GateRef calltarget = TaggedArgument(2); - [[maybe_unused]] GateRef newtarget = TaggedArgument(3); - [[maybe_unused]] GateRef thisObj = TaggedArgument(4); - CallRuntime(glue, RTSTUB_ID(DumpTaggedType), {thisObj}); - Return(thisObj); -} - -void TestAbsoluteAddressRelocationStubBuilder::GenerateCircuit() -{ - auto env = GetEnvironment(); - GateRef a = Int64Argument(0); - Label start(env); - Jump(&start); - Bind(&start); - GateRef globalValueC = RelocatableData(0xabc); - GateRef dummyValueC = Load(VariableType::INT64(), globalValueC); - GateRef result = ZExtInt1ToInt64(Int64Equal(a, dummyValueC)); - Return(result); -} -#endif -} // namespace panda::ecmascript::kungfu diff --git a/ecmascript/compiler/test_stubs_signature.cpp b/ecmascript/compiler/test_stubs_signature.cpp deleted file mode 100644 index 263a40e9dbce3c5379ff82d251b826a1a43bad83..0000000000000000000000000000000000000000 --- a/ecmascript/compiler/test_stubs_signature.cpp +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright (c) 2021 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ecmascript/compiler/call_signature.h" -namespace panda::ecmascript::kungfu { -#ifndef NDEBUG -DEF_CALL_SIGNATURE(FooAOT) -{ - // 7 : 7 input parameters - CallSignature fooAot("FooAOT", 0, 7, - ArgumentsOrder::DEFAULT_ORDER, VariableType::JS_ANY()); - *callSign = fooAot; - std::array params = { // 7 : 7 input parameters - VariableType::NATIVE_POINTER(), - VariableType::INT64(), - VariableType::JS_ANY(), // calltarget - VariableType::JS_ANY(), // newTarget - VariableType::JS_ANY(), // thisTarget - VariableType::JS_ANY(), // a - VariableType::JS_ANY(), // b - }; - callSign->SetParameters(params.data()); - callSign->SetCallConv(CallSignature::CallConv::WebKitJSCallConv); -} - -DEF_CALL_SIGNATURE(Foo1AOT) -{ - // 7 : 7 input parameters - CallSignature foo1Aot("Foo1AOT", 0, 7, - ArgumentsOrder::DEFAULT_ORDER, VariableType::JS_ANY()); - *callSign = foo1Aot; - std::array params = { // 7 : 7 input parameters - VariableType::NATIVE_POINTER(), - VariableType::INT64(), - VariableType::JS_ANY(), // calltarget - VariableType::JS_ANY(), // newTarget - VariableType::JS_ANY(), // thisTarget - VariableType::JS_ANY(), // a - VariableType::JS_ANY(), // b - }; - callSign->SetParameters(params.data()); - callSign->SetCallConv(CallSignature::CallConv::WebKitJSCallConv); -} - -DEF_CALL_SIGNATURE(Foo2AOT) -{ - // 7 : 7 input parameters - CallSignature foo2Aot("Foo2AOT", 0, 7, - ArgumentsOrder::DEFAULT_ORDER, VariableType::JS_ANY()); - *callSign = foo2Aot; - std::array params = { // 7 : 7 input parameters - VariableType::NATIVE_POINTER(), - VariableType::INT64(), - VariableType::JS_ANY(), // calltarget - VariableType::JS_ANY(), // newTarget - VariableType::JS_ANY(), // thisTarget - VariableType::JS_ANY(), // a - VariableType::JS_ANY(), // b - }; - callSign->SetParameters(params.data()); - callSign->SetCallConv(CallSignature::CallConv::WebKitJSCallConv); -} - -DEF_CALL_SIGNATURE(FooNativeAOT) -{ - // 7 : 7 input parameters - CallSignature foo2Aot("FooNativeAOT", 0, 7, - ArgumentsOrder::DEFAULT_ORDER, VariableType::JS_ANY()); - *callSign = foo2Aot; - std::array params = { // 7 : 7 input parameters - VariableType::NATIVE_POINTER(), - VariableType::INT64(), - VariableType::JS_ANY(), // calltarget - VariableType::JS_ANY(), // newTarget - VariableType::JS_ANY(), // thisTarget - VariableType::JS_ANY(), // a - VariableType::JS_ANY(), // b - }; - callSign->SetParameters(params.data()); - callSign->SetCallConv(CallSignature::CallConv::WebKitJSCallConv); -} - -DEF_CALL_SIGNATURE(FooBoundAOT) -{ - // 7 : 7 input parameters - CallSignature foo2Aot("FooBoundAOT", 0, 7, - ArgumentsOrder::DEFAULT_ORDER, VariableType::JS_ANY()); - *callSign = foo2Aot; - std::array params = { // 7 : 7 input parameters - VariableType::NATIVE_POINTER(), - VariableType::INT64(), - VariableType::JS_ANY(), // calltarget - VariableType::JS_ANY(), // newTarget - VariableType::JS_ANY(), // thisTarget - VariableType::JS_ANY(), // a - VariableType::JS_ANY(), // b - }; - callSign->SetParameters(params.data()); - callSign->SetCallConv(CallSignature::CallConv::WebKitJSCallConv); -} - -DEF_CALL_SIGNATURE(Bar1AOT) -{ - // 8 : 8 input parameters - CallSignature barAot("Bar1AOT", 0, 8, - ArgumentsOrder::DEFAULT_ORDER, VariableType::JS_ANY()); - *callSign = barAot; - std::array params = { // 8 : 8 input parameters - VariableType::NATIVE_POINTER(), - VariableType::INT64(), - VariableType::JS_ANY(), // calltarget - VariableType::JS_ANY(), // newTarget - VariableType::JS_ANY(), // thisTarget - VariableType::JS_ANY(), // a - VariableType::JS_ANY(), // b - VariableType::JS_ANY(), // c - }; - callSign->SetParameters(params.data()); - callSign->SetCallConv(CallSignature::CallConv::WebKitJSCallConv); -} - -DEF_CALL_SIGNATURE(BarAOT) -{ - // 7 : 7 input parameters - CallSignature barAot("BarAOT", 0, 7, - ArgumentsOrder::DEFAULT_ORDER, VariableType::JS_ANY()); - *callSign = barAot; - std::array params = { // 7 : 7 input parameters - VariableType::NATIVE_POINTER(), - VariableType::INT64(), - VariableType::JS_ANY(), // calltarget - VariableType::JS_ANY(), // newTarget - VariableType::JS_ANY(), // thisTarget - VariableType::JS_ANY(), // a - VariableType::JS_ANY(), // b - }; - callSign->SetParameters(params.data()); - callSign->SetCallConv(CallSignature::CallConv::WebKitJSCallConv); -} - -DEF_CALL_SIGNATURE(FooProxyAOT) -{ - // 8 : 8 input parameters - CallSignature fooProxyAot("FooProxyAOT", 0, 7, - ArgumentsOrder::DEFAULT_ORDER, VariableType::JS_ANY()); - *callSign = fooProxyAot; - std::array params = { // 7 : 7 input parameters - VariableType::NATIVE_POINTER(), - VariableType::INT64(), - VariableType::JS_ANY(), // calltarget - VariableType::JS_ANY(), // newTarget - VariableType::JS_ANY(), // thisTarget - VariableType::JS_ANY(), // a - VariableType::JS_ANY(), // b - }; - callSign->SetParameters(params.data()); - callSign->SetCallConv(CallSignature::CallConv::WebKitJSCallConv); -} - -DEF_CALL_SIGNATURE(FooProxy2AOT) -{ - // 7 : 7 input parameters - CallSignature FooProxy2AOT("FooProxy2AOT", 0, 7, - ArgumentsOrder::DEFAULT_ORDER, VariableType::JS_ANY()); - *callSign = FooProxy2AOT; - std::array params = { // 7 : 7 input parameters - VariableType::NATIVE_POINTER(), - VariableType::INT64(), - VariableType::JS_ANY(), // calltarget - VariableType::JS_ANY(), // newTarget - VariableType::JS_ANY(), // thisTarget - VariableType::JS_ANY(), // a - VariableType::JS_ANY(), // b - }; - callSign->SetParameters(params.data()); - callSign->SetCallConv(CallSignature::CallConv::WebKitJSCallConv); -} - -DEF_CALL_SIGNATURE(Bar2AOT) -{ - // 5 : 5 input parameters - CallSignature bar2Aot("Bar2AOT", 0, 5, - ArgumentsOrder::DEFAULT_ORDER, VariableType::JS_ANY()); - *callSign = bar2Aot; - std::array params = { // 5 : 5 input parameters - VariableType::NATIVE_POINTER(), - VariableType::INT64(), - VariableType::JS_ANY(), // calltarget - VariableType::JS_ANY(), // newTarget - VariableType::JS_ANY(), // thisTarget - }; - callSign->SetParameters(params.data()); - callSign->SetVariadicArgs(true); - callSign->SetCallConv(CallSignature::CallConv::WebKitJSCallConv); -} - -DEF_CALL_SIGNATURE(TestAbsoluteAddressRelocation) -{ - // 1 : 1 input parameters - CallSignature TestAbsoluteAddressRelocation("TestAbsoluteAddressRelocation", 0, 1, - ArgumentsOrder::DEFAULT_ORDER, VariableType::INT64()); // undefined or hole - *callSign = TestAbsoluteAddressRelocation; - // 1 : 1 input parameters - std::array params = { - VariableType::INT64(), - }; - callSign->SetParameters(params.data()); -} - -#endif -} // namespace panda::ecmascript::kungfu \ No newline at end of file diff --git a/ecmascript/compiler/tests/BUILD.gn b/ecmascript/compiler/tests/BUILD.gn index 9728049606a8884568070a3aeafd7a5bf2d4fb2d..6d3d4f7d56107b98e66b2800e873f3ccc1b24009 100644 --- a/ecmascript/compiler/tests/BUILD.gn +++ b/ecmascript/compiler/tests/BUILD.gn @@ -14,21 +14,6 @@ import("//arkcompiler/ets_runtime/js_runtime_config.gni") import("//arkcompiler/ets_runtime/test/test_helper.gni") -config("include_llvm_config") { - if (compile_llvm_online) { - include_dirs = [ - "//third_party/third_party_llvm-project/build/include", - "//third_party/third_party_llvm-project/llvm/include/", - ] - } else { - include_dirs = [ - "//prebuilts/ark_tools/ark_js_prebuilts/llvm_prebuilts/llvm/include", - "//prebuilts/ark_tools/ark_js_prebuilts/llvm_prebuilts/build/include", - ] - } - cflags_cc = [ "-DARK_GC_SUPPORT" ] -} - module_output_path = "arkcompiler/ets_runtime" host_unittest_action("AssemblerTest") { @@ -39,81 +24,11 @@ host_unittest_action("AssemblerTest") { "../assembler/tests/assembler_aarch64_test.cpp", "../assembler/tests/assembler_x64_test.cpp", ] - configs = [ - ":include_llvm_config", - "//arkcompiler/ets_runtime:ecma_test_config", - "//arkcompiler/ets_runtime:ark_jsruntime_compiler_config", - "//arkcompiler/ets_runtime:ark_jsruntime_public_config", - ] - - if (compile_llvm_online) { - lib_dirs = [ "//third_party/third_party_llvm-project/build/lib" ] - } else { - lib_dirs = - [ "//prebuilts/ark_tools/ark_js_prebuilts/llvm_prebuilts/build/lib" ] - } - - libs = [ - "stdc++", - "z", - "LLVMTarget", - "LLVMObject", - "LLVMMC", - "LLVMSupport", - "LLVMCore", - "LLVMExecutionEngine", - "LLVMInterpreter", - "LLVMMCJIT", - "LLVMExegesis", - "LLVMRuntimeDyld", - "LLVMInstCombine", - "LLVMAnalysis", - "LLVMScalarOpts", - "LLVMBinaryFormat", - "LLVMDebugInfoDWARF", - "LLVMRemarks", - "LLVMTextAPI", - "LLVMScalarOpts", - "LLVMTransformUtils", - "LLVMBitReader", - "LLVMAsmPrinter", - "LLVMProfileData", - "LLVMBitstreamReader", - "LLVMSelectionDAG", - "LLVMGlobalISel", - "LLVMLTO", - "LLVMCFGuard", - "LLVMVectorize", - "LLVMDemangle", - "LLVMipo", - "LLVMInstrumentation", - "LLVMDebugInfoCodeView", - "LLVMAggressiveInstCombine", - "LLVMAsmParser", - "LLVMMCParser", - "LLVMMIRParser", - "LLVMX86Info", - "LLVMAArch64Info", - "LLVMARMDesc", - "LLVMAArch64Desc", - "LLVMX86Desc", - "LLVMX86Disassembler", - "LLVMARMDisassembler", - "LLVMAArch64Disassembler", - "LLVMMCDisassembler", - "LLVMAArch64CodeGen", - "LLVMARMCodeGen", - "LLVMCodeGen", - "LLVMX86CodeGen", - "LLVMX86AsmParser", - "LLVMTransformUtils", - "LLVMAArch64Utils", - "LLVMARMUtils", - "LLVMIRReader", - ] deps = [ - "//arkcompiler/ets_runtime/ecmascript/compiler:libark_jsoptimizer_test", + "$ark_root/libpandafile:libarkfile_static", + "$js_root:libark_jsruntime_test_set", + "$js_root/ecmascript/compiler:libark_jsoptimizer_set", sdk_libc_secshared_dep, ] @@ -122,9 +37,77 @@ host_unittest_action("AssemblerTest") { deps += hiviewdfx_deps } +host_unittest_action("TypedArrayLoweringTest") { + module_out_path = module_output_path + + sources = [ + # test file + "typed_array_lowering_test.cpp", + ] + + deps = [ + "$ark_root/libpandafile:libarkfile_static", + "$js_root:libark_jsruntime_test_set", + "$js_root/ecmascript/compiler:libark_jsoptimizer_set", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("DeadCodeEliminationTest") { + module_out_path = module_output_path + + sources = [ + # test file + "dead_code_elimination_test.cpp", + ] + + deps = [ + "$ark_root/libpandafile:libarkfile_static", + "$js_root:libark_jsruntime_test_set", + "$js_root/ecmascript/compiler:libark_jsoptimizer_set", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("CombinedPassVisitorTest") { + module_out_path = module_output_path + + sources = [ + # test file + "combined_pass_visitor_test.cpp", + ] + + deps = [ + "$ark_root/libpandafile:libarkfile_static", + "$js_root:libark_jsruntime_test_set", + "$js_root/ecmascript/compiler:libark_jsoptimizer_set", + sdk_libc_secshared_dep, + ] +} + +host_unittest_action("LoopOptimizationTest") { + module_out_path = module_output_path + + sources = [ + # test file + "loop_optimization_test.cpp", + ] + + deps = [ + "$ark_root/libpandafile:libarkfile_static", + "$js_root:libark_jsruntime_test_set", + "$js_root/ecmascript/compiler:libark_jsoptimizer_set", + sdk_libc_secshared_dep, + ] +} + group("host_unittest") { testonly = true # deps file - deps = [ ":AssemblerTestAction" ] + deps = [ + ":AssemblerTestAction", + ":LoopOptimizationTestAction", + ":TypedArrayLoweringTestAction", + ] } diff --git a/ecmascript/compiler/tests/combined_pass_visitor_test.cpp b/ecmascript/compiler/tests/combined_pass_visitor_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..22543e25f7d56bc3201779ba7c946d0a83979f4d --- /dev/null +++ b/ecmascript/compiler/tests/combined_pass_visitor_test.cpp @@ -0,0 +1,81 @@ +/* + * 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/compiler/combined_pass_visitor.h" +#include "ecmascript/compiler/dead_code_elimination.h" +#include "ecmascript/compiler/early_elimination.h" +#include "ecmascript/compiler/later_elimination.h" +#include "ecmascript/compiler/verifier.h" +#include "ecmascript/tests/test_helper.h" + +namespace panda::test { +class CombinedPassVisitorTests : public testing::Test { +}; +using ecmascript::kungfu::Chunk; +using ecmascript::kungfu::CombinedPassVisitor; +using ecmascript::kungfu::Circuit; +using ecmascript::kungfu::CircuitBuilder; +using ecmascript::kungfu::DeadCodeElimination; +using ecmascript::kungfu::EarlyElimination; +using ecmascript::kungfu::Environment; +using ecmascript::kungfu::OpCode; +using ecmascript::kungfu::GateType; +using ecmascript::kungfu::LaterElimination; +using ecmascript::kungfu::MachineType; +using ecmascript::kungfu::GateAccessor; +using ecmascript::kungfu::GateRef; +using ecmascript::kungfu::Verifier; +using ecmascript::NativeAreaAllocator; + +HWTEST_F_L0(CombinedPassVisitorTests, TSLoweringTestFramework) +{ + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + builder.SetEnvironment(&env); + GateAccessor acc(&circuit); + auto entry = acc.GetStateRoot(); + auto depend = acc.GetDependRoot(); + auto arg0 = builder.Arguments(0); + auto arg1 = builder.Arguments(1); + auto arg2 = builder.Arguments(2); + auto dead = circuit.DeadGate(); + auto icmp = circuit.NewGate(circuit.Icmp(1), MachineType::I1, {arg0, arg1}, GateType::NJSValue()); + auto ifBranch = circuit.NewGate(circuit.IfBranch(0), {entry, icmp}); + circuit.NewGate(circuit.IfTrue(), {ifBranch}); + auto ifFalse = circuit.NewGate(circuit.IfFalse(), {ifBranch}); + auto merge = circuit.NewGate(circuit.Merge(2), {dead, ifFalse}); + auto valueSelector = circuit.NewGate(circuit.ValueSelector(2), {merge, arg0, arg1}); + auto load1 = circuit.NewGate(circuit.LoadElement(2), MachineType::I64, + {merge, depend, valueSelector, arg2}, GateType::AnyType()); + auto load2 = circuit.NewGate(circuit.LoadElement(2), MachineType::I64, + {load1, load1, valueSelector, arg2}, GateType::AnyType()); + auto circuitReturn = circuit.NewGate(circuit.Return(), {load2, load2, load2, circuit.GetReturnRoot()}); + Chunk chunk(&allocator); + CombinedPassVisitor visitor(&circuit, false, "combined pass visitor test", &chunk); + DeadCodeElimination deadCodeElimination(&circuit, &visitor, &chunk); + EarlyElimination earlyElimination(&circuit, &visitor, &chunk); + visitor.AddPass(&deadCodeElimination); + visitor.AddPass(&earlyElimination); + visitor.VisitGraph(); + EXPECT_TRUE(acc.IsNop(merge)); + EXPECT_TRUE(acc.IsNop(valueSelector)); + EXPECT_TRUE(acc.GetValueIn(load1, 0) == arg1); + EXPECT_TRUE(acc.IsNop(load2)); + EXPECT_TRUE(acc.GetState(circuitReturn) == load1); +} + +} \ No newline at end of file diff --git a/ecmascript/compiler/tests/dead_code_elimination_test.cpp b/ecmascript/compiler/tests/dead_code_elimination_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f120fc29718237e9b1917dd4dd0a99fefa17d518 --- /dev/null +++ b/ecmascript/compiler/tests/dead_code_elimination_test.cpp @@ -0,0 +1,138 @@ +/* + * 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/compiler/combined_pass_visitor.h" +#include "ecmascript/compiler/dead_code_elimination.h" +#include "ecmascript/compiler/verifier.h" +#include "ecmascript/tests/test_helper.h" + +namespace panda::test { +class DeadCodeEliminationTests : public testing::Test { +}; +using ecmascript::kungfu::Chunk; +using ecmascript::kungfu::CombinedPassVisitor; +using ecmascript::kungfu::Circuit; +using ecmascript::kungfu::CircuitBuilder; +using ecmascript::kungfu::DeadCodeElimination; +using ecmascript::kungfu::Environment; +using ecmascript::kungfu::OpCode; +using ecmascript::kungfu::GateType; +using ecmascript::kungfu::MachineType; +using ecmascript::kungfu::GateAccessor; +using ecmascript::kungfu::GateRef; +using ecmascript::kungfu::Verifier; +using ecmascript::NativeAreaAllocator; + +HWTEST_F_L0(DeadCodeEliminationTests, CommonEliminationFramework) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + CircuitBuilder builder(&circuit); + GateAccessor acc(&circuit); + auto depend = acc.GetDependRoot(); + auto arg0 = builder.Arguments(0); + auto dead = circuit.DeadGate(); + auto ifBranch = circuit.NewGate(circuit.IfBranch(0), {dead, arg0}); + auto ifTrue = circuit.NewGate(circuit.IfTrue(), {ifBranch}); + auto ifFalse = circuit.NewGate(circuit.IfFalse(), {ifBranch}); + auto trueReturn = circuit.NewGate(circuit.Return(), {ifTrue, depend, arg0, circuit.GetReturnRoot()}); + auto falseReturn = circuit.NewGate(circuit.Return(), {ifFalse, depend, arg0, circuit.GetReturnRoot()}); + Chunk chunk(&allocator); + CombinedPassVisitor visitor(&circuit, false, "dead code elimination", &chunk); + DeadCodeElimination elimination(&circuit, &visitor, &chunk); + visitor.AddPass(&elimination); + visitor.VisitGraph(); + EXPECT_TRUE(acc.IsNop(ifBranch)); + EXPECT_TRUE(acc.IsNop(ifTrue)); + EXPECT_TRUE(acc.IsNop(ifFalse)); + EXPECT_TRUE(acc.IsNop(trueReturn)); + EXPECT_TRUE(acc.IsNop(falseReturn)); +} + +HWTEST_F_L0(DeadCodeEliminationTests, MergeEliminationFramework) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + builder.SetEnvironment(&env); + GateAccessor acc(&circuit); + auto entry = acc.GetStateRoot(); + auto depend = acc.GetDependRoot(); + auto arg0 = builder.Arguments(0); + auto arg1 = builder.Arguments(1); + auto arg2 = builder.Arguments(2); + auto dead = circuit.DeadGate(); + auto icmp = circuit.NewGate(circuit.Icmp(1), MachineType::I1, {arg0, arg1}, GateType::NJSValue()); + auto ifBranch = circuit.NewGate(circuit.IfBranch(0), {entry, icmp}); + circuit.NewGate(circuit.IfTrue(), {ifBranch}); + auto ifFalse = circuit.NewGate(circuit.IfFalse(), {ifBranch}); + auto merge = circuit.NewGate(circuit.Merge(2), {dead, ifFalse}); + auto valueSelector = circuit.NewGate(circuit.ValueSelector(2), {merge, arg1, arg2}); + auto circuitReturn = circuit.NewGate(circuit.Return(), {merge, depend, valueSelector, circuit.GetReturnRoot()}); + Chunk chunk(&allocator); + CombinedPassVisitor visitor(&circuit, false, "dead code elimination", &chunk); + DeadCodeElimination elimination(&circuit, &visitor, &chunk); + visitor.AddPass(&elimination); + visitor.VisitGraph(); + EXPECT_TRUE(acc.IsNop(merge)); + EXPECT_TRUE(acc.IsNop(valueSelector)); + EXPECT_TRUE(acc.GetValueIn(circuitReturn) == arg2); +} + +HWTEST_F_L0(DeadCodeEliminationTests, MergeInputEliminationFramework) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + builder.SetEnvironment(&env); + GateAccessor acc(&circuit); + auto entry = acc.GetStateRoot(); + auto depend = acc.GetDependRoot(); + auto arg0 = builder.Arguments(0); + auto arg1 = builder.Arguments(1); + auto arg2 = builder.Arguments(2); + auto dead = circuit.DeadGate(); + auto icmp = circuit.NewGate(circuit.Icmp(1), MachineType::I1, {arg0, arg1}, GateType::NJSValue()); + auto ifBranch = circuit.NewGate(circuit.IfBranch(0), {entry, icmp}); + auto ifTrue = circuit.NewGate(circuit.IfTrue(), {ifBranch}); + auto ifFalse = circuit.NewGate(circuit.IfFalse(), {ifBranch}); + auto ifBranch2 = circuit.NewGate(circuit.IfBranch(0), {ifTrue, icmp}); + auto ifTrue2 = circuit.NewGate(circuit.IfTrue(), {ifBranch2}); + circuit.NewGate(circuit.IfFalse(), {ifBranch2}); + auto merge = circuit.NewGate(circuit.Merge(3), {ifTrue2, dead, ifFalse}); + auto valueSelector = circuit.NewGate(circuit.ValueSelector(3), {merge, arg0, arg1, arg2}); + auto circuitReturn = circuit.NewGate(circuit.Return(), {merge, depend, valueSelector, circuit.GetReturnRoot()}); + Chunk chunk(&allocator); + CombinedPassVisitor visitor(&circuit, false, "dead code elimination", &chunk); + DeadCodeElimination elimination(&circuit, &visitor, &chunk); + visitor.AddPass(&elimination); + visitor.VisitGraph(); + EXPECT_TRUE(acc.GetOpCode(merge) == OpCode::MERGE); + EXPECT_TRUE(acc.GetOpCode(valueSelector) == OpCode::VALUE_SELECTOR); + EXPECT_TRUE(acc.GetValueIn(circuitReturn) == valueSelector); + EXPECT_TRUE(acc.GetNumIns(merge) == 2); + EXPECT_TRUE(acc.GetNumIns(valueSelector) == 3); + EXPECT_TRUE(acc.GetIn(merge, 0) == ifTrue2); + EXPECT_TRUE(acc.GetIn(merge, 1) == ifFalse); + EXPECT_TRUE(acc.GetIn(valueSelector, 1) == arg0); + EXPECT_TRUE(acc.GetIn(valueSelector, 2) == arg2); +} + +} \ No newline at end of file diff --git a/ecmascript/compiler/tests/loop_optimization_test.cpp b/ecmascript/compiler/tests/loop_optimization_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..854450e36ce8232b18fccb00d56033bd37db8d1f --- /dev/null +++ b/ecmascript/compiler/tests/loop_optimization_test.cpp @@ -0,0 +1,232 @@ +/* + * 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/compiler/bytecodes.h" +#include "ecmascript/compiler/circuit_builder.h" +#include "ecmascript/compiler/early_elimination.h" +#include "ecmascript/compiler/gate_accessor.h" +#include "ecmascript/compiler/graph_editor.h" +#include "ecmascript/compiler/loop_analysis.h" +#include "ecmascript/compiler/loop_peeling.h" +#include "ecmascript/compiler/pass.h" +#include "ecmascript/compiler/stub_builder.h" +#include "ecmascript/compiler/type.h" +#include "ecmascript/compiler/variable_type.h" +#include "ecmascript/compiler/verifier.h" +#include "ecmascript/compiler/ts_hcr_lowering.h" +#include "ecmascript/compiler/type_mcr_lowering.h" +#include "ecmascript/mem/chunk.h" +#include "ecmascript/mem/native_area_allocator.h" +#include "ecmascript/tests/test_helper.h" +#include "gtest/gtest-death-test.h" +#include "gtest/gtest.h" + +namespace panda::test { +class LoopOptimizationTest : public testing::Test { +}; +using ecmascript::kungfu::Circuit; +using ecmascript::kungfu::GateAccessor; +using ecmascript::kungfu::GateType; +using ecmascript::kungfu::MachineType; +using ecmascript::kungfu::CircuitBuilder; +using ecmascript::kungfu::Label; +using ecmascript::kungfu::OpCode; +using ecmascript::kungfu::GateRef; +using ecmascript::kungfu::Variable; +using ecmascript::kungfu::VariableType; +using ecmascript::kungfu::Verifier; +using ecmascript::kungfu::LoopAnalysis; +using ecmascript::kungfu::Environment; +using ecmascript::kungfu::LoopPeeling; +using ecmascript::kungfu::EarlyElimination; +using ecmascript::kungfu::CombinedPassVisitor; +using ecmascript::kungfu::TypedBinOp; +using ecmascript::kungfu::PGOSampleType; +using ecmascript::kungfu::GraphLinearizer; +HWTEST_F_L0(LoopOptimizationTest, LoopInt32TypedArraySumOptimizationTest) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + ecmascript::Chunk chunk(&allocator); + GateAccessor acc(&circuit); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + // after number speculative runner + builder.SetEnvironment(&env); + auto array = builder.Arguments(1); + + DEFVAlUE(index, (&builder), VariableType::INT32(), builder.Int32(0)); + DEFVAlUE(sum, (&builder), VariableType::INT32(), builder.Int32(0)); + + Label loopHead(&env); + Label loopBody(&env); + Label loopExit(&env); + builder.Jump(&loopHead); + builder.LoopBegin(&loopHead); + auto loopBegin = builder.GetState(); + EXPECT_TRUE(acc.IsLoopHead(loopBegin)); + auto loadLength = builder.LoadTypedArrayLength(GateType::AnyType(), array); + acc.SetMachineType(loadLength, MachineType::I32); + acc.SetGateType(loadLength, GateType::NJSValue()); + auto cmp = builder.TypedBinaryOp( + *index, loadLength, GateType::IntType(), GateType::IntType(), + GateType::NJSValue(), PGOSampleType::NoneType()); + acc.SetMachineType(cmp, MachineType::I1); + builder.Branch(cmp, &loopBody, &loopExit); + builder.Bind(&loopBody); + auto loadElement = builder.LoadElement(array, *index); + acc.SetMachineType(loadElement, MachineType::I32); + acc.SetGateType(loadElement, GateType::NJSValue()); + auto sumAdd = builder.TypedBinaryOp( + *sum, loadElement, GateType::IntType(), GateType::IntType(), + GateType::NJSValue(), PGOSampleType::NoneType()); + acc.SetMachineType(sumAdd, MachineType::I32); + sum = sumAdd; + auto indexInc = builder.TypedBinaryOp( + *index, builder.Int32(1), GateType::IntType(), GateType::IntType(), + GateType::NJSValue(), PGOSampleType::NoneType()); + acc.SetMachineType(indexInc, MachineType::I32); + index = indexInc; + builder.LoopEnd(&loopHead); + builder.Bind(&loopExit); + builder.LoopExit({&sum}); + auto convert = builder.ConvertInt32ToTaggedInt(*sum); + builder.Return(convert); + LoopAnalysis analysis(&circuit, &chunk); + ecmascript::kungfu::LoopInfo beforeOpt(&chunk, loopBegin); + ecmascript::kungfu::LoopInfo afterOpt(&chunk, loopBegin); + analysis.CollectLoopBody(&beforeOpt); + bool foundLengthBeforeOpt = false; + for (auto gate : beforeOpt.loopBodys) { + if (acc.GetOpCode(gate) == OpCode::LOAD_TYPED_ARRAY_LENGTH) { + foundLengthBeforeOpt = true; + } + } + EXPECT_TRUE(foundLengthBeforeOpt); + analysis.PrintLoop(&beforeOpt); + LoopPeeling(nullptr, &circuit, false, "LoopInt32TypedArraySumOptimizationTest", &chunk, &beforeOpt).Peel(); + EXPECT_TRUE(Verifier::Run(&circuit)); + CombinedPassVisitor visitor(&circuit, false, "LoopInt32TypedArraySumOptimizationTest", &chunk); + EarlyElimination earlyElimination(&circuit, &visitor, &chunk); + visitor.AddPass(&earlyElimination); + visitor.VisitGraph(); + analysis.CollectLoopBody(&afterOpt); + EXPECT_TRUE(Verifier::Run(&circuit)); + EXPECT_TRUE(beforeOpt.loopBodys.size() > afterOpt.loopBodys.size()); + bool foundLengthAfterOpt = false; + for (auto gate : afterOpt.loopBodys) { + if (acc.GetOpCode(gate) == OpCode::LOAD_TYPED_ARRAY_LENGTH) { + foundLengthAfterOpt = true; + } + } + EXPECT_FALSE(foundLengthAfterOpt); +} + +HWTEST_F_L0(LoopOptimizationTest, LoopNumberCalculationOptimizationTest) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + ecmascript::Chunk chunk(&allocator); + GateAccessor acc(&circuit); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + // after slowpath lowering + builder.SetEnvironment(&env); + auto arg = builder.Arguments(1); + acc.SetMachineType(arg, MachineType::I32); + + DEFVAlUE(index, (&builder), VariableType::INT32(), builder.Int32(0)); + DEFVAlUE(sum, (&builder), VariableType::INT32(), builder.Int32(0)); + + Label loopHead(&env); + Label loopBody(&env); + Label loopExit(&env); + builder.Jump(&loopHead); + builder.LoopBegin(&loopHead); + auto loopBegin = builder.GetState(); + auto loopEntry = acc.GetState(loopBegin); + auto invariant = builder.Int32Mul(arg, builder.Int32(5)); + builder.Branch(builder.Int32LessThan(*index, invariant), &loopBody, &loopExit); + builder.Bind(&loopBody); + auto variant = builder.Int32Add(*sum, builder.Int32(2)); + sum = variant; + index = builder.Int32Add(*index, builder.Int32(1)); + builder.LoopEnd(&loopHead); + builder.Bind(&loopExit); + builder.Return(builder.ConvertInt32ToTaggedInt(*sum)); + EXPECT_TRUE(Verifier::Run(&circuit)); + std::vector> cfg; + auto linearizer = GraphLinearizer(&circuit, false, "LoopNumberCalculationOptimizationTest", &chunk, false, false); + linearizer.Run(cfg); + EXPECT_EQ(acc.GetOpCode(linearizer.GetStateOfSchedulableGate(invariant)), OpCode::IF_BRANCH); + EXPECT_EQ(acc.GetOpCode(linearizer.GetStateOfSchedulableGate(variant)), OpCode::LOOP_BACK); + std::vector> cfg2; + auto linearizer2 = GraphLinearizer(&circuit, false, "LoopNumberCalculationOptimizationTest", &chunk, false, true); + linearizer2.Run(cfg2); + EXPECT_EQ(linearizer2.GetStateOfSchedulableGate(invariant), loopEntry); + EXPECT_EQ(acc.GetOpCode(linearizer2.GetStateOfSchedulableGate(variant)), OpCode::LOOP_BACK); +} + +HWTEST_F_L0(LoopOptimizationTest, LoopLoadConstOptimizationTest) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + ecmascript::Chunk chunk(&allocator); + GateAccessor acc(&circuit); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + // after slowpath lowering + builder.SetEnvironment(&env); + auto arg1 = builder.Arguments(1); + acc.SetGateType(arg1, GateType::TaggedPointer()); + auto arg2 = builder.Arguments(2); + acc.SetMachineType(arg2, MachineType::ARCH); + GateRef invariant = circuit.NewGate(circuit.Load(), MachineType::I32, + { circuit.GetDependRoot(), arg2 }, GateType::NJSValue()); + + DEFVAlUE(index, (&builder), VariableType::INT32(), builder.Int32(0)); + DEFVAlUE(sum, (&builder), VariableType::INT32(), builder.Int32(0)); + + Label loopHead(&env); + Label loopBody(&env); + Label loopExit(&env); + builder.Jump(&loopHead); + builder.LoopBegin(&loopHead); + auto loopBegin = builder.GetState(); + auto loopEntry = acc.GetState(loopBegin); + + builder.Branch(builder.Int32LessThan(*index, invariant), &loopBody, &loopExit); + builder.Bind(&loopBody); + auto variant = builder.Load(VariableType::INT32(), arg1, builder.PtrAdd(arg2, *index)); + sum = builder.Int32Add(*sum, variant); + index = builder.Int32Add(*index, builder.Int32(1)); + builder.LoopEnd(&loopHead); + builder.Bind(&loopExit); + builder.Return(builder.ConvertInt32ToTaggedInt(*sum)); + EXPECT_TRUE(Verifier::Run(&circuit)); + std::vector> cfg; + auto linearizer = GraphLinearizer(&circuit, false, "LoopNumberCalculationOptimizationTest", &chunk, false, false); + linearizer.Run(cfg); + EXPECT_EQ(acc.GetOpCode(linearizer.GetStateOfSchedulableGate(invariant)), OpCode::IF_BRANCH); + EXPECT_EQ(acc.GetOpCode(linearizer.GetStateOfSchedulableGate(variant)), OpCode::LOOP_BACK); + std::vector> cfg2; + auto linearizer2 = GraphLinearizer(&circuit, false, "LoopNumberCalculationOptimizationTest", &chunk, false, true); + linearizer2.Run(cfg2); + EXPECT_EQ(linearizer2.GetStateOfSchedulableGate(invariant), loopEntry); + EXPECT_EQ(acc.GetOpCode(linearizer2.GetStateOfSchedulableGate(variant)), OpCode::LOOP_BACK); +} +} // namespace panda::test \ No newline at end of file diff --git a/ecmascript/compiler/tests/lowering_relate_gate_test.cpp b/ecmascript/compiler/tests/lowering_relate_gate_test.cpp deleted file mode 100644 index c34c85333722cb8e82594605c639b07c35482a0a..0000000000000000000000000000000000000000 --- a/ecmascript/compiler/tests/lowering_relate_gate_test.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (c) 2022 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "ecmascript/compiler/gate_accessor.h" -#include "ecmascript/compiler/verifier.h" -#include "ecmascript/compiler/ts_hcr_lowering.h" -#include "ecmascript/compiler/type_mcr_lowering.h" -#include "ecmascript/mem/native_area_allocator.h" -#include "ecmascript/pgo_profiler/pgo_profiler_type.h" -#include "ecmascript/tests/test_helper.h" - -namespace panda::test { -class LoweringRelateGateTests : public testing::Test { -}; -using ecmascript::GlobalEnvConstants; -using ecmascript::ConstantIndex; -using ecmascript::RegionSpaceFlag; -using ecmascript::kungfu::Circuit; -using ecmascript::kungfu::GateAccessor; -using ecmascript::kungfu::OpCode; -using ecmascript::kungfu::GateType; -using ecmascript::kungfu::MachineType; -using ecmascript::kungfu::CircuitBuilder; -using ecmascript::kungfu::Verifier; -using ecmascript::kungfu::TypedBinOp; -using ecmascript::kungfu::Label; -using ecmascript::kungfu::Variable; -using ecmascript::kungfu::VariableType; -using ecmascript::kungfu::Environment; -using ecmascript::kungfu::TypeMCRLowering; -using ecmascript::kungfu::TSHCRLowering; -using ecmascript::kungfu::CompilationConfig; - -HWTEST_F_L0(LoweringRelateGateTests, TypeCheckFramework) -{ - // construct a circuit - ecmascript::NativeAreaAllocator allocator; - Circuit circuit(&allocator); - CircuitBuilder builder(&circuit); - GateAccessor acc(&circuit); - Environment env(1, &builder); - builder.SetEnvironment(&env); - auto depend = acc.GetDependRoot(); - auto state = acc.GetStateRoot(); - auto arg0 = builder.Arguments(0); - auto pcGate = circuit.GetConstantGate(MachineType::I64, 0, GateType::NJSValue()); - auto frameArgs = circuit.NewGate( - circuit.FrameArgs(), {builder.Arguments(3), builder.Arguments(4), builder.Arguments(5), builder.Arguments(2)}); - auto frameState = circuit.NewGate(circuit.FrameState(1), {pcGate, frameArgs}); - auto stateSplit = circuit.NewGate(circuit.StateSplit(), {state, depend, frameState}); - builder.SetDepend(stateSplit); - auto check = builder.TryPrimitiveTypeCheck(GateType::NumberType(), arg0); - builder.ReturnVoid(check, depend); - - CompilationConfig config(TARGET_X64); - TypeMCRLowering typeMCRLowering(&circuit, &config, nullptr, false, "TypeCheckFramework"); - typeMCRLowering.RunTypeMCRLowering(); - EXPECT_TRUE(Verifier::Run(&circuit)); -} - -HWTEST_F_L0(LoweringRelateGateTests, TypeConvertFramework) -{ - // construct a circuit - ecmascript::NativeAreaAllocator allocator; - Circuit circuit(&allocator); - CircuitBuilder builder(&circuit); - GateAccessor acc(&circuit); - auto entry = acc.GetStateRoot(); - auto depend = acc.GetDependRoot(); - auto arg0 = builder.Arguments(0); - auto convert = builder.TypeConvert(MachineType::I64, GateType::NJSValue(), GateType::NumberType(), - {entry, depend, arg0}); - builder.Return(convert, convert, convert); - EXPECT_TRUE(Verifier::Run(&circuit)); - CompilationConfig config(TARGET_X64); - TypeMCRLowering typeMCRLowering(&circuit, &config, nullptr, false, "TypeConvertFramework"); - typeMCRLowering.RunTypeMCRLowering(); - EXPECT_TRUE(Verifier::Run(&circuit)); -} - -HWTEST_F_L0(LoweringRelateGateTests, HeapAllocTest) -{ - // construct a circuit - ecmascript::NativeAreaAllocator allocator; - Circuit circuit(&allocator); - CircuitBuilder builder(&circuit); - Environment env(0, &builder); - builder.SetEnvironment(&env); - auto glue = builder.Arguments(0); - auto arg0 = builder.Arguments(1); - auto arg1 = builder.Arguments(2); - auto array = builder.HeapAlloc(arg0, GateType::AnyType(), RegionSpaceFlag::IN_YOUNG_SPACE); - - auto offset = builder.Int64(JSThread::GlueData::GetGlobalConstOffset(false)); - auto globalEnv = builder.Load(VariableType::JS_POINTER(), glue, offset); - auto lenthOffset = builder.IntPtr(GlobalEnvConstants::GetOffsetOfLengthString()); - auto lengthString = builder.Load(VariableType::JS_POINTER(), globalEnv, lenthOffset); - - builder.Store(VariableType::JS_POINTER(), glue, array, builder.IntPtr(0), arg1); - builder.StoreElement(array, builder.IntPtr(0), - builder.ToTaggedInt(builder.Int64(0)), builder.Undefined()); - builder.StoreElement(array, builder.IntPtr(1), - builder.ToTaggedInt(builder.Int64(1)), builder.Undefined()); - builder.StoreProperty(array, lengthString, builder.ToTaggedInt(builder.Int64(2))); - auto length = builder.LoadProperty(array, lengthString, false); - Label less2(&builder); - Label notLess2(&builder); - auto condtion = builder.TaggedIsTrue(builder.TypedBinaryOp(length, - builder.ToTaggedInt(builder.Int64(2))), GateType::NumberType(), GateType::NumberType(), - GateType::AnyType(), PGOSampleType::NoneType()); - builder.Branch(condtion, &less2, ¬Less2); - builder.Bind(&less2); - auto ret = builder.LoadElement(array, builder.IntPtr(1), - builder.Undefined()); - builder.Return(ret); - builder.Bind(¬Less2); - builder.Return(builder.Int64(-1)); - EXPECT_TRUE(Verifier::Run(&circuit)); -} -} // namespace panda::test diff --git a/ecmascript/compiler/tests/typed_array_lowering_test.cpp b/ecmascript/compiler/tests/typed_array_lowering_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9ba63a5e3afb18c593209aca7b905e954e13d966 --- /dev/null +++ b/ecmascript/compiler/tests/typed_array_lowering_test.cpp @@ -0,0 +1,598 @@ +/* + * 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/compiler/gate_accessor.h" +#include "ecmascript/compiler/type.h" +#include "ecmascript/compiler/verifier.h" +#include "ecmascript/compiler/ts_hcr_lowering.h" +#include "ecmascript/compiler/type_mcr_lowering.h" +#include "ecmascript/mem/chunk.h" +#include "ecmascript/mem/native_area_allocator.h" +#include "ecmascript/tests/test_helper.h" +#include "gtest/gtest-death-test.h" +#include "gtest/gtest.h" + +namespace panda::test { +class TypedArrayLoweringTests : public testing::Test { +}; +using ecmascript::kungfu::Circuit; +using ecmascript::kungfu::GateAccessor; +using ecmascript::kungfu::OpCode; +using ecmascript::kungfu::GateType; +using ecmascript::kungfu::MachineType; +using ecmascript::kungfu::CircuitBuilder; +using ecmascript::kungfu::Verifier; +using ecmascript::kungfu::Environment; +using ecmascript::kungfu::CombinedPassVisitor; +using ecmascript::kungfu::TypeMCRLowering; +HWTEST_F_L0(TypedArrayLoweringTests, LoadTypedArrayLength) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + ecmascript::Chunk chunk(&allocator); + GateAccessor acc(&circuit); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + // after number speculative runner + builder.SetEnvironment(&env); + auto array = builder.Arguments(1); + auto loadLength = builder.LoadTypedArrayLength(GateType::AnyType(), array); + acc.SetMachineType(loadLength, MachineType::I32); + acc.SetGateType(loadLength, GateType::NJSValue()); + auto convert = builder.ConvertInt32ToTaggedInt(loadLength); + builder.Return(convert); + EXPECT_TRUE(Verifier::Run(&circuit)); + CombinedPassVisitor visitor(&circuit, false, "LoadTypedArrayLength", &chunk); + TypeMCRLowering lowering(&circuit, &visitor, nullptr, nullptr, &chunk, false); + visitor.AddPass(&lowering); + visitor.VisitGraph(); + EXPECT_TRUE(Verifier::Run(&circuit)); + EXPECT_EQ(acc.GetOpCode(loadLength), OpCode::NOP); + auto result = acc.GetValueIn(convert, 0); + EXPECT_EQ(acc.GetOpCode(result), OpCode::LOAD_CONST_OFFSET); + EXPECT_EQ(acc.GetMachineType(result), MachineType::I32); +} + +HWTEST_F_L0(TypedArrayLoweringTests, Int32ArrayLoadElement) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + ecmascript::Chunk chunk(&allocator); + GateAccessor acc(&circuit); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + // after number speculative runner + builder.SetEnvironment(&env); + auto array = builder.Arguments(1); + auto index = builder.Int32(1); + auto loadElement = builder.LoadElement(array, index); + acc.SetMachineType(loadElement, MachineType::I32); + acc.SetGateType(loadElement, GateType::NJSValue()); + auto convert = builder.ConvertInt32ToTaggedInt(loadElement); + builder.Return(convert); + EXPECT_TRUE(Verifier::Run(&circuit)); + CombinedPassVisitor visitor(&circuit, false, "Int32ArrayLoadElement", &chunk); + TypeMCRLowering lowering(&circuit, &visitor, nullptr, nullptr, &chunk, false); + visitor.AddPass(&lowering); + visitor.VisitGraph(); + EXPECT_TRUE(Verifier::Run(&circuit)); + EXPECT_EQ(acc.GetOpCode(loadElement), OpCode::NOP); + auto result = acc.GetValueIn(convert, 0); + EXPECT_EQ(acc.GetOpCode(result), OpCode::VALUE_SELECTOR); + EXPECT_EQ(acc.GetMachineType(result), MachineType::I32); + auto numIn = acc.GetNumValueIn(result); + EXPECT_EQ(numIn, 2); + for (size_t i = 0; i < numIn; ++i) { + EXPECT_EQ(acc.GetOpCode(acc.GetValueIn(result, i)), OpCode::LOAD); + } +} + +HWTEST_F_L0(TypedArrayLoweringTests, Int32OnHeapArrayLoadElement) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + ecmascript::Chunk chunk(&allocator); + GateAccessor acc(&circuit); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + // after number speculative runner + builder.SetEnvironment(&env); + auto array = builder.Arguments(1); + acc.SetGateType(array, GateType::AnyType()); + auto index = builder.Int32(1); + auto loadElement = builder.LoadElement(array, index); + acc.SetMachineType(loadElement, MachineType::I32); + acc.SetGateType(loadElement, GateType::NJSValue()); + auto convert = builder.ConvertInt32ToTaggedInt(loadElement); + builder.Return(convert); + EXPECT_TRUE(Verifier::Run(&circuit)); + CombinedPassVisitor visitor(&circuit, false, "Int32OnHeapArrayLoadElement", &chunk); + TypeMCRLowering lowering(&circuit, &visitor, nullptr, nullptr, &chunk, true); + visitor.AddPass(&lowering); + visitor.VisitGraph(); + EXPECT_TRUE(Verifier::Run(&circuit)); + EXPECT_EQ(acc.GetOpCode(loadElement), OpCode::NOP); + auto result = acc.GetValueIn(convert, 0); + EXPECT_EQ(acc.GetOpCode(result), OpCode::LOAD); + EXPECT_EQ(acc.GetMachineType(result), MachineType::I32); +} + +HWTEST_F_L0(TypedArrayLoweringTests, Float64OnHeapArrayLoadElement) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + ecmascript::Chunk chunk(&allocator); + GateAccessor acc(&circuit); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + // after number speculative runner + builder.SetEnvironment(&env); + auto array = builder.Arguments(1); + acc.SetGateType(array, GateType::AnyType()); + auto index = builder.Int32(1); + auto loadElement = builder.LoadElement(array, index); + acc.SetMachineType(loadElement, MachineType::I32); + acc.SetGateType(loadElement, GateType::NJSValue()); + auto convert = builder.ConvertInt32ToTaggedInt(loadElement); + builder.Return(convert); + EXPECT_TRUE(Verifier::Run(&circuit)); + CombinedPassVisitor visitor(&circuit, false, "Float64OnHeapArrayLoadElement", &chunk); + TypeMCRLowering lowering(&circuit, &visitor, nullptr, nullptr, &chunk, true); + visitor.AddPass(&lowering); + visitor.VisitGraph(); + EXPECT_TRUE(Verifier::Run(&circuit)); + EXPECT_EQ(acc.GetOpCode(loadElement), OpCode::NOP); + auto result = acc.GetValueIn(convert, 0); + EXPECT_EQ(acc.GetOpCode(result), OpCode::LOAD); + EXPECT_EQ(acc.GetMachineType(result), MachineType::F64); +} + +HWTEST_F_L0(TypedArrayLoweringTests, FLOAT32OnHeapArrayLoadElement) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + ecmascript::Chunk chunk(&allocator); + GateAccessor acc(&circuit); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + // after number speculative runner + builder.SetEnvironment(&env); + auto array = builder.Arguments(1); + acc.SetGateType(array, GateType::AnyType()); + auto index = builder.Int32(1); + auto loadElement = builder.LoadElement(array, index); + acc.SetMachineType(loadElement, MachineType::I32); + acc.SetGateType(loadElement, GateType::NJSValue()); + auto convert = builder.ConvertInt32ToTaggedInt(loadElement); + builder.Return(convert); + EXPECT_TRUE(Verifier::Run(&circuit)); + CombinedPassVisitor visitor(&circuit, false, "FLOAT32OnHeapArrayLoadElement", &chunk); + TypeMCRLowering lowering(&circuit, &visitor, nullptr, nullptr, &chunk, true); + visitor.AddPass(&lowering); + visitor.VisitGraph(); + EXPECT_TRUE(Verifier::Run(&circuit)); + EXPECT_EQ(acc.GetOpCode(loadElement), OpCode::NOP); + auto result = acc.GetValueIn(convert, 0); + EXPECT_EQ(acc.GetOpCode(result), OpCode::FEXT); + EXPECT_EQ(acc.GetMachineType(result), MachineType::F64); + auto load = acc.GetValueIn(result, 0); + EXPECT_EQ(acc.GetOpCode(load), OpCode::LOAD); + EXPECT_EQ(acc.GetMachineType(load), MachineType::F32); +} + +HWTEST_F_L0(TypedArrayLoweringTests, Int8OnHeapArrayLoadElement) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + ecmascript::Chunk chunk(&allocator); + GateAccessor acc(&circuit); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + // after number speculative runner + builder.SetEnvironment(&env); + auto array = builder.Arguments(1); + acc.SetGateType(array, GateType::AnyType()); + auto index = builder.Int32(1); + auto loadElement = builder.LoadElement(array, index); + acc.SetMachineType(loadElement, MachineType::I32); + acc.SetGateType(loadElement, GateType::NJSValue()); + auto convert = builder.ConvertInt32ToTaggedInt(loadElement); + builder.Return(convert); + EXPECT_TRUE(Verifier::Run(&circuit)); + CombinedPassVisitor visitor(&circuit, false, "Int8OnHeapArrayLoadElement", &chunk); + TypeMCRLowering lowering(&circuit, &visitor, nullptr, nullptr, &chunk, true); + visitor.AddPass(&lowering); + visitor.VisitGraph(); + EXPECT_TRUE(Verifier::Run(&circuit)); + EXPECT_EQ(acc.GetOpCode(loadElement), OpCode::NOP); + auto result = acc.GetValueIn(convert, 0); + EXPECT_EQ(acc.GetOpCode(result), OpCode::SEXT); + EXPECT_EQ(acc.GetMachineType(result), MachineType::I32); + auto load = acc.GetValueIn(result, 0); + EXPECT_EQ(acc.GetOpCode(load), OpCode::LOAD); + EXPECT_EQ(acc.GetMachineType(load), MachineType::I8); +} + +HWTEST_F_L0(TypedArrayLoweringTests, UInt8OnHeapArrayLoadElement) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + ecmascript::Chunk chunk(&allocator); + GateAccessor acc(&circuit); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + // after number speculative runner + builder.SetEnvironment(&env); + auto array = builder.Arguments(1); + acc.SetGateType(array, GateType::AnyType()); + auto index = builder.Int32(1); + auto loadElement = builder.LoadElement(array, index); + acc.SetMachineType(loadElement, MachineType::I32); + acc.SetGateType(loadElement, GateType::NJSValue()); + auto convert = builder.ConvertInt32ToTaggedInt(loadElement); + builder.Return(convert); + EXPECT_TRUE(Verifier::Run(&circuit)); + CombinedPassVisitor visitor(&circuit, false, "UInt8OnHeapArrayLoadElement", &chunk); + TypeMCRLowering lowering(&circuit, &visitor, nullptr, nullptr, &chunk, true); + visitor.AddPass(&lowering); + visitor.VisitGraph(); + EXPECT_TRUE(Verifier::Run(&circuit)); + EXPECT_EQ(acc.GetOpCode(loadElement), OpCode::NOP); + auto result = acc.GetValueIn(convert, 0); + EXPECT_EQ(acc.GetOpCode(result), OpCode::ZEXT); + EXPECT_EQ(acc.GetMachineType(result), MachineType::I32); + auto load = acc.GetValueIn(result, 0); + EXPECT_EQ(acc.GetOpCode(load), OpCode::LOAD); + EXPECT_EQ(acc.GetMachineType(load), MachineType::I8); +} + +HWTEST_F_L0(TypedArrayLoweringTests, Int16OnHeapArrayLoadElement) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + ecmascript::Chunk chunk(&allocator); + GateAccessor acc(&circuit); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + // after number speculative runner + builder.SetEnvironment(&env); + auto array = builder.Arguments(1); + acc.SetGateType(array, GateType::AnyType()); + auto index = builder.Int32(1); + auto loadElement = builder.LoadElement(array, index); + acc.SetMachineType(loadElement, MachineType::I32); + acc.SetGateType(loadElement, GateType::NJSValue()); + auto convert = builder.ConvertInt32ToTaggedInt(loadElement); + builder.Return(convert); + EXPECT_TRUE(Verifier::Run(&circuit)); + CombinedPassVisitor visitor(&circuit, false, "Int16OnHeapArrayLoadElement", &chunk); + TypeMCRLowering lowering(&circuit, &visitor, nullptr, nullptr, &chunk, true); + visitor.AddPass(&lowering); + visitor.VisitGraph(); + EXPECT_TRUE(Verifier::Run(&circuit)); + EXPECT_EQ(acc.GetOpCode(loadElement), OpCode::NOP); + auto result = acc.GetValueIn(convert, 0); + EXPECT_EQ(acc.GetOpCode(result), OpCode::SEXT); + EXPECT_EQ(acc.GetMachineType(result), MachineType::I32); + auto load = acc.GetValueIn(result, 0); + EXPECT_EQ(acc.GetOpCode(load), OpCode::LOAD); + EXPECT_EQ(acc.GetMachineType(load), MachineType::I16); +} + +HWTEST_F_L0(TypedArrayLoweringTests, UInt16OnHeapArrayLoadElement) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + ecmascript::Chunk chunk(&allocator); + GateAccessor acc(&circuit); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + // after number speculative runner + builder.SetEnvironment(&env); + auto array = builder.Arguments(1); + acc.SetGateType(array, GateType::AnyType()); + auto index = builder.Int32(1); + auto loadElement = builder.LoadElement(array, index); + acc.SetMachineType(loadElement, MachineType::I32); + acc.SetGateType(loadElement, GateType::NJSValue()); + auto convert = builder.ConvertInt32ToTaggedInt(loadElement); + builder.Return(convert); + EXPECT_TRUE(Verifier::Run(&circuit)); + CombinedPassVisitor visitor(&circuit, false, "UInt16OnHeapArrayLoadElement", &chunk); + TypeMCRLowering lowering(&circuit, &visitor, nullptr, nullptr, &chunk, true); + visitor.AddPass(&lowering); + visitor.VisitGraph(); + EXPECT_TRUE(Verifier::Run(&circuit)); + EXPECT_EQ(acc.GetOpCode(loadElement), OpCode::NOP); + auto result = acc.GetValueIn(convert, 0); + EXPECT_EQ(acc.GetOpCode(result), OpCode::ZEXT); + EXPECT_EQ(acc.GetMachineType(result), MachineType::I32); + auto load = acc.GetValueIn(result, 0); + EXPECT_EQ(acc.GetOpCode(load), OpCode::LOAD); + EXPECT_EQ(acc.GetMachineType(load), MachineType::I16); +} + + +HWTEST_F_L0(TypedArrayLoweringTests, Int32ArrayStoreElement) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + ecmascript::Chunk chunk(&allocator); + GateAccessor acc(&circuit); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + // after number speculative runner + builder.SetEnvironment(&env); + auto array = builder.Arguments(1); + acc.SetGateType(array, GateType::AnyType()); + auto index = builder.Int32(1); + auto value = builder.Int32(2); + auto storeElement = + builder.StoreElement(array, index, value); + auto ret = builder.Return(builder.Undefined()); + EXPECT_TRUE(Verifier::Run(&circuit)); + CombinedPassVisitor visitor(&circuit, false, "Int32ArrayStoreElement", &chunk); + TypeMCRLowering lowering(&circuit, &visitor, nullptr, nullptr, &chunk, false); + visitor.AddPass(&lowering); + visitor.VisitGraph(); + EXPECT_TRUE(Verifier::Run(&circuit)); + EXPECT_EQ(acc.GetOpCode(storeElement), OpCode::NOP); + auto result = acc.GetDep(ret); + EXPECT_EQ(acc.GetOpCode(result), OpCode::DEPEND_SELECTOR); + auto numIn = acc.GetDependCount(result); + EXPECT_EQ(numIn, 2); + for (size_t i = 0; i < numIn; ++i) { + auto input = acc.GetDep(result, i); + EXPECT_EQ(acc.GetOpCode(input), OpCode::STORE_MEMORY); + EXPECT_EQ(value, acc.GetValueIn(input, 2)); + } +} + +HWTEST_F_L0(TypedArrayLoweringTests, Int32OnHeapArrayStoreElement) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + ecmascript::Chunk chunk(&allocator); + GateAccessor acc(&circuit); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + // after number speculative runner + builder.SetEnvironment(&env); + auto array = builder.Arguments(1); + acc.SetGateType(array, GateType::AnyType()); + auto index = builder.Int32(1); + auto value = builder.Int32(2); + auto storeElement = + builder.StoreElement(array, index, value); + auto ret = builder.Return(builder.Undefined()); + EXPECT_TRUE(Verifier::Run(&circuit)); + CombinedPassVisitor visitor(&circuit, false, "Int32OnHeapArrayStoreElement", &chunk); + TypeMCRLowering lowering(&circuit, &visitor, nullptr, nullptr, &chunk, true); + visitor.AddPass(&lowering); + visitor.VisitGraph(); + EXPECT_TRUE(Verifier::Run(&circuit)); + EXPECT_EQ(acc.GetOpCode(storeElement), OpCode::NOP); + auto result = acc.GetDep(ret); + EXPECT_EQ(acc.GetOpCode(result), OpCode::STORE_MEMORY); + auto storeValue = acc.GetValueIn(result, 2); + EXPECT_EQ(value, storeValue); +} + +HWTEST_F_L0(TypedArrayLoweringTests, Float64OnHeapArrayStoreElement) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + ecmascript::Chunk chunk(&allocator); + GateAccessor acc(&circuit); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + // after number speculative runner + builder.SetEnvironment(&env); + auto array = builder.Arguments(1); + acc.SetGateType(array, GateType::AnyType()); + auto index = builder.Int32(1); + auto value = builder.Double(1.5); + auto storeElement = + builder.StoreElement(array, index, value); + auto ret = builder.Return(builder.Undefined()); + EXPECT_TRUE(Verifier::Run(&circuit)); + CombinedPassVisitor visitor(&circuit, false, "Float64OnHeapArrayStoreElement", &chunk); + TypeMCRLowering lowering(&circuit, &visitor, nullptr, nullptr, &chunk, true); + visitor.AddPass(&lowering); + visitor.VisitGraph(); + EXPECT_TRUE(Verifier::Run(&circuit)); + EXPECT_EQ(acc.GetOpCode(storeElement), OpCode::NOP); + auto result = acc.GetDep(ret); + EXPECT_EQ(acc.GetOpCode(result), OpCode::STORE_MEMORY); + auto storeValue = acc.GetValueIn(result, 2); + EXPECT_EQ(value, storeValue); +} + +HWTEST_F_L0(TypedArrayLoweringTests, Int8OnHeapArrayStoreElement) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + ecmascript::Chunk chunk(&allocator); + GateAccessor acc(&circuit); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + // after number speculative runner + builder.SetEnvironment(&env); + auto array = builder.Arguments(1); + acc.SetGateType(array, GateType::AnyType()); + auto index = builder.Int32(1); + auto value = builder.Int32(2); + auto storeElement = + builder.StoreElement(array, index, value); + auto ret = builder.Return(builder.Undefined()); + EXPECT_TRUE(Verifier::Run(&circuit)); + CombinedPassVisitor visitor(&circuit, false, "Int8OnHeapArrayStoreElement", &chunk); + TypeMCRLowering lowering(&circuit, &visitor, nullptr, nullptr, &chunk, true); + visitor.AddPass(&lowering); + visitor.VisitGraph(); + EXPECT_TRUE(Verifier::Run(&circuit)); + EXPECT_EQ(acc.GetOpCode(storeElement), OpCode::NOP); + auto result = acc.GetDep(ret); + EXPECT_EQ(acc.GetOpCode(result), OpCode::STORE_MEMORY); + auto storeValue = acc.GetValueIn(result, 2); + EXPECT_EQ(value, acc.GetValueIn(storeValue)); + EXPECT_EQ(acc.GetOpCode(storeValue), OpCode::TRUNC); + EXPECT_EQ(acc.GetMachineType(storeValue), MachineType::I8); +} + +HWTEST_F_L0(TypedArrayLoweringTests, UInt8OnHeapArrayStoreElement) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + ecmascript::Chunk chunk(&allocator); + GateAccessor acc(&circuit); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + // after number speculative runner + builder.SetEnvironment(&env); + auto array = builder.Arguments(1); + acc.SetGateType(array, GateType::AnyType()); + auto index = builder.Int32(1); + auto value = builder.Int32(2); + auto storeElement = + builder.StoreElement(array, index, value); + auto ret = builder.Return(builder.Undefined()); + EXPECT_TRUE(Verifier::Run(&circuit)); + CombinedPassVisitor visitor(&circuit, false, "UInt8OnHeapArrayStoreElement", &chunk); + TypeMCRLowering lowering(&circuit, &visitor, nullptr, nullptr, &chunk, true); + visitor.AddPass(&lowering); + visitor.VisitGraph(); + EXPECT_TRUE(Verifier::Run(&circuit)); + EXPECT_EQ(acc.GetOpCode(storeElement), OpCode::NOP); + auto result = acc.GetDep(ret); + EXPECT_EQ(acc.GetOpCode(result), OpCode::STORE_MEMORY); + auto storeValue = acc.GetValueIn(result, 2); + EXPECT_EQ(value, acc.GetValueIn(storeValue)); + EXPECT_EQ(acc.GetOpCode(storeValue), OpCode::TRUNC); + EXPECT_EQ(acc.GetMachineType(storeValue), MachineType::I8); +} + +HWTEST_F_L0(TypedArrayLoweringTests, Int16OnHeapArrayStoreElement) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + ecmascript::Chunk chunk(&allocator); + GateAccessor acc(&circuit); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + // after number speculative runner + builder.SetEnvironment(&env); + auto array = builder.Arguments(1); + acc.SetGateType(array, GateType::AnyType()); + auto index = builder.Int32(1); + auto value = builder.Int32(2); + auto storeElement = + builder.StoreElement(array, index, value); + auto ret = builder.Return(builder.Undefined()); + EXPECT_TRUE(Verifier::Run(&circuit)); + CombinedPassVisitor visitor(&circuit, false, "Int16OnHeapArrayStoreElement", &chunk); + TypeMCRLowering lowering(&circuit, &visitor, nullptr, nullptr, &chunk, true); + visitor.AddPass(&lowering); + visitor.VisitGraph(); + EXPECT_TRUE(Verifier::Run(&circuit)); + EXPECT_EQ(acc.GetOpCode(storeElement), OpCode::NOP); + auto result = acc.GetDep(ret); + EXPECT_EQ(acc.GetOpCode(result), OpCode::STORE_MEMORY); + auto storeValue = acc.GetValueIn(result, 2); + EXPECT_EQ(value, acc.GetValueIn(storeValue)); + EXPECT_EQ(acc.GetOpCode(storeValue), OpCode::TRUNC); + EXPECT_EQ(acc.GetMachineType(storeValue), MachineType::I16); +} + +HWTEST_F_L0(TypedArrayLoweringTests, UInt16OnHeapArrayStoreElement) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + ecmascript::Chunk chunk(&allocator); + GateAccessor acc(&circuit); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + // after number speculative runner + builder.SetEnvironment(&env); + auto array = builder.Arguments(1); + acc.SetGateType(array, GateType::AnyType()); + auto index = builder.Int32(1); + auto value = builder.Int32(2); + auto storeElement = + builder.StoreElement(array, index, value); + auto ret = builder.Return(builder.Undefined()); + EXPECT_TRUE(Verifier::Run(&circuit)); + CombinedPassVisitor visitor(&circuit, false, "UInt16OnHeapArrayStoreElement", &chunk); + TypeMCRLowering lowering(&circuit, &visitor, nullptr, nullptr, &chunk, true); + visitor.AddPass(&lowering); + visitor.VisitGraph(); + EXPECT_TRUE(Verifier::Run(&circuit)); + EXPECT_EQ(acc.GetOpCode(storeElement), OpCode::NOP); + auto result = acc.GetDep(ret); + EXPECT_EQ(acc.GetOpCode(result), OpCode::STORE_MEMORY); + auto storeValue = acc.GetValueIn(result, 2); + EXPECT_EQ(value, acc.GetValueIn(storeValue)); + EXPECT_EQ(acc.GetOpCode(storeValue), OpCode::TRUNC); + EXPECT_EQ(acc.GetMachineType(storeValue), MachineType::I16); +} + +HWTEST_F_L0(TypedArrayLoweringTests, Float32OnHeapArrayStoreElement) +{ + // construct a circuit + ecmascript::NativeAreaAllocator allocator; + Circuit circuit(&allocator); + ecmascript::Chunk chunk(&allocator); + GateAccessor acc(&circuit); + CircuitBuilder builder(&circuit); + Environment env(0, &builder); + // after number speculative runner + builder.SetEnvironment(&env); + auto array = builder.Arguments(1); + acc.SetGateType(array, GateType::AnyType()); + auto index = builder.Int32(1); + auto value = builder.Double(1.5); + auto storeElement = + builder.StoreElement(array, index, value); + auto ret = builder.Return(builder.Undefined()); + EXPECT_TRUE(Verifier::Run(&circuit)); + CombinedPassVisitor visitor(&circuit, false, "Float32OnHeapArrayStoreElement", &chunk); + TypeMCRLowering lowering(&circuit, &visitor, nullptr, nullptr, &chunk, true); + visitor.AddPass(&lowering); + visitor.VisitGraph(); + EXPECT_TRUE(Verifier::Run(&circuit)); + EXPECT_EQ(acc.GetOpCode(storeElement), OpCode::NOP); + auto result = acc.GetDep(ret); + EXPECT_EQ(acc.GetOpCode(result), OpCode::STORE_MEMORY); + auto storeValue = acc.GetValueIn(result, 2); + EXPECT_EQ(value, acc.GetValueIn(storeValue)); + EXPECT_EQ(acc.GetOpCode(storeValue), OpCode::FTRUNC); + EXPECT_EQ(acc.GetMachineType(storeValue), MachineType::F32); +} +} // namespace panda::test \ No newline at end of file diff --git a/ecmascript/compiler/trampoline/aarch64/asm_interpreter_call.cpp b/ecmascript/compiler/trampoline/aarch64/asm_interpreter_call.cpp index 072d1a8f8dcab524d6762069d4808d989cf9683a..8d6f0597f6cdb7ea5f75bab766488c94f4c3977e 100644 --- a/ecmascript/compiler/trampoline/aarch64/asm_interpreter_call.cpp +++ b/ecmascript/compiler/trampoline/aarch64/asm_interpreter_call.cpp @@ -827,6 +827,55 @@ void AsmInterpreterCall::ResumeUncaughtFrameAndReturn(ExtendedAssembler *assembl __ Ret(); } +// ResumeRspAndRollback(uintptr_t glue, uintptr_t sp, uintptr_t pc, uintptr_t constantPool, +// uint64_t profileTypeInfo, uint64_t acc, uint32_t hotnessCounter, size_t jumpSize) +// GHC calling convention +// X19 - glue +// FP - sp +// X20 - pc +// X21 - constantPool +// X22 - profileTypeInfo +// X23 - acc +// X24 - hotnessCounter +// X25 - jumpSizeAfterCall +void AsmInterpreterCall::ResumeRspAndRollback(ExtendedAssembler *assembler) +{ + __ BindAssemblerStub(RTSTUB_ID(ResumeRspAndRollback)); + + Register glueRegister = __ GlueRegister(); + Register sp(FP); + Register rsp(SP); + Register pc(X20); + Register jumpSizeRegister(X25); + + Register ret(X23); + Register opcode(X6, W); + Register bcStub(X7); + Register fp(X8); + + int64_t fpOffset = static_cast(AsmInterpretedFrame::GetFpOffset(false)) + - static_cast(AsmInterpretedFrame::GetSize(false)); + int64_t spOffset = static_cast(AsmInterpretedFrame::GetBaseOffset(false)) + - static_cast(AsmInterpretedFrame::GetSize(false)); + int64_t funcOffset = static_cast(AsmInterpretedFrame::GetFunctionOffset(false)) + - static_cast(AsmInterpretedFrame::GetSize(false)); + ASSERT(fpOffset < 0); + ASSERT(spOffset < 0); + ASSERT(funcOffset < 0); + + __ Ldur(fp, MemoryOperand(sp, fpOffset)); // store fp for temporary + __ Ldur(ret, MemoryOperand(sp, funcOffset)); // restore acc + __ Ldur(sp, MemoryOperand(sp, spOffset)); // update sp + + __ Add(pc, pc, Operand(jumpSizeRegister, LSL, 0)); + __ Ldrb(opcode, MemoryOperand(pc, 0)); + + __ Mov(rsp, fp); // resume rsp + __ Add(bcStub, glueRegister, Operand(opcode, UXTW, FRAME_SLOT_SIZE_LOG2)); + __ Ldr(bcStub, MemoryOperand(bcStub, JSThread::GlueData::GetBCStubEntriesOffset(false))); + __ Br(bcStub); +} + // c++ calling convention // X0 - glue // X1 - callTarget diff --git a/ecmascript/compiler/trampoline/aarch64/common_call.h b/ecmascript/compiler/trampoline/aarch64/common_call.h index 8a4e4cb7e7ad7337af566d0eaec14ffd076a59bc..71ecb781f143011f94a562e95329e1ce6da56df3 100644 --- a/ecmascript/compiler/trampoline/aarch64/common_call.h +++ b/ecmascript/compiler/trampoline/aarch64/common_call.h @@ -177,6 +177,8 @@ public: static void ResumeUncaughtFrameAndReturn(ExtendedAssembler *assembler); + static void ResumeRspAndRollback(ExtendedAssembler *assembler); + static void CallGetter(ExtendedAssembler *assembler); static void CallSetter(ExtendedAssembler *assembler); diff --git a/ecmascript/compiler/trampoline/aarch64/optimized_call.cpp b/ecmascript/compiler/trampoline/aarch64/optimized_call.cpp index 0391ffcd8eafa8b73c7a22f68c1e8e18e1758349..1e52993848a14b05db5ce203f2d9df20ea022cf0 100644 --- a/ecmascript/compiler/trampoline/aarch64/optimized_call.cpp +++ b/ecmascript/compiler/trampoline/aarch64/optimized_call.cpp @@ -401,6 +401,7 @@ void OptimizedCall::JSCallInternal(ExtendedAssembler *assembler, Register jsfunc Label lCallConstructor; Label lCallBuiltinStub; Label lCallNativeCpp; + Label lNotClass; __ Ldr(Register(X5), MemoryOperand(jsfunc, 0)); __ Ldr(Register(X5), MemoryOperand(Register(X5), JSHClass::BIT_FIELD_OFFSET)); @@ -409,8 +410,10 @@ void OptimizedCall::JSCallInternal(ExtendedAssembler *assembler, Register jsfunc __ Ldr(callField, MemoryOperand(method, Method::CALL_FIELD_OFFSET)); __ Tbnz(callField, MethodLiteral::IsNativeBit::START_BIT, &callNativeMethod); if (!isNew) { - __ Tbnz(Register(X5), JSHClass::ClassConstructorBit::START_BIT, &lCallConstructor); + __ Tbz(Register(X5), JSHClass::IsClassConstructorOrPrototypeBit::START_BIT, &lNotClass); + __ Tbnz(Register(X5), JSHClass::ConstructorBit::START_BIT, &lCallConstructor); } + __ Bind(&lNotClass); { Register argV(X5); // skip argc @@ -603,6 +606,7 @@ void OptimizedCall::JSBoundFunctionCallInternal(ExtendedAssembler *assembler, Re Label popArgs; Label slowCall; Label aotCall; + Label notClass; // get bound arguments __ Ldr(boundLength, MemoryOperand(jsfunc, JSBoundFunction::BOUND_ARGUMENTS_OFFSET)); // get bound length @@ -650,7 +654,9 @@ void OptimizedCall::JSBoundFunctionCallInternal(ExtendedAssembler *assembler, Re Register hclass = __ AvailableRegister2(); __ Ldr(hclass, MemoryOperand(boundTarget, 0)); __ Ldr(hclass, MemoryOperand(hclass, JSHClass::BIT_FIELD_OFFSET)); - __ Tbnz(hclass, JSHClass::ClassConstructorBit::START_BIT, &slowCall); + __ Tbz(hclass, JSHClass::IsClassConstructorOrPrototypeBit::START_BIT, ¬Class); + __ Tbnz(hclass, JSHClass::ConstructorBit::START_BIT, &slowCall); + __ Bind(¬Class); __ Tbz(hclass, JSHClass::IsOptimizedBit::START_BIT, &slowCall); __ Bind(&aotCall); { @@ -747,13 +753,12 @@ void OptimizedCall::CallRuntimeWithArgv(ExtendedAssembler *assembler) Register sp(SP); // 2 : 2 means pair __ Stp(argc, argv, MemoryOperand(sp, -FRAME_SLOT_SIZE * 2, AddrMode::PREINDEX)); - __ Str(runtimeId, MemoryOperand(sp, -FRAME_SLOT_SIZE, AddrMode::PREINDEX)); - __ PushFpAndLr(); + __ Stp(Register(X30), runtimeId, MemoryOperand(sp, -FRAME_SLOT_SIZE * 2, AddrMode::PREINDEX)); // 2 : 2 means pair Register fp(X29); // construct leave frame Register frameType(X9); __ Mov(frameType, Immediate(static_cast(FrameType::LEAVE_FRAME_WITH_ARGV))); - __ Str(frameType, MemoryOperand(sp, -FRAME_SLOT_SIZE, AddrMode::PREINDEX)); + __ Stp(frameType, Register(X29), MemoryOperand(sp, -FRAME_SLOT_SIZE * 2, AddrMode::PREINDEX)); // 2 : 2 means pair __ Add(Register(FP), sp, Immediate(FRAME_SLOT_SIZE)); __ Str(fp, MemoryOperand(glue, JSThread::GlueData::GetLeaveFrameOffset(false))); @@ -766,9 +771,9 @@ void OptimizedCall::CallRuntimeWithArgv(ExtendedAssembler *assembler) __ Mov(X1, argc); __ Mov(X2, argv); __ Blr(rtfunc); - __ Add(sp, sp, Immediate(FRAME_SLOT_SIZE)); - __ RestoreFpAndLr(); - __ Add(sp, sp, Immediate(3 * FRAME_SLOT_SIZE)); // 3 : 3 means pair + __ Ldp(Register(Zero), Register(X29), MemoryOperand(sp, ExtendedAssembler::PAIR_SLOT_SIZE, POSTINDEX)); + __ Ldp(Register(X30), Register(Zero), MemoryOperand(sp, ExtendedAssembler::PAIR_SLOT_SIZE, POSTINDEX)); + __ Add(sp, sp, Immediate(2 * FRAME_SLOT_SIZE)); // 2 : 2 means pair __ Ret(); } @@ -1161,4 +1166,4 @@ void OptimizedCall::DeoptHandlerAsm(ExtendedAssembler *assembler) } } #undef __ -} // panda::ecmascript::aarch64 \ No newline at end of file +} // panda::ecmascript::aarch64 diff --git a/ecmascript/compiler/trampoline/x64/asm_interpreter_call.cpp b/ecmascript/compiler/trampoline/x64/asm_interpreter_call.cpp index f8853e730040be290298625a39fdcd67176608a6..bcf4b131577956a7129f6a2bcf975dee513def80 100644 --- a/ecmascript/compiler/trampoline/x64/asm_interpreter_call.cpp +++ b/ecmascript/compiler/trampoline/x64/asm_interpreter_call.cpp @@ -1186,6 +1186,44 @@ void AsmInterpreterCall::ResumeUncaughtFrameAndReturn(ExtendedAssembler *assembl __ Ret(); } +// ResumeRspAndRollback(uintptr_t glue, uintptr_t sp, uintptr_t pc, uintptr_t constantPool, +// uint64_t profileTypeInfo, uint64_t acc, uint32_t hotnessCounter, size_t jumpSize) +// GHC calling convention +// %r13 - glue +// %rbp - sp +// %r12 - pc +// %rbx - constantPool +// %r14 - profileTypeInfo +// %rsi - acc +// %rdi - hotnessCounter +// %r8 - jumpSizeAfterCall +void AsmInterpreterCall::ResumeRspAndRollback(ExtendedAssembler *assembler) +{ + __ BindAssemblerStub(RTSTUB_ID(ResumeRspAndRollback)); + Register glueRegister = __ GlueRegister(); + Register spRegister = rbp; + Register pcRegister = r12; + Register ret = rsi; + Register jumpSizeRegister = r8; + + Register frameStateBaseRegister = r11; + __ Movq(spRegister, frameStateBaseRegister); + __ Subq(AsmInterpretedFrame::GetSize(false), frameStateBaseRegister); + + __ Movq(Operand(frameStateBaseRegister, AsmInterpretedFrame::GetBaseOffset(false)), spRegister); // update sp + __ Addq(jumpSizeRegister, pcRegister); // newPC + Register opcodeRegister = rax; + __ Movzbq(Operand(pcRegister, 0), opcodeRegister); + + __ Movq(Operand(frameStateBaseRegister, AsmInterpretedFrame::GetFunctionOffset(false)), ret); // restore acc + + __ Movq(Operand(frameStateBaseRegister, AsmInterpretedFrame::GetFpOffset(false)), rsp); // resume rsp + Register bcStubRegister = r11; + __ Movq(Operand(glueRegister, opcodeRegister, Times8, JSThread::GlueData::GetBCStubEntriesOffset(false)), + bcStubRegister); + __ Jmp(bcStubRegister); +} + void AsmInterpreterCall::PushUndefinedWithArgcAndCheckStack(ExtendedAssembler *assembler, Register glue, Register argc, Register op1, Register op2, Label *stackOverflow) { diff --git a/ecmascript/compiler/trampoline/x64/common_call.h b/ecmascript/compiler/trampoline/x64/common_call.h index 3ed42aa9f68c75148e0945e8ea008634f5c9ece4..b641fb207f1ba4bc6f03f4c13d086404ae9b2ada 100644 --- a/ecmascript/compiler/trampoline/x64/common_call.h +++ b/ecmascript/compiler/trampoline/x64/common_call.h @@ -155,6 +155,8 @@ public: static void ResumeUncaughtFrameAndReturn(ExtendedAssembler *assembler); + static void ResumeRspAndRollback(ExtendedAssembler *assembler); + private: static void PushFrameState(ExtendedAssembler *assembler, Register prevSpRegister, Register fpRegister, Register callTargetRegister, Register thisRegister, Register methodRegister, Register pcRegister, diff --git a/ecmascript/compiler/trampoline/x64/optimized_call.cpp b/ecmascript/compiler/trampoline/x64/optimized_call.cpp index 2345cb298256870a56827ef7b285e314848bca9f..3532d6d55976749d591872d24b0f17f552182602 100644 --- a/ecmascript/compiler/trampoline/x64/optimized_call.cpp +++ b/ecmascript/compiler/trampoline/x64/optimized_call.cpp @@ -350,15 +350,19 @@ void OptimizedCall::GenJSCall(ExtendedAssembler *assembler, bool isNew) Register argV = r9; { Label lCallConstructor; + Label lNotClass; __ Mov(Operand(jsFuncReg, JSFunctionBase::METHOD_OFFSET), method); // get method __ Movl(Operand(rsp, FRAME_SLOT_SIZE), argc); // skip return addr __ Mov(Operand(method, Method::CALL_FIELD_OFFSET), methodCallField); // get call field __ Btq(MethodLiteral::IsNativeBit::START_BIT, methodCallField); // is native __ Jb(&lCallNativeMethod); if (!isNew) { - __ Btq(JSHClass::ClassConstructorBit::START_BIT, rax); // is CallConstructor + __ Btq(JSHClass::IsClassConstructorOrPrototypeBit::START_BIT, rax); // is CallConstructor + __ Jnb(&lNotClass); + __ Btq(JSHClass::ConstructorBit::START_BIT, rax); // is CallConstructor __ Jb(&lCallConstructor); } + __ Bind(&lNotClass); __ Movq(rsp, argV); auto argvSlotOffset = kungfu::ArgumentAccessor::GetExtraArgsNum() + 1; // 1: return addr __ Addq(argvSlotOffset * FRAME_SLOT_SIZE, argV); // skip return addr and argc @@ -575,6 +579,7 @@ void OptimizedCall::JSBoundFunctionCallInternal(ExtendedAssembler *assembler, Re Label aotCall; Label popArgs; Label isJsFunc; + Label isNotClass; __ Pushq(rbp); __ Pushq(static_cast(FrameType::OPTIMIZED_JS_FUNCTION_ARGS_CONFIG_FRAME)); __ Leaq(Operand(rsp, FRAME_SLOT_SIZE), rbp); @@ -640,8 +645,11 @@ void OptimizedCall::JSBoundFunctionCallInternal(ExtendedAssembler *assembler, Re Register jsfunc = rsi; __ Bind(&isJsFunc); { - __ Btq(JSHClass::ClassConstructorBit::START_BIT, rax); // is CallConstructor + __ Btq(JSHClass::IsClassConstructorOrPrototypeBit::START_BIT, rax); // is CallConstructor + __ Jnb(&isNotClass); + __ Btq(JSHClass::ConstructorBit::START_BIT, rax); __ Jb(&slowCall); + __ Bind(&isNotClass); __ Btq(JSHClass::IsOptimizedBit::START_BIT, rax); // is aot __ Jnb(&slowCall); __ Bind(&aotCall); @@ -1161,4 +1169,4 @@ void OptimizedCall::DeoptHandlerAsm(ExtendedAssembler *assembler) } } #undef __ -} // namespace panda::ecmascript::x64 \ No newline at end of file +} // namespace panda::ecmascript::x64 diff --git a/ecmascript/compiler/ts_class_analysis.cpp b/ecmascript/compiler/ts_class_analysis.cpp index 18bcd1b65c779eaa389d9cffe7ebdb22ab5716cd..8b4109d735e9346985c749e328d5a86ca57512af 100644 --- a/ecmascript/compiler/ts_class_analysis.cpp +++ b/ecmascript/compiler/ts_class_analysis.cpp @@ -99,7 +99,7 @@ void TSClassAnalysis::AnalyzeProperties(const JSThread *thread, const TSClassTyp GlobalTSTypeRef classGT = classType->GetGT(); int hclassIndex = tsManager_->GetHClassIndex(classGT); ASSERT(hclassIndex != -1); - JSHClass *hclass = JSHClass::Cast(tsManager_->GetHClassFromCache(hclassIndex).GetTaggedObject()); + JSHClass *hclass = JSHClass::Cast(tsManager_->GetValueFromCache(hclassIndex).GetTaggedObject()); if (UNLIKELY(hclass->IsDictionaryMode())) { return; } diff --git a/ecmascript/compiler/ts_hclass_generator.cpp b/ecmascript/compiler/ts_hclass_generator.cpp index fc1ff5e76663bccc70283434e2eb223441a8dd32..311135d042bf02e376ca3d61b76eb84956d5a7e7 100644 --- a/ecmascript/compiler/ts_hclass_generator.cpp +++ b/ecmascript/compiler/ts_hclass_generator.cpp @@ -14,11 +14,14 @@ */ #include "ecmascript/compiler/ts_hclass_generator.h" +#include "ecmascript/global_env_constants-inl.h" #include "ecmascript/subtyping_operator.h" #include "ecmascript/jspandafile/class_info_extractor.h" namespace panda::ecmascript::kungfu { using ClassInfoExtractor = panda::ecmascript::ClassInfoExtractor; +using PGOHClassLayoutDesc = pgo::PGOHClassLayoutDesc; +using PGOHandler = pgo::PGOHandler; void TSHClassGenerator::GenerateTSHClasses() const { const JSThread *thread = tsManager_->GetThread(); @@ -40,13 +43,38 @@ void TSHClassGenerator::GenerateTSHClasses() const } } -void TSHClassGenerator::UpdateTSHClassFromPGO(JSHClass *hclass, const PGOHClassLayoutDesc &desc) const +void TSHClassGenerator::UpdateTSHClassFromPGO(const kungfu::GateType &type, const PGOHClassLayoutDesc &desc, + bool enableOptTrackField) const { DISALLOW_GARBAGE_COLLECTION; + auto hclassValue = tsManager_->GetTSHClass(type); + if (!hclassValue.IsJSHClass()) { + return; + } + + tsManager_->InsertPtToGtMap(desc.GetProfileType(), type); + if (!enableOptTrackField) { + return; + } + if (tsManager_->IsInSkipTrackFieldSet(desc.GetProfileType())) { + return; + } + + std::vector superHClasses; + kungfu::GateType current = type; + while (tsManager_->GetSuperGateType(current)) { + auto superHClassValue = tsManager_->GetTSHClass(current); + if (!superHClassValue.IsJSHClass()) { + break; + } + superHClasses.emplace_back(JSHClass::Cast(superHClassValue.GetTaggedObject())); + } + + auto hclass = JSHClass::Cast(hclassValue.GetTaggedObject()); const JSThread *thread = tsManager_->GetThread(); LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject()); - int element = layoutInfo->NumberOfElements(); - for (int i = 0; i < element; i++) { + uint32_t numOfProps = hclass->NumberOfProps(); + for (uint32_t i = 0; i < numOfProps; i++) { auto key = layoutInfo->GetKey(i); if (!key.IsString()) { continue; @@ -55,12 +83,24 @@ void TSHClassGenerator::UpdateTSHClassFromPGO(JSHClass *hclass, const PGOHClassL PGOHandler newHandler; if (desc.FindDescWithKey(keyString, newHandler)) { auto attr = layoutInfo->GetAttr(i); - if (newHandler.GetTrackType() == TrackType::DOUBLE) { - attr.SetRepresentation(Representation::DOUBLE); - } else { - attr.SetRepresentation(Representation::OBJECT); + if (newHandler.SetAttribute(attr)) { + hclass->SetIsAllTaggedProp(false); } layoutInfo->SetNormalAttr(thread, i, attr); + + // Update super class representation + for (auto superHClass : superHClasses) { + int entry = JSHClass::FindPropertyEntry(thread, superHClass, key); + if (entry == -1) { + continue; + } + auto superLayout = LayoutInfo::Cast(superHClass->GetLayout().GetTaggedObject()); + PropertyAttributes superAttr = superLayout->GetAttr(entry); + if (newHandler.SetAttribute(superAttr)) { + superHClass->SetIsAllTaggedProp(false); + } + superLayout->SetNormalAttr(thread, entry, superAttr); + } } } } @@ -144,7 +184,7 @@ JSHandle TSHClassGenerator::CreateIHClass(const JSThread *thread, JSHandle tsLayout(thread, instanceType->GetObjLayoutInfo()); uint32_t numOfProps = tsLayout->GetNumOfProperties(); JSHandle hclass; - if (LIKELY(numOfProps <= PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES)) { + if (LIKELY(numOfProps <= PropertyAttributes::MAX_FAST_PROPS_CAPACITY)) { JSMutableHandle key(thread, JSTaggedValue::Undefined()); JSHandle layout = factory->CreateLayoutInfo(numOfProps); for (uint32_t index = 0; index < numOfProps; ++index) { @@ -153,7 +193,7 @@ JSHandle TSHClassGenerator::CreateIHClass(const JSThread *thread, ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); PropertyAttributes attributes = PropertyAttributes::Default(); attributes.SetIsInlinedProps(true); - attributes.SetRepresentation(Representation::MIXED); + attributes.SetRepresentation(Representation::NONE); attributes.SetOffset(index); layout->AddKey(thread, index, key.GetTaggedValue(), attributes); } @@ -181,12 +221,14 @@ JSHandle TSHClassGenerator::CreatePHClass(const JSThread *thread, JSHandle tsLayout(thread, prototypeType->GetObjLayoutInfo()); uint32_t numOfProps = tsLayout->GetNumOfProperties(); JSHandle hclass; - if (LIKELY(numOfProps <= PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES)) { + // There is only function information in abc, which needs to be brought up to the front for processing. Other fields should be collected first and then optimized to the end. + // Please note that there may be a problem with order in this way + if (LIKELY(numOfProps <= PropertyAttributes::MAX_FAST_PROPS_CAPACITY)) { TSManager *tsManager = thread->GetCurrentEcmaContext()->GetTSManager(); const GlobalEnvConstants *globalConst = thread->GlobalConstants(); JSHandle ctor = globalConst->GetHandledConstructorString(); CVector, GlobalTSTypeRef>> sortedPrototype {{ctor, GlobalTSTypeRef()}}; - CVector, GlobalTSTypeRef>> signatureVec {}; + CUnorderedMap keysMap; for (uint32_t index = 0; index < numOfProps; ++index) { JSHandle key(thread, tsLayout->GetKey(index)); auto value = GlobalTSTypeRef(tsLayout->GetTypeId(index).GetInt()); @@ -197,18 +239,20 @@ JSHandle TSHClassGenerator::CreatePHClass(const JSThread *thread, bool isAbs = tsManager->IsAbstractMethod(value); if (!isSame && !isAbs) { bool isSign = tsManager->IsMethodSignature(value); - if (LIKELY(!isSign)) { - sortedPrototype.emplace_back(std::make_pair(key, value)); - } else { - signatureVec.emplace_back(std::make_pair(key, value)); + if (isSign) { + continue; + } + std::string keyStr = EcmaStringAccessor(key.GetTaggedValue()).ToStdString(); + auto it = keysMap.find(keyStr); + if (it != keysMap.end()) { + sortedPrototype[it->second] = std::make_pair(key, value); + continue; } + keysMap[keyStr] = sortedPrototype.size(); + sortedPrototype.emplace_back(std::make_pair(key, value)); } } - if (!signatureVec.empty()) { - sortedPrototype.insert(sortedPrototype.end(), signatureVec.begin(), signatureVec.end()); - } - uint32_t keysLen = sortedPrototype.size(); JSMutableHandle key(thread, JSTaggedValue::Undefined()); JSHandle layout = factory->CreateLayoutInfo(keysLen); @@ -221,7 +265,7 @@ JSHandle TSHClassGenerator::CreatePHClass(const JSThread *thread, attributes.SetIsAccessor(true); } attributes.SetIsInlinedProps(true); - attributes.SetRepresentation(Representation::MIXED); + attributes.SetRepresentation(Representation::NONE); attributes.SetOffset(index); layout->AddKey(thread, index, key.GetTaggedValue(), attributes); } @@ -251,13 +295,12 @@ JSHandle TSHClassGenerator::CreateCHClass(const JSThread *thread, JSHandle tsLayout(thread, constructorType->GetObjLayoutInfo()); uint32_t numOfProps = tsLayout->GetNumOfProperties() + ClassInfoExtractor::STATIC_RESERVED_LENGTH; JSHandle hclass; - uint32_t functionFirstIndex = numOfProps; - uint32_t numNonStaticFunc = 0; - bool hasFunction = false; - if (LIKELY(numOfProps <= PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES)) { + if (LIKELY(numOfProps <= PropertyAttributes::MAX_FAST_PROPS_CAPACITY)) { TSManager *tsManager = thread->GetCurrentEcmaContext()->GetTSManager(); const GlobalEnvConstants *globalConst = thread->GlobalConstants(); JSHandle layout = factory->CreateLayoutInfo(numOfProps); + std::vector> noFunc; + int functionCount = -1; for (uint32_t index = 0; index < numOfProps; ++index) { JSTaggedValue tsPropKey; PropertyAttributes attributes; @@ -265,14 +308,17 @@ JSHandle TSHClassGenerator::CreateCHClass(const JSThread *thread, case ClassInfoExtractor::LENGTH_INDEX: attributes = PropertyAttributes::Default(false, false, true); tsPropKey = globalConst->GetLengthString(); + functionCount++; break; case ClassInfoExtractor::NAME_INDEX: attributes = PropertyAttributes::Default(false, false, true); tsPropKey = globalConst->GetNameString(); + functionCount++; break; case ClassInfoExtractor::PROTOTYPE_INDEX: attributes = PropertyAttributes::DefaultAccessor(false, false, false); tsPropKey = globalConst->GetPrototypeString(); + functionCount++; break; default: attributes = PropertyAttributes::Default(true, false, true); @@ -280,33 +326,28 @@ JSHandle TSHClassGenerator::CreateCHClass(const JSThread *thread, JSTaggedValue typeId = tsLayout->GetTypeId(index - ClassInfoExtractor::STATIC_RESERVED_LENGTH); GlobalTSTypeRef gt(static_cast(typeId.GetNumber())); if (!tsManager->IsFunctionTypeKind(gt)) { + noFunc.emplace_back(JSHandle(thread, tsPropKey)); continue; } - if (!hasFunction) { - hasFunction = true; - functionFirstIndex = index; - numNonStaticFunc = functionFirstIndex - ClassInfoExtractor::STATIC_RESERVED_LENGTH; - } if (tsManager->IsGetterSetterFunc(gt)) { attributes.SetIsAccessor(true); } + functionCount++; break; } ASSERT_PRINT(JSTaggedValue::IsPropertyKey(JSHandle(thread, tsPropKey)), "Key is not a property key"); attributes.SetIsInlinedProps(true); - attributes.SetRepresentation(Representation::MIXED); - attributes.SetOffset(index - numNonStaticFunc); - layout->AddKey(thread, index - numNonStaticFunc, tsPropKey, attributes); + attributes.SetRepresentation(Representation::NONE); + attributes.SetOffset(functionCount); + layout->AddKey(thread, functionCount, tsPropKey, attributes); } - uint32_t numStaticFunc = numOfProps - functionFirstIndex; - for (uint32_t index = ClassInfoExtractor::STATIC_RESERVED_LENGTH; index < functionFirstIndex; index++) { - JSTaggedValue tsPropKey = tsLayout->GetKey(index - ClassInfoExtractor::STATIC_RESERVED_LENGTH); + for (uint32_t index = 1; index <= noFunc.size(); index++) { PropertyAttributes attributes = PropertyAttributes::Default(); attributes.SetIsInlinedProps(true); - attributes.SetRepresentation(Representation::MIXED); - attributes.SetOffset(index + numStaticFunc); - layout->AddKey(thread, index + numStaticFunc, tsPropKey, attributes); + attributes.SetRepresentation(Representation::NONE); + attributes.SetOffset(index + functionCount); + layout->AddKey(thread, index + functionCount, noFunc[index - 1].GetTaggedValue(), attributes); } hclass = factory->NewEcmaHClass(JSFunction::SIZE, JSType::JS_FUNCTION, numOfProps); hclass->SetLayout(thread, layout); diff --git a/ecmascript/compiler/ts_hclass_generator.h b/ecmascript/compiler/ts_hclass_generator.h index 6c0b670b47fb02f056a3b0d899b3f54ecabac23d..646befb245b454023f9bb240324d1c27e6ab6837 100644 --- a/ecmascript/compiler/ts_hclass_generator.h +++ b/ecmascript/compiler/ts_hclass_generator.h @@ -26,7 +26,8 @@ public: ~TSHClassGenerator() = default; void GenerateTSHClasses() const; - void UpdateTSHClassFromPGO(JSHClass *hclass, const PGOHClassLayoutDesc &desc) const; + void UpdateTSHClassFromPGO(const kungfu::GateType &type, const PGOHClassLayoutDesc &desc, + bool enableOptTrackField) const; private: void RecursiveGenerate(const JSHandle &classType) const; diff --git a/ecmascript/compiler/ts_hcr_lowering.cpp b/ecmascript/compiler/ts_hcr_lowering.cpp index 4bb6bae56c04fa2a43eed2836490ef64002f9685..6bfc7ad90a322403cf718a9bbdfba4f61cb4a209 100644 --- a/ecmascript/compiler/ts_hcr_lowering.cpp +++ b/ecmascript/compiler/ts_hcr_lowering.cpp @@ -16,6 +16,7 @@ #include "ecmascript/compiler/ts_hcr_lowering.h" #include "ecmascript/compiler/bytecodes.h" #include "ecmascript/compiler/builtins_lowering.h" +#include "ecmascript/compiler/circuit.h" #include "ecmascript/dfx/vmstat/opt_code_profiler.h" #include "ecmascript/stackmap/llvm_stackmap_parser.h" #include "ecmascript/ts_types/ts_type.h" @@ -121,6 +122,32 @@ bool TSHCRLowering::IsTrustedType(GateRef gate) const return false; } +bool TSHCRLowering::IsTrustedStringType(GateRef gate) const +{ + auto op = acc_.GetOpCode(gate); + if (op == OpCode::LOAD_ELEMENT) { + return acc_.GetTypedLoadOp(gate) == TypedLoadOp::STRING_LOAD_ELEMENT; + } + if (op == OpCode::JS_BYTECODE) { + EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate); + switch (ecmaOpcode) { + case EcmaOpcode::LDA_STR_ID16: + return true; + case EcmaOpcode::LDOBJBYVALUE_IMM8_V8: + case EcmaOpcode::LDOBJBYVALUE_IMM16_V8: { + GateRef receiver = acc_.GetValueIn(gate, 1); + GateRef propKey = acc_.GetValueIn(gate, 2); + GateType receiverType = acc_.GetGateType(receiver); + GateType propKeyType = acc_.GetGateType(propKey); + return propKeyType.IsNumberType() && receiverType.IsStringType(); + } + default: + break; + } + } + return false; +} + void TSHCRLowering::Lower(GateRef gate) { EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate); @@ -138,10 +165,10 @@ void TSHCRLowering::Lower(GateRef gate) LowerTypedBinOp(gate); break; case EcmaOpcode::DIV2_IMM8_V8: - LowerTypedDiv(gate); + LowerTypedBinOp(gate); break; case EcmaOpcode::MOD2_IMM8_V8: - LowerTypedMod(gate); + LowerTypedBinOp(gate); break; case EcmaOpcode::LESS_IMM8_V8: LowerTypedBinOp(gate); @@ -156,46 +183,46 @@ void TSHCRLowering::Lower(GateRef gate) LowerTypedBinOp(gate); break; case EcmaOpcode::EQ_IMM8_V8: - LowerTypedBinOp(gate); + LowerTypedBinOp(gate, false); break; case EcmaOpcode::STRICTEQ_IMM8_V8: LowerTypedStrictEq(gate); break; case EcmaOpcode::NOTEQ_IMM8_V8: - LowerTypedBinOp(gate); + LowerTypedBinOp(gate, false); break; case EcmaOpcode::SHL2_IMM8_V8: - LowerTypedShl(gate); + LowerTypedBinOp(gate); break; case EcmaOpcode::SHR2_IMM8_V8: - LowerTypedShr(gate); + LowerTypedBinOp(gate); break; case EcmaOpcode::ASHR2_IMM8_V8: - LowerTypedAshr(gate); + LowerTypedBinOp(gate); break; case EcmaOpcode::AND2_IMM8_V8: - LowerTypedAnd(gate); + LowerTypedBinOp(gate); break; case EcmaOpcode::OR2_IMM8_V8: - LowerTypedOr(gate); + LowerTypedBinOp(gate); break; case EcmaOpcode::XOR2_IMM8_V8: - LowerTypedXor(gate); + LowerTypedBinOp(gate); break; case EcmaOpcode::TONUMERIC_IMM8: LowerTypeToNumeric(gate); break; case EcmaOpcode::NEG_IMM8: - LowerTypedNeg(gate); + LowerTypedUnOp(gate); break; case EcmaOpcode::NOT_IMM8: - LowerTypedNot(gate); + LowerTypedUnOp(gate); break; case EcmaOpcode::INC_IMM8: - LowerTypedInc(gate); + LowerTypedUnOp(gate); break; case EcmaOpcode::DEC_IMM8: - LowerTypedDec(gate); + LowerTypedUnOp(gate); break; case EcmaOpcode::ISTRUE: LowerTypedIsTrueOrFalse(gate, true); @@ -258,10 +285,6 @@ void TSHCRLowering::Lower(GateRef gate) case EcmaOpcode::WIDE_SUPERCALLTHISRANGE_PREF_IMM16_V8: LowerTypedSuperCall(gate); break; - case EcmaOpcode::CREATEEMPTYARRAY_IMM8: - case EcmaOpcode::CREATEEMPTYARRAY_IMM16: - LowerTypedCreateEmptyArray(gate); - break; case EcmaOpcode::CALLARG0_IMM8: LowerTypedCallArg0(gate); break; @@ -292,6 +315,10 @@ void TSHCRLowering::Lower(GateRef gate) case EcmaOpcode::CALLTHISRANGE_IMM8_IMM8_V8: LowerTypedCallthisrange(gate); break; + case EcmaOpcode::TYPEOF_IMM8: + case EcmaOpcode::TYPEOF_IMM16: + LowerTypedTypeOf(gate); + break; default: DeleteBytecodeCount(ecmaOpcode); allNonTypedOpCount_++; @@ -300,30 +327,23 @@ void TSHCRLowering::Lower(GateRef gate) } template -void TSHCRLowering::LowerTypedBinOp(GateRef gate) +void TSHCRLowering::LowerTypedBinOp(GateRef gate, bool convertNumberType) { GateRef left = acc_.GetValueIn(gate, 0); GateRef right = acc_.GetValueIn(gate, 1); - if (HasNumberType(gate, left, right)) { + if (HasNumberType(gate, left, right, convertNumberType)) { SpeculateNumbers(gate); + } else if (HasStringType(gate, left, right)) { + SpeculateStrings(gate); } } -void TSHCRLowering::LowerTypedMod(GateRef gate) -{ - GateRef left = acc_.GetValueIn(gate, 0); - GateRef right = acc_.GetValueIn(gate, 1); - if (HasNumberType(gate, left, right)) { - SpeculateNumbers(gate); - } -} - -void TSHCRLowering::LowerTypedDiv(GateRef gate) +template +void TSHCRLowering::LowerTypedUnOp(GateRef gate) { - GateRef left = acc_.GetValueIn(gate, 0); - GateRef right = acc_.GetValueIn(gate, 1); - if (HasNumberType(gate, left, right)) { - SpeculateNumbers(gate); + GateRef value = acc_.GetValueIn(gate, 0); + if (HasNumberType(gate, value)) { + SpeculateNumber(gate); } } @@ -335,107 +355,78 @@ void TSHCRLowering::LowerTypedStrictEq(GateRef gate) GateType rightType = acc_.GetGateType(right); GateType gateType = acc_.GetGateType(gate); PGOSampleType sampleType = acc_.TryGetPGOType(gate); - if (acc_.IsConstantUndefined(left) || acc_.IsConstantUndefined(right) || HasNumberType(gate, left, right)) { + if (acc_.IsConstantUndefined(left) || acc_.IsConstantUndefined(right) || HasNumberType(gate, left, right, false)) { GateRef result = builder_.TypedBinaryOp( left, right, leftType, rightType, gateType, sampleType); acc_.ReplaceHirAndDeleteIfException(gate, builder_.GetStateDepend(), result); } } -void TSHCRLowering::LowerTypedShl(GateRef gate) -{ - GateRef left = acc_.GetValueIn(gate, 0); - GateRef right = acc_.GetValueIn(gate, 1); - if (HasNumberType(gate, left, right)) { - SpeculateNumbers(gate); - } -} - -void TSHCRLowering::LowerTypedShr(GateRef gate) -{ - GateRef left = acc_.GetValueIn(gate, 0); - GateRef right = acc_.GetValueIn(gate, 1); - if (HasNumberType(gate, left, right)) { - SpeculateNumbers(gate); - } -} - -void TSHCRLowering::LowerTypedAshr(GateRef gate) -{ - GateRef left = acc_.GetValueIn(gate, 0); - GateRef right = acc_.GetValueIn(gate, 1); - if (HasNumberType(gate, left, right)) { - SpeculateNumbers(gate); - } -} - -void TSHCRLowering::LowerTypedAnd(GateRef gate) -{ - GateRef left = acc_.GetValueIn(gate, 0); - GateRef right = acc_.GetValueIn(gate, 1); - if (HasNumberType(gate, left, right)) { - SpeculateNumbers(gate); - } -} - -void TSHCRLowering::LowerTypedOr(GateRef gate) -{ - GateRef left = acc_.GetValueIn(gate, 0); - GateRef right = acc_.GetValueIn(gate, 1); - if (HasNumberType(gate, left, right)) { - SpeculateNumbers(gate); - } -} - -void TSHCRLowering::LowerTypedXor(GateRef gate) -{ - GateRef left = acc_.GetValueIn(gate, 0); - GateRef right = acc_.GetValueIn(gate, 1); - if (HasNumberType(gate, left, right)) { - SpeculateNumbers(gate); - } -} - -void TSHCRLowering::LowerTypedInc(GateRef gate) -{ - GateRef value = acc_.GetValueIn(gate, 0); - if (HasNumberType(gate, value)) { - SpeculateNumber(gate); - } -} - -void TSHCRLowering::LowerTypedDec(GateRef gate) -{ - GateRef value = acc_.GetValueIn(gate, 0); - if (HasNumberType(gate, value)) { - SpeculateNumber(gate); - } -} - bool TSHCRLowering::HasNumberType(GateRef gate, GateRef value) const { GateType valueType = acc_.GetGateType(value); PGOSampleType sampleType = acc_.TryGetPGOType(gate); if (sampleType.IsNumber() || - (sampleType.IsNone() && valueType.IsNumberType())) { + (sampleType.IsNone() && valueType.IsPrimitiveNumberType())) { return true; } return false; } -bool TSHCRLowering::HasNumberType(GateRef gate, GateRef left, GateRef right) const +bool TSHCRLowering::HasNumberType(GateRef gate, GateRef left, GateRef right, bool convertNumberType) const { GateType leftType = acc_.GetGateType(left); GateType rightType = acc_.GetGateType(right); PGOSampleType sampleType = acc_.TryGetPGOType(gate); - if (sampleType.IsNumber() || - (sampleType.IsNone() && leftType.IsNumberType() && rightType.IsNumberType())) { + if (sampleType.IsNumber()) { + return true; + } else if (convertNumberType && sampleType.IsNone() && leftType.IsPrimitiveNumberType() && + rightType.IsPrimitiveNumberType()) { + return true; + } else if (!convertNumberType && sampleType.IsNone() && leftType.IsNumberType() && rightType.IsNumberType()) { + return true; + } else { + return false; + } + return false; +} + +bool TSHCRLowering::HasStringType([[maybe_unused]] GateRef gate, GateRef left, GateRef right) const +{ + GateType leftType = acc_.GetGateType(left); + GateType rightType = acc_.GetGateType(right); + // PGO has not collected string type yet, so skip the check for whether the sampleType is string. + if (leftType.IsStringType() && rightType.IsStringType()) { return true; } return false; } +template +void TSHCRLowering::SpeculateStrings(GateRef gate) +{ + if (Op == TypedBinOp::TYPED_EQ) { + AddProfiling(gate); + GateRef left = acc_.GetValueIn(gate, 0); + GateRef right = acc_.GetValueIn(gate, 1); + if (!IsTrustedStringType(left)) { + builder_.EcmaStringCheck(left); + } + if (!IsTrustedStringType(right)) { + builder_.EcmaStringCheck(right); + } + GateType leftType = acc_.GetGateType(left); + GateType rightType = acc_.GetGateType(right); + GateType gateType = acc_.GetGateType(gate); + PGOSampleType sampleType = acc_.TryGetPGOType(gate); + pgoTypeLog_.CollectGateTypeLogInfo(gate, true); + + GateRef result = builder_.TypedBinaryOp(left, right, leftType, rightType, gateType, sampleType); + acc_.ReplaceHirAndDeleteIfException(gate, builder_.GetStateDepend(), result); + } +} + template void TSHCRLowering::SpeculateNumbers(GateRef gate) { @@ -512,40 +503,15 @@ void TSHCRLowering::SpeculateConditionJump(GateRef gate, bool flag) GateRef value = acc_.GetValueIn(gate, 0); GateType valueType = acc_.GetGateType(value); GateRef jump = Circuit::NullGate(); + PGOSampleType sampleType = acc_.TryGetPGOType(value); if (flag) { - jump = builder_.TypedConditionJump(value, valueType); + jump = builder_.TypedConditionJump(value, valueType, sampleType.GetWeight()); } else { - jump = builder_.TypedConditionJump(value, valueType); + jump = builder_.TypedConditionJump(value, valueType, sampleType.GetWeight()); } acc_.ReplaceGate(gate, jump, jump, Circuit::NullGate()); } -void TSHCRLowering::LowerTypedNeg(GateRef gate) -{ - GateRef value = acc_.GetValueIn(gate, 0); - if (HasNumberType(gate, value)) { - SpeculateNumber(gate); - } -} - -void TSHCRLowering::LowerTypedNot(GateRef gate) -{ - GateRef value = acc_.GetValueIn(gate, 0); - if (HasNumberType(gate, value)) { - SpeculateNumber(gate); - } -} - -void TSHCRLowering::LowerTypedLdArrayLength(GateRef gate) -{ - AddProfiling(gate); - GateRef array = acc_.GetValueIn(gate, 2); - builder_.StableArrayCheck(array); - - GateRef result = builder_.LoadArrayLength(array); - acc_.ReplaceHirAndDeleteIfException(gate, builder_.GetStateDepend(), result); -} - void TSHCRLowering::DeleteConstDataIfNoUser(GateRef gate) { auto uses = acc_.Uses(gate); @@ -559,77 +525,13 @@ void TSHCRLowering::LowerTypedLdObjByName(GateRef gate) { DISALLOW_GARBAGE_COLLECTION; auto constData = acc_.GetValueIn(gate, 1); // 1: valueIn 1 - uint16_t propIndex = acc_.GetConstantValue(constData); - auto thread = tsManager_->GetEcmaVM()->GetJSThread(); - auto prop = tsManager_->GetStringFromConstantPool(propIndex); + uint16_t keyIndex = acc_.GetConstantValue(constData); + JSTaggedValue key = tsManager_->GetStringFromConstantPool(keyIndex); // 3: number of value inputs ASSERT(acc_.GetNumValueIn(gate) == 3); GateRef receiver = acc_.GetValueIn(gate, 2); // 2: acc or this object - GateType receiverType = acc_.GetGateType(receiver); - receiverType = tsManager_->TryNarrowUnionType(receiverType); - if (tsManager_->IsArrayTypeKind(receiverType)) { - EcmaString *propString = EcmaString::Cast(prop.GetTaggedObject()); - EcmaString *lengthString = EcmaString::Cast(thread->GlobalConstants()->GetLengthString().GetTaggedObject()); - if (propString == lengthString) { - LowerTypedLdArrayLength(gate); - return; - } - } - if (tsManager_->IsClassInstanceTypeKind(receiverType)) { - int hclassIndex = tsManager_->GetHClassIndexByInstanceGateType(receiverType); - if (hclassIndex == -1) { // slowpath - return; - } - JSHClass *hclass = JSHClass::Cast(tsManager_->GetHClassFromCache(hclassIndex).GetTaggedObject()); - if (!hclass->HasTSSubtyping()) { // slowpath - return; - } - - PropertyLookupResult plr = JSHClass::LookupPropertyInAotHClass(thread, hclass, prop); - if (!plr.IsFound()) { // slowpath - return; - } - - AddProfiling(gate); - - GateRef hclassIndexGate = builder_.IntPtr(hclassIndex); - builder_.ObjectTypeCheck(receiverType, receiver, hclassIndexGate); - - GateRef pfrGate = builder_.Int32(plr.GetData()); - GateRef result = Circuit::NullGate(); - if (LIKELY(!plr.IsAccessor())) { - result = builder_.LoadProperty(receiver, pfrGate, plr.IsVtable()); - if (UNLIKELY(IsVerifyVTbale())) { - AddVTableLoadVerifer(gate, result); - } - } else { - result = builder_.CallGetter(gate, receiver, pfrGate); - } - - acc_.ReplaceHirAndDeleteIfException(gate, builder_.GetStateDepend(), result); - DeleteConstDataIfNoUser(constData); - return; - } - - int hclassIndex = tsManager_->GetConstructorHClassIndexByClassGateType(receiverType); - if (hclassIndex == -1) { // slowpath - return; - } - JSHClass *hclass = JSHClass::Cast(tsManager_->GetHClassFromCache(hclassIndex).GetTaggedObject()); - - PropertyLookupResult plr = JSHClass::LookupPropertyInAotHClass(thread, hclass, prop); - if (!plr.IsFound() || !plr.IsLocal() || plr.IsAccessor()) { // slowpath - return; - } - AddProfiling(gate); - GateRef hclassIndexGate = builder_.IntPtr(hclassIndex); - builder_.ObjectTypeCheck(receiverType, receiver, hclassIndexGate); - - GateRef pfrGate = builder_.Int32(plr.GetData()); - GateRef result = builder_.LoadProperty(receiver, pfrGate, false); - - acc_.ReplaceHirAndDeleteIfException(gate, builder_.GetStateDepend(), result); + LowerNamedAccess(gate, receiver, AccessMode::LOAD, key, Circuit::NullGate()); DeleteConstDataIfNoUser(constData); } @@ -637,9 +539,8 @@ void TSHCRLowering::LowerTypedStObjByName(GateRef gate, bool isThis) { DISALLOW_GARBAGE_COLLECTION; auto constData = acc_.GetValueIn(gate, 1); // 1: valueIn 1 - uint16_t propIndex = acc_.GetConstantValue(constData); - auto thread = tsManager_->GetEcmaVM()->GetJSThread(); - auto prop = tsManager_->GetStringFromConstantPool(propIndex); + uint16_t keyIndex = acc_.GetConstantValue(constData); + JSTaggedValue key = tsManager_->GetStringFromConstantPool(keyIndex); GateRef receiver = Circuit::NullGate(); GateRef value = Circuit::NullGate(); @@ -654,94 +555,262 @@ void TSHCRLowering::LowerTypedStObjByName(GateRef gate, bool isThis) receiver = acc_.GetValueIn(gate, 2); // 2: receiver value = acc_.GetValueIn(gate, 3); // 3: acc } + LowerNamedAccess(gate, receiver, AccessMode::STORE, key, value); + DeleteConstDataIfNoUser(constData); +} + +void TSHCRLowering::LowerNamedAccess(GateRef gate, GateRef receiver, AccessMode accessMode, JSTaggedValue key, + GateRef value) +{ + DISALLOW_GARBAGE_COLLECTION; GateType receiverType = acc_.GetGateType(receiver); receiverType = tsManager_->TryNarrowUnionType(receiverType); - if (tsManager_->IsClassInstanceTypeKind(receiverType)) { - int hclassIndex = tsManager_->GetHClassIndexByInstanceGateType(receiverType); - if (hclassIndex == -1) { // slowpath - return; - } - JSHClass *hclass = JSHClass::Cast(tsManager_->GetHClassFromCache(hclassIndex).GetTaggedObject()); - if (!hclass->HasTSSubtyping()) { // slowpath - return; - } + if (accessMode == AccessMode::LOAD && TryLowerTypedLdObjByNameForBuiltin(gate, receiverType, key)) { + return; + } - PropertyLookupResult plr = JSHClass::LookupPropertyInAotHClass(thread, hclass, prop); - if (!plr.IsFound() || plr.IsFunction()) { // slowpath - return; + ObjectAccessHelper accessHelper(tsManager_, accessMode, receiver, receiverType, key, value); + ChunkVector infos(circuit_->chunk()); + bool continuation = accessHelper.Compute(infos); + if (!continuation) { + return; // slowpath + } + + ASSERT(!infos.empty()); + AddProfiling(gate); + + // If all elements of the array are objects, and receiver is one of the elements, + // no HeapObjectCheck is required. + bool isHeapObject = acc_.IsHeapObjectFromElementsKind(receiver); + + // monomorphic + if (infos.size() == 1) { + int hclassIndex = infos[0].HClassIndex(); + PropertyLookupResult plr = infos[0].Plr(); + if (!Uncheck()) { + GateRef hclassIndexGate = builder_.IntPtr(hclassIndex); + builder_.ObjectTypeCheck(infos[0].Type(), isHeapObject, receiver, hclassIndexGate); } - AddProfiling(gate); + GateRef result = BuildNamedPropertyAccess(gate, accessHelper, plr); + acc_.ReplaceHirAndDeleteIfException(gate, builder_.GetStateDepend(), result); + return; + } + // polymorphic + size_t size = infos.size(); + GateRef fallthroughState = builder_.GetState(); + GateRef fallthroughDepend = builder_.GetDepend(); + std::vector values(size + 1, Circuit::NullGate()); // +1: state for value selector + std::vector depends(size + 1, Circuit::NullGate()); // +1: state for depend selector + std::vector states(size, Circuit::NullGate()); + for (size_t i = 0; i < size; ++i) { + GateType type = infos[i].Type(); + int hclassIndex = infos[i].HClassIndex(); + PropertyLookupResult plr = infos[i].Plr(); GateRef hclassIndexGate = builder_.IntPtr(hclassIndex); - builder_.ObjectTypeCheck(receiverType, receiver, hclassIndexGate); - GateRef pfrGate = builder_.Int32(plr.GetData()); - if (LIKELY(plr.IsLocal())) { - GateRef store = builder_.StoreProperty(receiver, pfrGate, value); - if (UNLIKELY(IsVerifyVTbale())) { - AddVTableStoreVerifer(gate, store, isThis); - } + builder_.SetState(fallthroughState); + builder_.SetDepend(fallthroughDepend); + if (i == size - 1) { + builder_.ObjectTypeCheck(type, isHeapObject, receiver, hclassIndexGate); + fallthroughState = Circuit::NullGate(); + fallthroughDepend = Circuit::NullGate(); } else { - builder_.CallSetter(gate, receiver, pfrGate, value); + GateRef compare = builder_.ObjectTypeCompare(type, isHeapObject, receiver, hclassIndexGate); + GateRef branch = builder_.Branch(builder_.GetState(), compare); + GateRef ifTrue = builder_.IfTrue(branch); + GateRef ifFalse = builder_.IfFalse(branch); + GateRef tDepend = builder_.DependRelay(ifTrue, builder_.GetDepend()); + fallthroughState = ifFalse; + fallthroughDepend = builder_.DependRelay(ifFalse, builder_.GetDepend()); + builder_.SetState(ifTrue); + builder_.SetDepend(tDepend); } - acc_.ReplaceHirAndDeleteIfException(gate, builder_.GetStateDepend(), Circuit::NullGate()); - DeleteConstDataIfNoUser(constData); - return; + values[i + 1] = BuildNamedPropertyAccess(gate, accessHelper, plr); + depends[i + 1] = builder_.GetDepend(); + states[i] = builder_.GetState(); + } + ASSERT(fallthroughState == Circuit::NullGate()); + ASSERT(fallthroughDepend == Circuit::NullGate()); + GateRef mergeState = circuit_->NewGate(circuit_->Merge(size), states); + depends[0] = mergeState; + values[0] = mergeState; + GateRef dependSelector = circuit_->NewGate(circuit_->DependSelector(size), depends); + GateRef result = accessHelper.IsLoading() ? + circuit_->NewGate(circuit_->ValueSelector(size), MachineType::I64, size + 1, values.data(), GateType::AnyType()) + : Circuit::NullGate(); + acc_.ReplaceHirAndDeleteIfException(gate, StateDepend(mergeState, dependSelector), result); +} + +GateRef TSHCRLowering::BuildNamedPropertyAccess(GateRef hir, ObjectAccessHelper accessHelper, PropertyLookupResult plr) +{ + GateRef receiver = accessHelper.GetReceiver(); + GateRef plrGate = builder_.Int32(plr.GetData()); + GateRef result = Circuit::NullGate(); + + AccessMode mode = accessHelper.GetAccessMode(); + switch (mode) { + case AccessMode::LOAD: { + if (LIKELY(!plr.IsAccessor())) { + result = builder_.LoadProperty(receiver, plrGate, plr.IsFunction()); + if (UNLIKELY(IsVerifyVTbale())) { + BuildNamedPropertyAccessVerifier(hir, receiver, mode, result); + } + } else { + result = builder_.CallGetter(hir, receiver, plrGate); + } + } + break; + case AccessMode::STORE: { + GateRef value = accessHelper.GetValue(); + if (LIKELY(plr.IsLocal())) { + builder_.StoreProperty(receiver, plrGate, value); + if (UNLIKELY(IsVerifyVTbale())) { + BuildNamedPropertyAccessVerifier(hir, receiver, mode, value); + } + } else { + builder_.CallSetter(hir, receiver, plrGate, value); + } + } + break; + default: + break; } - int hclassIndex = tsManager_->GetConstructorHClassIndexByClassGateType(receiverType); - if (hclassIndex == -1) { // slowpath - return; + + return result; +} + +void TSHCRLowering::BuildNamedPropertyAccessVerifier(GateRef gate, GateRef receiver, AccessMode mode, GateRef value) +{ + GateRef constData = acc_.GetValueIn(gate, 1); + uint16_t keyIndex = acc_.GetConstantValue(constData); + GateRef func = argAcc_.GetFrameArgsIn(gate, FrameArgIdx::FUNC); + GateRef constPool = builder_.GetConstPool(func); + GateRef key = builder_.GetValueFromTaggedArray(constPool, builder_.Int32(keyIndex)); + int stubId = mode == AccessMode::LOAD ? RTSTUB_ID(VerifyVTableLoading) : RTSTUB_ID(VerifyVTableStoring); + builder_.CallRuntime(glue_, stubId, builder_.GetDepend(), { receiver, key, value }, gate); +} + +bool TSHCRLowering::TryLowerTypedLdObjByNameForBuiltin(GateRef gate, GateType receiverType, JSTaggedValue key) +{ + EcmaString *propString = EcmaString::Cast(key.GetTaggedObject()); + EcmaString *lengthString = EcmaString::Cast(thread_->GlobalConstants()->GetLengthString().GetTaggedObject()); + if (propString == lengthString) { + if (tsManager_->IsArrayTypeKind(receiverType)) { + LowerTypedLdArrayLength(gate); + return true; + } else if (tsManager_->IsValidTypedArrayType(receiverType)) { + LowerTypedLdTypedArrayLength(gate); + return true; + } else if (receiverType.IsStringType()) { + LowerTypedLdStringLength(gate); + return true; + } } - JSHClass *hclass = JSHClass::Cast(tsManager_->GetHClassFromCache(hclassIndex).GetTaggedObject()); + return TryLowerTypedLdObjByNameForBuiltinMethod(gate, receiverType, key); +} - PropertyLookupResult plr = JSHClass::LookupPropertyInAotHClass(thread, hclass, prop); - if (!plr.IsFound() || !plr.IsLocal() || plr.IsAccessor() || !plr.IsWritable()) { // slowpath - return; +bool TSHCRLowering::IsCreateArray(GateRef gate) +{ + if (acc_.GetOpCode(gate) != OpCode::JS_BYTECODE) { + return false; } + EcmaOpcode ecmaop = acc_.GetByteCodeOpcode(gate); + switch (ecmaop) { + case EcmaOpcode::CREATEEMPTYARRAY_IMM8: + case EcmaOpcode::CREATEEMPTYARRAY_IMM16: + case EcmaOpcode::CREATEARRAYWITHBUFFER_IMM8_ID16: + case EcmaOpcode::CREATEARRAYWITHBUFFER_IMM16_ID16: + return true; + default: + return false; + } + UNREACHABLE(); + return false; +} + +void TSHCRLowering::LowerTypedLdArrayLength(GateRef gate) +{ AddProfiling(gate); - GateRef hclassIndexGate = builder_.IntPtr(hclassIndex); - builder_.ObjectTypeCheck(receiverType, receiver, hclassIndexGate); + GateRef array = acc_.GetValueIn(gate, 2); + if (!Uncheck()) { + ElementsKind kind = acc_.TryGetElementsKind(gate); + if (!IsCreateArray(array)) { + builder_.StableArrayCheck(array, kind, ArrayMetaDataAccessor::Mode::LOAD_LENGTH); + } + } - GateRef pfrGate = builder_.Int32(plr.GetData()); - builder_.StoreProperty(receiver, pfrGate, value); - acc_.ReplaceHirAndDeleteIfException(gate, builder_.GetStateDepend(), Circuit::NullGate()); - DeleteConstDataIfNoUser(constData); + GateRef result = builder_.LoadArrayLength(array); + acc_.ReplaceHirAndDeleteIfException(gate, builder_.GetStateDepend(), result); } -void TSHCRLowering::LowerTypedLdObjByIndex(GateRef gate) +void TSHCRLowering::LowerTypedLdTypedArrayLength(GateRef gate) { - // 2: number of value inputs - ASSERT(acc_.GetNumValueIn(gate) == 2); - GateRef receiver = acc_.GetValueIn(gate, 1); - GateType receiverType = acc_.GetGateType(receiver); - receiverType = tsManager_->TryNarrowUnionType(receiverType); - if (!tsManager_->IsFloat32ArrayType(receiverType)) { // slowpath - return; + AddProfiling(gate); + GateRef array = acc_.GetValueIn(gate, 2); + GateType arrayType = acc_.GetGateType(array); + arrayType = tsManager_->TryNarrowUnionType(arrayType); + if (!Uncheck()) { + builder_.TypedArrayCheck(arrayType, array); } + GateRef result = builder_.LoadTypedArrayLength(arrayType, array); + acc_.ReplaceHirAndDeleteIfException(gate, builder_.GetStateDepend(), result); +} +void TSHCRLowering::LowerTypedLdStringLength(GateRef gate) +{ AddProfiling(gate); + GateRef str = acc_.GetValueIn(gate, 2); + if (!Uncheck()) { + builder_.EcmaStringCheck(str); + } + GateRef result = builder_.LoadStringLength(str); + acc_.ReplaceHirAndDeleteIfException(gate, builder_.GetStateDepend(), result); +} - if (tsManager_->IsFloat32ArrayType(receiverType)) { - builder_.TypedArrayCheck(receiverType, receiver); - } else { - LOG_ECMA(FATAL) << "this branch is unreachable"; - UNREACHABLE(); +bool TSHCRLowering::TryLowerTypedLdObjByNameForBuiltinMethod(GateRef gate, GateType receiverType, JSTaggedValue key) +{ + JSHandle globalEnv = thread_->GetEcmaVM()->GetGlobalEnv(); + if (receiverType.IsStringType()) { + JSHClass *stringPhc = globalEnv->GetStringPrototype()->GetTaggedObject()->GetClass(); + PropertyLookupResult plr = JSHClass::LookupPropertyInBuiltinPrototypeHClass(thread_, stringPhc, key); + // Unable to handle accessor at the moment + if (!plr.IsFound() || plr.IsAccessor()) { + return false; + } + AddProfiling(gate); + GateRef str = acc_.GetValueIn(gate, 2); + if (!Uncheck()) { + builder_.EcmaStringCheck(str); + } + GateRef plrGate = builder_.Int32(plr.GetData()); + GateRef strPrototype = builder_.GetGlobalEnvObj(builder_.GetGlobalEnv(), GlobalEnv::STRING_PROTOTYPE_INDEX); + GateRef result = builder_.LoadProperty(strPrototype, plrGate, plr.IsFunction()); + acc_.ReplaceHirAndDeleteIfException(gate, builder_.GetStateDepend(), result); + return true; } - GateRef index = acc_.GetValueIn(gate, 0); - uint32_t indexValue = static_cast(acc_.GetConstantValue(index)); - index = builder_.Int32(indexValue); - builder_.IndexCheck(receiverType, receiver, index); + return false; +} +void TSHCRLowering::LowerTypedLdObjByIndex(GateRef gate) +{ + // 2: number of value inputs + ASSERT(acc_.GetNumValueIn(gate) == 2); + GateRef receiver = acc_.GetValueIn(gate, 1); + GateType receiverType = acc_.GetGateType(receiver); + receiverType = tsManager_->TryNarrowUnionType(receiverType); GateRef result = Circuit::NullGate(); - if (tsManager_->IsFloat32ArrayType(receiverType)) { - result = builder_.LoadElement(receiver, index); + if (tsManager_->IsValidTypedArrayType(receiverType)) { + AddProfiling(gate); + GateRef index = acc_.GetValueIn(gate, 0); + uint32_t indexValue = static_cast(acc_.GetConstantValue(index)); + index = builder_.Int32(indexValue); + result = LoadTypedArrayByIndex(receiver, index); } else { - LOG_ECMA(FATAL) << "this branch is unreachable"; - UNREACHABLE(); + return; // slowpath } - acc_.ReplaceHirAndDeleteIfException(gate, builder_.GetStateDepend(), result); } @@ -754,14 +823,17 @@ void TSHCRLowering::LowerTypedStObjByIndex(GateRef gate) GateType receiverType = acc_.GetGateType(receiver); GateType valueType = acc_.GetGateType(value); receiverType = tsManager_->TryNarrowUnionType(receiverType); - if ((!tsManager_->IsFloat32ArrayType(receiverType)) || (!valueType.IsNumberType())) { // slowpath + if ((!tsManager_->IsBuiltinInstanceType(BuiltinTypeId::FLOAT32_ARRAY, receiverType)) || + (!valueType.IsNumberType())) { // slowpath return; } AddProfiling(gate); - if (tsManager_->IsFloat32ArrayType(receiverType)) { - builder_.TypedArrayCheck(receiverType, receiver); + if (tsManager_->IsBuiltinInstanceType(BuiltinTypeId::FLOAT32_ARRAY, receiverType)) { + if (!Uncheck()) { + builder_.TypedArrayCheck(receiverType, receiver); + } } else { LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); @@ -769,9 +841,12 @@ void TSHCRLowering::LowerTypedStObjByIndex(GateRef gate) GateRef index = acc_.GetValueIn(gate, 1); uint32_t indexValue = static_cast(acc_.GetConstantValue(index)); index = builder_.Int32(indexValue); - builder_.IndexCheck(receiverType, receiver, index); + auto length = builder_.LoadTypedArrayLength(receiverType, receiver); + if (!Uncheck()) { + builder_.IndexCheck(receiverType, length, index); + } - if (tsManager_->IsFloat32ArrayType(receiverType)) { + if (tsManager_->IsBuiltinInstanceType(BuiltinTypeId::FLOAT32_ARRAY, receiverType)) { builder_.StoreElement(receiver, index, value); } else { LOG_ECMA(FATAL) << "this branch is unreachable"; @@ -799,18 +874,168 @@ void TSHCRLowering::LowerTypedLdObjByValue(GateRef gate, bool isThis) GateType receiverType = acc_.GetGateType(receiver); GateType propKeyType = acc_.GetGateType(propKey); receiverType = tsManager_->TryNarrowUnionType(receiverType); - if (!tsManager_->IsArrayTypeKind(receiverType) || !propKeyType.IsNumberType()) { // slowpath - return; + if (!propKeyType.IsNumberType()) { + return; // slowpath } - AddProfiling(gate); + GateRef result = Circuit::NullGate(); + if (receiverType.IsStringType()) { + AddProfiling(gate); + result = LoadStringByIndex(receiver, propKey); + } else if (tsManager_->IsArrayTypeKind(receiverType)) { + AddProfiling(gate); + ElementsKind kind = acc_.TryGetArrayElementsKind(gate); + result = LoadJSArrayByIndex(receiver, propKey, kind); + } else if (tsManager_->IsValidTypedArrayType(receiverType)) { + AddProfiling(gate); + result = LoadTypedArrayByIndex(receiver, propKey); + } else { + return; // slowpath + } + acc_.ReplaceHirAndDeleteIfException(gate, builder_.GetStateDepend(), result); +} - builder_.StableArrayCheck(receiver); - GateRef length = builder_.LoadArrayLength(receiver); - propKey = builder_.IndexCheck(receiverType, length, propKey); - GateRef result = builder_.LoadElement(receiver, propKey); +GateRef TSHCRLowering::LoadStringByIndex(GateRef receiver, GateRef propKey) +{ + if (!Uncheck()) { + GateType receiverType = acc_.GetGateType(receiver); + receiverType = tsManager_->TryNarrowUnionType(receiverType); + builder_.EcmaStringCheck(receiver); + GateRef length = builder_.LoadStringLength(receiver); + propKey = builder_.IndexCheck(receiverType, length, propKey); + receiver = builder_.FlattenTreeStringCheck(receiver); + } + return builder_.LoadElement(receiver, propKey); +} - acc_.ReplaceHirAndDeleteIfException(gate, builder_.GetStateDepend(), result); +GateRef TSHCRLowering::LoadJSArrayByIndex(GateRef receiver, GateRef propKey, ElementsKind kind) +{ + if (!Uncheck()) { + GateType receiverType = acc_.GetGateType(receiver); + receiverType = tsManager_->TryNarrowUnionType(receiverType); + if (!IsCreateArray(receiver)) { + builder_.StableArrayCheck(receiver, kind, ArrayMetaDataAccessor::Mode::LOAD_ELEMENT); + } + GateRef length = builder_.LoadArrayLength(receiver); + propKey = builder_.IndexCheck(receiverType, length, propKey); + } + + GateRef result = Circuit::NullGate(); + if (Elements::IsInt(kind)) { + result = builder_.LoadElement(receiver, propKey); + } else if (Elements::IsNumber(kind)) { + result = builder_.LoadElement(receiver, propKey); + } else if (Elements::IsObject(kind)) { + result = builder_.LoadElement(receiver, propKey); + } else if (!Elements::IsHole(kind)) { + result = builder_.LoadElement(receiver, propKey); + } else { + result = builder_.LoadElement(receiver, propKey); + } + return result; +} + +GateRef TSHCRLowering::LoadTypedArrayByIndex(GateRef receiver, GateRef propKey) +{ + GateType receiverType = acc_.GetGateType(receiver); + receiverType = tsManager_->TryNarrowUnionType(receiverType); + if (!Uncheck()) { + builder_.TypedArrayCheck(receiverType, receiver); + GateRef length = builder_.LoadTypedArrayLength(receiverType, receiver); + propKey = builder_.IndexCheck(receiverType, length, propKey); + } + auto builtinTypeId = tsManager_->GetTypedArrayBuiltinId(receiverType); + switch (builtinTypeId) { + case BuiltinTypeId::INT8_ARRAY: + return builder_.LoadElement(receiver, propKey); + case BuiltinTypeId::UINT8_ARRAY: + return builder_.LoadElement(receiver, propKey); + case BuiltinTypeId::UINT8_CLAMPED_ARRAY: + return builder_.LoadElement(receiver, propKey); + case BuiltinTypeId::INT16_ARRAY: + return builder_.LoadElement(receiver, propKey); + case BuiltinTypeId::UINT16_ARRAY: + return builder_.LoadElement(receiver, propKey); + case BuiltinTypeId::INT32_ARRAY: + return builder_.LoadElement(receiver, propKey); + case BuiltinTypeId::UINT32_ARRAY: + return builder_.LoadElement(receiver, propKey); + case BuiltinTypeId::FLOAT32_ARRAY: + return builder_.LoadElement(receiver, propKey); + case BuiltinTypeId::FLOAT64_ARRAY: + return builder_.LoadElement(receiver, propKey); + default: + LOG_ECMA(FATAL) << "this branch is unreachable"; + UNREACHABLE(); + } + + return Circuit::NullGate(); +} + +void TSHCRLowering::StoreJSArrayByIndex(GateRef receiver, GateRef propKey, GateRef value, ElementsKind kind) +{ + if (!Uncheck()) { + GateType receiverType = acc_.GetGateType(receiver); + receiverType = tsManager_->TryNarrowUnionType(receiverType); + if (!IsCreateArray(receiver)) { + builder_.StableArrayCheck(receiver, kind, ArrayMetaDataAccessor::Mode::STORE_ELEMENT); + } + GateRef length = builder_.LoadArrayLength(receiver); + builder_.IndexCheck(receiverType, length, propKey); + builder_.COWArrayCheck(receiver); + + if (Elements::IsObject(kind)) { + GateRef frameState = acc_.FindNearestFrameState(builder_.GetDepend()); + builder_.HeapObjectCheck(value, frameState); + } + } + builder_.StoreElement(receiver, propKey, value); +} + + +void TSHCRLowering::StoreTypedArrayByIndex(GateRef receiver, GateRef propKey, GateRef value) +{ + GateType receiverType = acc_.GetGateType(receiver); + receiverType = tsManager_->TryNarrowUnionType(receiverType); + if (!Uncheck()) { + builder_.TypedArrayCheck(receiverType, receiver); + GateRef length = builder_.LoadTypedArrayLength(receiverType, receiver); + propKey = builder_.IndexCheck(receiverType, length, propKey); + } + + auto builtinTypeId = tsManager_->GetTypedArrayBuiltinId(receiverType); + switch (builtinTypeId) { + case BuiltinTypeId::INT8_ARRAY: + builder_.StoreElement(receiver, propKey, value); + break; + case BuiltinTypeId::UINT8_ARRAY: + builder_.StoreElement(receiver, propKey, value); + break; + case BuiltinTypeId::UINT8_CLAMPED_ARRAY: + builder_.StoreElement(receiver, propKey, value); + break; + case BuiltinTypeId::INT16_ARRAY: + builder_.StoreElement(receiver, propKey, value); + break; + case BuiltinTypeId::UINT16_ARRAY: + builder_.StoreElement(receiver, propKey, value); + break; + case BuiltinTypeId::INT32_ARRAY: + builder_.StoreElement(receiver, propKey, value); + break; + case BuiltinTypeId::UINT32_ARRAY: + builder_.StoreElement(receiver, propKey, value); + break; + case BuiltinTypeId::FLOAT32_ARRAY: + builder_.StoreElement(receiver, propKey, value); + break; + case BuiltinTypeId::FLOAT64_ARRAY: + builder_.StoreElement(receiver, propKey, value); + break; + default: + LOG_ECMA(FATAL) << "this branch is unreachable"; + UNREACHABLE(); + } } void TSHCRLowering::LowerTypedStObjByValue(GateRef gate) @@ -822,15 +1047,20 @@ void TSHCRLowering::LowerTypedStObjByValue(GateRef gate) GateType receiverType = acc_.GetGateType(receiver); GateType propKeyType = acc_.GetGateType(propKey); receiverType = tsManager_->TryNarrowUnionType(receiverType); - if (!tsManager_->IsArrayTypeKind(receiverType) || !propKeyType.IsNumberType()) { // slowpath - return; + if (!propKeyType.IsNumberType()) { + return; // slowpath } - AddProfiling(gate); - builder_.StableArrayCheck(receiver); - GateRef length = builder_.LoadArrayLength(receiver); - builder_.IndexCheck(receiverType, length, propKey); - builder_.StoreElement(receiver, propKey, value); + if (tsManager_->IsArrayTypeKind(receiverType)) { + AddProfiling(gate); + ElementsKind kind = acc_.TryGetArrayElementsKind(gate); + StoreJSArrayByIndex(receiver, propKey, value, kind); + } else if (tsManager_->IsValidTypedArrayType(receiverType)) { + AddProfiling(gate); + StoreTypedArrayByIndex(receiver, propKey, value); + } else { + return; + } acc_.ReplaceHirAndDeleteIfException(gate, builder_.GetStateDepend(), Circuit::NullGate()); } @@ -840,7 +1070,7 @@ void TSHCRLowering::LowerTypedIsTrueOrFalse(GateRef gate, bool flag) ASSERT(acc_.GetNumValueIn(gate) == 1); auto value = acc_.GetValueIn(gate, 0); auto valueType = acc_.GetGateType(value); - if ((!valueType.IsNumberType()) && (!valueType.IsBooleanType())) { + if ((!valueType.IsPrimitiveNumberType()) && (!valueType.IsBooleanType())) { return; } @@ -912,62 +1142,78 @@ void TSHCRLowering::LowerTypedSuperCall(GateRef gate) acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), constructGate); } -void TSHCRLowering::SpeculateCallBuiltin(GateRef gate, GateRef func, GateRef a0, BuiltinsStubCSigns::ID id) +void TSHCRLowering::SpeculateCallBuiltin(GateRef gate, GateRef func, const std::vector &args, + BuiltinsStubCSigns::ID id, bool isThrow) { - builder_.CallTargetCheck(func, builder_.IntPtr(static_cast(id)), a0); - GateRef result = builder_.TypedCallBuiltin(gate, a0, id); + if (!Uncheck()) { + builder_.CallTargetCheck(gate, func, builder_.IntPtr(static_cast(id)), args[0]); + } - acc_.ReplaceHirAndDeleteIfException(gate, builder_.GetStateDepend(), result); + GateRef result = builder_.TypedCallBuiltin(gate, args, id); + + if (isThrow) { + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result); + } else { + acc_.ReplaceHirAndDeleteIfException(gate, builder_.GetStateDepend(), result); + } } -BuiltinsStubCSigns::ID TSHCRLowering::GetBuiltinId(GateRef func) +BuiltinsStubCSigns::ID TSHCRLowering::GetBuiltinId(BuiltinTypeId id, GateRef func) { GateType funcType = acc_.GetGateType(func); - if (!tsManager_->IsBuiltinMath(funcType)) { + if (!tsManager_->IsBuiltinObjectMethod(id, funcType)) { return BuiltinsStubCSigns::ID::NONE; } std::string name = tsManager_->GetFuncName(funcType); - BuiltinsStubCSigns::ID id = BuiltinsStubCSigns::GetBuiltinId(name); - return id; + BuiltinsStubCSigns::ID stubId = BuiltinsStubCSigns::GetBuiltinId(name); + return stubId; +} + +void TSHCRLowering::CheckCallTargetFromDefineFuncAndLowerCall(GateRef gate, GateRef func, GlobalTSTypeRef funcGt, + GateType funcType, const std::vector &args, const std::vector &argsFastCall, bool isNoGC) +{ + if (!Uncheck()) { + builder_.JSCallTargetFromDefineFuncCheck(funcType, func, gate); + } + if (tsManager_->CanFastCall(funcGt)) { + LowerFastCall(gate, func, argsFastCall, isNoGC); + } else { + LowerCall(gate, func, args, isNoGC); + } +} + +void TSHCRLowering::LowerFastCall(GateRef gate, GateRef func, + const std::vector &argsFastCall, bool isNoGC) +{ + builder_.StartCallTimer(glue_, gate, {glue_, func, builder_.True()}, true); + GateRef result = builder_.TypedFastCall(gate, argsFastCall, isNoGC); + builder_.EndCallTimer(glue_, gate, {glue_, func}, true); + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result); +} + +void TSHCRLowering::LowerCall(GateRef gate, GateRef func, + const std::vector &args, bool isNoGC) +{ + builder_.StartCallTimer(glue_, gate, {glue_, func, builder_.True()}, true); + GateRef result = builder_.TypedCall(gate, args, isNoGC); + builder_.EndCallTimer(glue_, gate, {glue_, func}, true); + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result); } void TSHCRLowering::CheckCallTargetAndLowerCall(GateRef gate, GateRef func, GlobalTSTypeRef funcGt, GateType funcType, const std::vector &args, const std::vector &argsFastCall) { if (IsLoadVtable(func)) { - if (tsManager_->CanFastCall(funcGt)) { - builder_.JSFastCallThisTargetTypeCheck(funcType, func); - builder_.StartCallTimer(glue_, gate, {glue_, func, builder_.True()}, true); - GateRef result = builder_.TypedFastCall(gate, argsFastCall); - builder_.EndCallTimer(glue_, gate, {glue_, func}, true); - acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result); - } else { - builder_.JSCallThisTargetTypeCheck(funcType, func); - builder_.StartCallTimer(glue_, gate, {glue_, func, builder_.True()}, true); - GateRef result = builder_.TypedCall(gate, args); - builder_.EndCallTimer(glue_, gate, {glue_, func}, true); - acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result); - } + CheckThisCallTargetAndLowerCall(gate, func, funcGt, funcType, args, argsFastCall); // func = a.foo, func() } else { + bool isNoGC = tsManager_->IsNoGC(funcGt); auto op = acc_.GetOpCode(func); if (!tsManager_->FastCallFlagIsVaild(funcGt)) { return; } if (op == OpCode::JS_BYTECODE && (acc_.GetByteCodeOpcode(func) == EcmaOpcode::DEFINEFUNC_IMM8_ID16_IMM8 || acc_.GetByteCodeOpcode(func) == EcmaOpcode::DEFINEFUNC_IMM16_ID16_IMM8)) { - if (tsManager_->CanFastCall(funcGt)) { - builder_.JSCallTargetFromDefineFuncCheck(funcType, func); - builder_.StartCallTimer(glue_, gate, {glue_, func, builder_.True()}, true); - GateRef result = builder_.TypedFastCall(gate, argsFastCall); - builder_.EndCallTimer(glue_, gate, {glue_, func}, true); - acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result); - } else { - builder_.JSCallTargetFromDefineFuncCheck(funcType, func); - builder_.StartCallTimer(glue_, gate, {glue_, func, builder_.True()}, true); - GateRef result = builder_.TypedCall(gate, args); - builder_.EndCallTimer(glue_, gate, {glue_, func}, true); - acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result); - } + CheckCallTargetFromDefineFuncAndLowerCall(gate, func, funcGt, funcType, args, argsFastCall, isNoGC); return; } int methodIndex = tsManager_->GetMethodIndex(funcGt); @@ -975,17 +1221,17 @@ void TSHCRLowering::CheckCallTargetAndLowerCall(GateRef gate, GateRef func, Glob return; } if (tsManager_->CanFastCall(funcGt)) { - builder_.JSFastCallTargetTypeCheck(funcType, func, builder_.IntPtr(methodIndex)); - builder_.StartCallTimer(glue_, gate, {glue_, func, builder_.True()}, true); - GateRef result = builder_.TypedFastCall(gate, argsFastCall); - builder_.EndCallTimer(glue_, gate, {glue_, func}, true); - acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result); + if (!Uncheck()) { + builder_.JSCallTargetTypeCheck(funcType, + func, builder_.IntPtr(methodIndex), gate); + } + LowerFastCall(gate, func, argsFastCall, isNoGC); } else { - builder_.JSCallTargetTypeCheck(funcType, func, builder_.IntPtr(methodIndex)); - builder_.StartCallTimer(glue_, gate, {glue_, func, builder_.True()}, true); - GateRef result = builder_.TypedCall(gate, args); - builder_.EndCallTimer(glue_, gate, {glue_, func}, true); - acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result); + if (!Uncheck()) { + builder_.JSCallTargetTypeCheck(funcType, + func, builder_.IntPtr(methodIndex), gate); + } + LowerCall(gate, func, args, isNoGC); } } } @@ -1011,10 +1257,10 @@ void TSHCRLowering::LowerTypedCallArg1(GateRef gate) } GateRef a0Value = acc_.GetValueIn(gate, 0); GateType a0Type = acc_.GetGateType(a0Value); - BuiltinsStubCSigns::ID id = GetBuiltinId(func); - if (id != BuiltinsStubCSigns::ID::NONE && a0Type.IsNumberType()) { + BuiltinsStubCSigns::ID id = GetBuiltinId(BuiltinTypeId::MATH, func); + if (IS_TYPED_BUILTINS_MATH_ID(id) && a0Type.IsNumberType()) { AddProfiling(gate); - SpeculateCallBuiltin(gate, func, a0Value, id); + SpeculateCallBuiltin(gate, func, { a0Value }, id, false); } else { GateRef actualArgc = builder_.Int64(BytecodeCallArgc::ComputeCallArgc(acc_.GetNumValueIn(gate), EcmaOpcode::CALLARG1_IMM8_V8)); @@ -1105,24 +1351,51 @@ bool TSHCRLowering::CanOptimizeAsFastCall(GateRef func) return true; } +void TSHCRLowering::CheckFastCallThisCallTarget(GateRef gate, GateRef func, GlobalTSTypeRef funcGt, + GateType funcType, bool isNoGC) +{ + if (noCheck_) { + return; + } + if (isNoGC) { + auto methodOffset = tsManager_->GetFuncMethodOffset(funcGt); + builder_.JSNoGCCallThisTargetTypeCheck(funcType, + func, builder_.IntPtr(methodOffset), gate); + } else { + builder_.JSCallThisTargetTypeCheck(funcType, + func, gate); + } +} + +void TSHCRLowering::CheckCallThisCallTarget(GateRef gate, GateRef func, GlobalTSTypeRef funcGt, + GateType funcType, bool isNoGC) +{ + if (noCheck_) { + return; + } + if (isNoGC) { + auto methodOffset = tsManager_->GetFuncMethodOffset(funcGt); + builder_.JSNoGCCallThisTargetTypeCheck(funcType, + func, builder_.IntPtr(methodOffset), gate); + } else { + builder_.JSCallThisTargetTypeCheck(funcType, + func, gate); + } +} + void TSHCRLowering::CheckThisCallTargetAndLowerCall(GateRef gate, GateRef func, GlobalTSTypeRef funcGt, GateType funcType, const std::vector &args, const std::vector &argsFastCall) { if (!tsManager_->FastCallFlagIsVaild(funcGt)) { return; } + bool isNoGC = tsManager_->IsNoGC(funcGt); if (tsManager_->CanFastCall(funcGt)) { - builder_.JSFastCallThisTargetTypeCheck(funcType, func); - builder_.StartCallTimer(glue_, gate, {glue_, func, builder_.True()}, true); - GateRef result = builder_.TypedFastCall(gate, argsFastCall); - builder_.EndCallTimer(glue_, gate, {glue_, func}, true); - acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result); + CheckFastCallThisCallTarget(gate, func, funcGt, funcType, isNoGC); + LowerFastCall(gate, func, argsFastCall, isNoGC); } else { - builder_.JSCallThisTargetTypeCheck(funcType, func); - builder_.StartCallTimer(glue_, gate, {glue_, func, builder_.True()}, true); - GateRef result = builder_.TypedCall(gate, args); - builder_.EndCallTimer(glue_, gate, {glue_, func}, true); - acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result); + CheckCallThisCallTarget(gate, func, funcGt, funcType, isNoGC); + LowerCall(gate, func, args, isNoGC); } } @@ -1131,12 +1404,19 @@ void TSHCRLowering::LowerTypedCallthis0(GateRef gate) // 2: number of value inputs ASSERT(acc_.GetNumValueIn(gate) == 2); GateRef func = acc_.GetValueIn(gate, 1); + BuiltinsStubCSigns::ID id = GetBuiltinId(BuiltinTypeId::ARRAY, func); + if (id == BuiltinsStubCSigns::ID::SORT) { + AddProfiling(gate); + GateRef thisObj = acc_.GetValueIn(gate, 0); + SpeculateCallBuiltin(gate, func, { thisObj }, id, true); + return; + } if (!CanOptimizeAsFastCall(func)) { return; } GateRef actualArgc = builder_.Int64(BytecodeCallArgc::ComputeCallArgc(acc_.GetNumValueIn(gate), EcmaOpcode::CALLTHIS0_IMM8_V8)); - LowerTypedThisCall(gate, func, actualArgc, 1); + LowerTypedThisCall(gate, func, actualArgc, 0); } void TSHCRLowering::LowerTypedCallthis1(GateRef gate) @@ -1146,17 +1426,25 @@ void TSHCRLowering::LowerTypedCallthis1(GateRef gate) GateRef a0 = acc_.GetValueIn(gate, 1); // 1:parameter index GateType a0Type = acc_.GetGateType(a0); GateRef func = acc_.GetValueIn(gate, 2); // 2:function - BuiltinsStubCSigns::ID id = GetBuiltinId(func); - if (id != BuiltinsStubCSigns::ID::NONE && a0Type.IsNumberType()) { - AddProfiling(gate); - SpeculateCallBuiltin(gate, func, a0, id); + BuiltinsStubCSigns::ID id = GetBuiltinId(BuiltinTypeId::MATH, func); + if (id == BuiltinsStubCSigns::ID::NONE) { + id = GetBuiltinId(BuiltinTypeId::JSON, func); + if (id != BuiltinsStubCSigns::ID::NONE) { + AddProfiling(gate); + SpeculateCallBuiltin(gate, func, { a0 }, id, true); + } } else { - if (!CanOptimizeAsFastCall(func)) { - return; + if (a0Type.IsNumberType()) { + AddProfiling(gate); + SpeculateCallBuiltin(gate, func, { a0 }, id, false); + } else { + if (!CanOptimizeAsFastCall(func)) { + return; + } + GateRef actualArgc = builder_.Int64(BytecodeCallArgc::ComputeCallArgc(acc_.GetNumValueIn(gate), + EcmaOpcode::CALLTHIS1_IMM8_V8_V8)); + LowerTypedThisCall(gate, func, actualArgc, 1); } - GateRef actualArgc = builder_.Int64(BytecodeCallArgc::ComputeCallArgc(acc_.GetNumValueIn(gate), - EcmaOpcode::CALLTHIS1_IMM8_V8_V8)); - LowerTypedThisCall(gate, func, actualArgc, 1); } } @@ -1178,6 +1466,17 @@ void TSHCRLowering::LowerTypedCallthis3(GateRef gate) // 5: number of value inputs ASSERT(acc_.GetNumValueIn(gate) == 5); GateRef func = acc_.GetValueIn(gate, 4); // 4: func + BuiltinsStubCSigns::ID id = GetBuiltinId(BuiltinTypeId::STRING, func); + if (id == BuiltinsStubCSigns::ID::LocaleCompare) { + AddProfiling(gate); + GateRef thisObj = acc_.GetValueIn(gate, 0); + GateRef a0 = acc_.GetValueIn(gate, 1); // 1: the first-para + GateRef a1 = acc_.GetValueIn(gate, 2); // 2: the third-para + GateRef a2 = acc_.GetValueIn(gate, 3); // 3: the fourth-para + SpeculateCallBuiltin(gate, func, { thisObj, a0, a1, a2 }, id, true); + return; + } + if (!CanOptimizeAsFastCall(func)) { return; } @@ -1224,15 +1523,6 @@ void TSHCRLowering::LowerTypedCallthisrange(GateRef gate) LowerTypedThisCall(gate, func, actualArgc, numIns - callTargetIndex - fixedInputsNum); } -void TSHCRLowering::LowerTypedCreateEmptyArray(GateRef gate) -{ - // in the future, the type of the elements in the array will be obtained through pgo, - // and the type will be used to determine whether to create a typed-array. - GateRef emptyArray = builder_.GetGlobalConstantValue(ConstantIndex::EMPTY_ARRAY_OBJECT_INDEX); - GateRef array = builder_.CreateArray(emptyArray, true); - acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), array); -} - void TSHCRLowering::AddProfiling(GateRef gate) { hitTypedOpCount_++; @@ -1303,31 +1593,25 @@ void TSHCRLowering::AddHitBytecodeCount() } } -void TSHCRLowering::AddVTableLoadVerifer(GateRef gate, GateRef value) -{ - GateRef receiver = acc_.GetValueIn(gate, 2); // 2: receiver - GateRef key = acc_.GetValueIn(gate, 1); // 1: key - - GateRef verifier = builder_.CallRuntime(glue_, RTSTUB_ID(VerifyVTableLoading), acc_.GetDep(gate), - { receiver, key, value }, gate); - acc_.SetDep(gate, verifier); -} -void TSHCRLowering::AddVTableStoreVerifer(GateRef gate, GateRef store, bool isThis) +void TSHCRLowering::LowerTypedTypeOf(GateRef gate) { - GateRef key = acc_.GetValueIn(gate, 1); - GateRef receiver = Circuit::NullGate(); - GateRef value = Circuit::NullGate(); - if (isThis) { - receiver = argAcc_.GetFrameArgsIn(gate, FrameArgIdx::THIS_OBJECT); - value = acc_.GetValueIn(gate, 2); // 2: acc - } else { - receiver = acc_.GetValueIn(gate, 2); // 2: receiver - value = acc_.GetValueIn(gate, 3); // 3: acc + // 1: number of value inputs + ASSERT(acc_.GetNumValueIn(gate) == 1); + GateRef value = acc_.GetValueIn(gate, 0); + GateType valueType = acc_.GetGateType(gate); + if (!valueType.IsDigitablePrimitiveType() && !valueType.IsStringType() && !valueType.IsSymbolType()) { + if (!tsManager_->IsFunctionTypeKind(valueType) && !tsManager_->IsObjectTypeKind(valueType) && + !tsManager_->IsClassTypeKind(valueType) && !tsManager_->IsClassInstanceTypeKind(valueType) && + !tsManager_->IsArrayTypeKind(valueType)) { + return; + } } - - GateRef verifier = builder_.CallRuntime(glue_, RTSTUB_ID(VerifyVTableStoring), store, - { receiver, key, value }, gate); - acc_.SetDep(gate, verifier); + AddProfiling(gate); + if (!Uncheck()) { + builder_.TypeOfCheck(value, valueType); + } + GateRef result = builder_.TypedTypeOf(valueType); + acc_.ReplaceHirAndDeleteIfException(gate, builder_.GetStateDepend(), result); } } // namespace panda::ecmascript diff --git a/ecmascript/compiler/ts_hcr_lowering.h b/ecmascript/compiler/ts_hcr_lowering.h index 91b1996c36c6b71e6e66d426feac7b7c54383eaa..d0775e82de79dedc4e47b9047d2bef39c2b86877 100644 --- a/ecmascript/compiler/ts_hcr_lowering.h +++ b/ecmascript/compiler/ts_hcr_lowering.h @@ -20,6 +20,7 @@ #include "ecmascript/compiler/builtins/builtins_call_signature.h" #include "ecmascript/compiler/bytecode_circuit_builder.h" #include "ecmascript/compiler/circuit_builder-inl.h" +#include "ecmascript/compiler/object_access_helper.h" #include "ecmascript/compiler/pass_manager.h" namespace panda::ecmascript::kungfu { @@ -41,7 +42,9 @@ public: methodName_(name), glue_(acc_.GetGlueFromArgList()), argAcc_(circuit), - pgoTypeLog_(circuit) {} + pgoTypeLog_(circuit), + noCheck_(ctx->GetEcmaVM()->GetJSOptions().IsCompilerNoCheck()), + thread_(ctx->GetEcmaVM()->GetJSThread()) {} ~TSHCRLowering() = default; @@ -80,26 +83,26 @@ private: void Lower(GateRef gate); template - void LowerTypedBinOp(GateRef gate); - void LowerTypedMod(GateRef gate); - void LowerTypedDiv(GateRef gate); + void LowerTypedBinOp(GateRef gate, bool convertNumberType = true); + template + void LowerTypedUnOp(GateRef gate); void LowerTypedStrictEq(GateRef gate); - void LowerTypedShl(GateRef gate); - void LowerTypedShr(GateRef gate); - void LowerTypedAshr(GateRef gate); - void LowerTypedAnd(GateRef gate); - void LowerTypedOr(GateRef gate); - void LowerTypedXor(GateRef gate); - void LowerTypedInc(GateRef gate); - void LowerTypedDec(GateRef gate); void LowerTypeToNumeric(GateRef gate); void LowerPrimitiveTypeToNumber(GateRef gate); void LowerConditionJump(GateRef gate, bool flag); - void LowerTypedNeg(GateRef gate); - void LowerTypedNot(GateRef gate); + void LowerTypedLdObjByName(GateRef gate); - void LowerTypedLdArrayLength(GateRef gate); void LowerTypedStObjByName(GateRef gate, bool isThis); + using AccessMode = ObjectAccessHelper::AccessMode; + void LowerNamedAccess(GateRef gate, GateRef receiver, AccessMode accessMode, JSTaggedValue key, GateRef value); + GateRef BuildNamedPropertyAccess(GateRef hir, ObjectAccessHelper accessHelper, PropertyLookupResult plr); + void BuildNamedPropertyAccessVerifier(GateRef gate, GateRef receiver, AccessMode mode, GateRef value); + bool TryLowerTypedLdObjByNameForBuiltin(GateRef gate, GateType receiverType, JSTaggedValue key); + bool TryLowerTypedLdObjByNameForBuiltinMethod(GateRef gate, GateType receiverType, JSTaggedValue key); + void LowerTypedLdArrayLength(GateRef gate); + void LowerTypedLdTypedArrayLength(GateRef gate); + void LowerTypedLdStringLength(GateRef gate); + void LowerTypedLdObjByIndex(GateRef gate); void LowerTypedStObjByIndex(GateRef gate); void LowerTypedLdObjByValue(GateRef gate, bool isThis); @@ -107,7 +110,6 @@ private: void LowerTypedIsTrueOrFalse(GateRef gate, bool flag); void LowerTypedNewObjRange(GateRef gate); void LowerTypedSuperCall(GateRef gate); - void LowerTypedCreateEmptyArray(GateRef gate); void LowerTypedCallArg0(GateRef gate); void LowerTypedCallArg1(GateRef gate); @@ -125,31 +127,55 @@ private: bool CanOptimizeAsFastCall(GateRef func); void CheckCallTargetAndLowerCall(GateRef gate, GateRef func, GlobalTSTypeRef funcGt, GateType funcType, const std::vector &args, const std::vector &argsFastCall); + void CheckCallTargetFromDefineFuncAndLowerCall(GateRef gate, GateRef func, GlobalTSTypeRef funcGt, + GateType funcType, const std::vector &args, const std::vector &argsFastCall, bool isNoGC); void CheckThisCallTargetAndLowerCall(GateRef gate, GateRef func, GlobalTSTypeRef funcGt, GateType funcType, const std::vector &args, const std::vector &argsFastCall); + void CheckCallThisCallTarget(GateRef gate, GateRef func, GlobalTSTypeRef funcGt, + GateType funcType, bool isNoGC); + void CheckFastCallThisCallTarget(GateRef gate, GateRef func, GlobalTSTypeRef funcGt, + GateType funcType, bool isNoGC); + void LowerFastCall(GateRef gate, GateRef func, const std::vector &argsFastCall, bool isNoGC); + void LowerCall(GateRef gate, GateRef func, const std::vector &args, bool isNoGC); + void LowerTypedTypeOf(GateRef gate); + GateRef LoadStringByIndex(GateRef receiver, GateRef propKey); + GateRef LoadJSArrayByIndex(GateRef receiver, GateRef propKey, ElementsKind kind); + GateRef LoadTypedArrayByIndex(GateRef receiver, GateRef propKey); + void StoreJSArrayByIndex(GateRef receiver, GateRef propKey, GateRef value, ElementsKind kind); + void StoreTypedArrayByIndex(GateRef receiver, GateRef propKey, GateRef value); + bool IsCreateArray(GateRef receiver); // TypeTrusted means the type of gate is already PrimitiveTypeCheck-passed, // or the gate is constant and no need to check. bool IsTrustedType(GateRef gate) const; + bool IsTrustedStringType(GateRef gate) const; bool HasNumberType(GateRef gate, GateRef value) const; - bool HasNumberType(GateRef gate, GateRef left, GateRef right) const; + bool HasNumberType(GateRef gate, GateRef left, GateRef right, bool convertNumberType = true) const; + bool HasStringType(GateRef gate, GateRef left, GateRef right) const; void AddBytecodeCount(EcmaOpcode op); void DeleteBytecodeCount(EcmaOpcode op); void AddHitBytecodeCount(); + template + void SpeculateStrings(GateRef gate); template void SpeculateNumbers(GateRef gate); template void SpeculateNumber(GateRef gate); void SpeculateConditionJump(GateRef gate, bool flag); - void SpeculateCallBuiltin(GateRef gate, GateRef func, GateRef a0, BuiltinsStubCSigns::ID Op); - BuiltinsStubCSigns::ID GetBuiltinId(GateRef func); + void SpeculateCallBuiltin(GateRef gate, GateRef func, const std::vector &args, + BuiltinsStubCSigns::ID id, bool isThrow); + BuiltinsStubCSigns::ID GetBuiltinId(BuiltinTypeId id, GateRef func); void DeleteConstDataIfNoUser(GateRef gate); void AddProfiling(GateRef gate); - void AddVTableLoadVerifer(GateRef gate, GateRef value); - void AddVTableStoreVerifer(GateRef gate, GateRef store, bool isThis); + + bool Uncheck() const + { + return noCheck_; + } + Circuit *circuit_ {nullptr}; GateAccessor acc_; CircuitBuilder builder_; @@ -166,10 +192,12 @@ private: std::string methodName_; GateRef glue_ {Circuit::NullGate()}; ArgumentAccessor argAcc_; - EcmaOpcode currentOp_; + EcmaOpcode currentOp_ {static_cast(0xff)}; PGOTypeLogList pgoTypeLog_; std::unordered_map bytecodeMap_; std::unordered_map bytecodeHitTimeMap_; + bool noCheck_ {false}; + const JSThread *thread_ {nullptr}; }; } // panda::ecmascript::kungfu #endif // ECMASCRIPT_COMPILER_TS_HCR_LOWERING_H diff --git a/ecmascript/compiler/ts_hcr_opt_pass.cpp b/ecmascript/compiler/ts_hcr_opt_pass.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0dbc803db0d54437b1dbe36871d1bee2e14d8529 --- /dev/null +++ b/ecmascript/compiler/ts_hcr_opt_pass.cpp @@ -0,0 +1,112 @@ +/* + * 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/compiler/ts_hcr_opt_pass.h" + +namespace panda::ecmascript::kungfu { + +GateRef TSHCROptPass::VisitGate(GateRef gate) +{ + auto opcode = acc_.GetOpCode(gate); + switch (opcode) { + case OpCode::TYPED_BINARY_OP: + return VisitTypedBinaryOp(gate); + default: + break; + } + return Circuit::NullGate(); +} + +GateRef TSHCROptPass::VisitTypedBinaryOp(GateRef gate) +{ + if (acc_.HasStringType(gate)) { + return VisitStringBinOp(gate); + } + return Circuit::NullGate(); +} + +GateRef TSHCROptPass::VisitStringBinOp(GateRef gate) +{ + TypedBinOp op = acc_.GetTypedBinaryOp(gate); + switch (op) { + case TypedBinOp::TYPED_EQ: + return VisitStringEqual(gate); + default: + return Circuit::NullGate(); + } +} + +GateRef TSHCROptPass::VisitStringEqual(GateRef gate) +{ + Environment env(gate, circuit_, &builder_); + GateRef left = acc_.GetValueIn(gate, 0); + GateRef right = acc_.GetValueIn(gate, 1); + if (acc_.IsConstString(left) && acc_.IsConstString(right)) { + return ConvertStringEqualToConst(left, right); + } + + if (IsSingleCharString(left) && IsSingleCharString(right)) { + return ConvertToSingleCharComparison(left, right); + } + + return Circuit::NullGate(); +} + +GateRef TSHCROptPass::ConvertStringEqualToConst(GateRef left, GateRef right) +{ + uint32_t leftId = acc_.GetStringIdFromLdaStrGate(left); + uint32_t rightId = acc_.GetStringIdFromLdaStrGate(right); + JSHandle leftStr(thread_, tsManager_->GetStringFromConstantPool(leftId)); + JSHandle rightStr(thread_, tsManager_->GetStringFromConstantPool(rightId)); + bool isEqual = EcmaStringAccessor::StringsAreEqual(thread_->GetEcmaVM(), leftStr, rightStr); + if (isEqual) { + return builder_.Boolean(true); + } + return builder_.Boolean(false); +} + +bool TSHCROptPass::IsSingleCharString(GateRef gate) +{ + if (acc_.IsConstString(gate)) { + uint32_t strId = acc_.GetStringIdFromLdaStrGate(gate); + JSTaggedValue str = tsManager_->GetStringFromConstantPool(strId); + return EcmaStringAccessor(str).GetLength() == 1; + } + return acc_.IsSingleCharGate(gate); +} + +GateRef TSHCROptPass::ConvertConstSingleCharToInt32(GateRef gate) +{ + ASSERT(acc_.IsConstString(gate)); + uint32_t strId = acc_.GetStringIdFromLdaStrGate(gate); + JSTaggedValue str = tsManager_->GetStringFromConstantPool(strId); + ASSERT(EcmaStringAccessor(str).GetLength() == 1); + uint16_t strToInt = EcmaStringAccessor(str).Get(0); + return builder_.Int32(strToInt); +} + +GateRef TSHCROptPass::ConvertToSingleCharComparison(GateRef left, GateRef right) +{ + ASSERT(!acc_.IsConstString(left) || !acc_.IsConstString(right)); + if (acc_.IsConstString(left)) { + left = ConvertConstSingleCharToInt32(left); + } else if (acc_.IsConstString(right)) { + right = ConvertConstSingleCharToInt32(right); + } + return builder_.TypedBinaryOp(left, right, GateType::IntType(), + GateType::IntType(), GateType::BooleanType(), + PGOSampleType::NoneType()); +} +} // namespace panda::ecmascript::kungfu diff --git a/ecmascript/compiler/ts_hcr_opt_pass.h b/ecmascript/compiler/ts_hcr_opt_pass.h new file mode 100644 index 0000000000000000000000000000000000000000..cd7380b0a799e5a16e5bd3d6198c93099608eace --- /dev/null +++ b/ecmascript/compiler/ts_hcr_opt_pass.h @@ -0,0 +1,65 @@ +/* + * 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_COMPILER_TS_HCR_OPT_PASS_H +#define ECMASCRIPT_COMPILER_TS_HCR_OPT_PASS_H + +#include "ecmascript/compiler/combined_pass_visitor.h" +#include "ecmascript/compiler/pass_manager.h" + +namespace panda::ecmascript::kungfu { +class TSHCROptPass : public PassVisitor { +public: + TSHCROptPass(Circuit* circuit, RPOVisitor *visitor, Chunk* chunk, + PassContext *ctx, bool enableLog, const std::string &name) + : PassVisitor(circuit, chunk, visitor), + builder_(circuit, ctx->GetCompilerConfig()), + tsManager_(ctx->GetTSManager()), + thread_(ctx->GetEcmaVM()->GetJSThread()), + enableLog_(enableLog), + methodName_(name) {} + + ~TSHCROptPass() = default; + + GateRef VisitGate(GateRef gate) override; + +private: + bool IsLogEnabled() const + { + return enableLog_; + } + + const std::string& GetMethodName() const + { + return methodName_; + } + + GateRef VisitTypedBinaryOp(GateRef gate); + + GateRef VisitStringBinOp(GateRef gate); + GateRef VisitStringEqual(GateRef gate); + bool IsSingleCharString(GateRef gate); + GateRef ConvertStringEqualToConst(GateRef left, GateRef right); + GateRef ConvertConstSingleCharToInt32(GateRef gate); + GateRef ConvertToSingleCharComparison(GateRef left, GateRef right); + + CircuitBuilder builder_; + TSManager *tsManager_ {nullptr}; + const JSThread *thread_ {nullptr}; + bool enableLog_ {false}; + std::string methodName_; +}; +} // panda::ecmascript::kungfu +#endif // ECMASCRIPT_COMPILER_TS_HCR_OPT_PASS_H diff --git a/ecmascript/compiler/ts_inline_lowering.cpp b/ecmascript/compiler/ts_inline_lowering.cpp index 43dc162709c5d441c3410afe65c540125acb0cae..428dd47c7930325eb25f7454234970f36c125f2c 100644 --- a/ecmascript/compiler/ts_inline_lowering.cpp +++ b/ecmascript/compiler/ts_inline_lowering.cpp @@ -21,31 +21,63 @@ #include "ecmascript/ts_types/ts_type.h" #include "libpandabase/utils/utf.h" #include "libpandafile/class_data_accessor-inl.h" +#include "ecmascript/ts_types/ts_type_accessor.h" namespace panda::ecmascript::kungfu { void TSInlineLowering::RunTSInlineLowering() +{ + circuit_->AdvanceTime(); + ChunkQueue workList(chunk_); + UpdateWorkList(workList); + + while (!workList.empty()) { + CallGateInfo info = workList.front(); + workList.pop(); + TryInline(info, workList); + } + CollectInlineInfo(); +} + +void TSInlineLowering::CollectInlineInfo() { std::vector gateList; circuit_->GetAllGates(gateList); for (const auto &gate : gateList) { auto op = acc_.GetOpCode(gate); - if (op == OpCode::JS_BYTECODE) { - TryInline(gate); + if (op == OpCode::FRAME_ARGS) { + GetInlinedMethodId(gate); } } } -void TSInlineLowering::TryInline(GateRef gate) +void TSInlineLowering::GetInlinedMethodId(GateRef gate) +{ + ASSERT(acc_.GetOpCode(gate) == OpCode::FRAME_ARGS); + GateRef func = acc_.GetValueIn(gate, static_cast(FrameArgIdx::FUNC)); + uint32_t methodOffset = 0; + auto funcType = acc_.GetGateType(func); + if (tsManager_->IsFunctionTypeKind(funcType)) { + GlobalTSTypeRef gt = funcType.GetGTRef(); + methodOffset = tsManager_->GetFuncMethodOffset(gt); + } + acc_.UpdateMethodOffset(gate, methodOffset); +} + +void TSInlineLowering::CandidateInlineCall(GateRef gate, ChunkQueue &workList) { EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate); switch (ecmaOpcode) { - case EcmaOpcode::CALLARG0_IMM8: - case EcmaOpcode::CALLARG1_IMM8_V8: - case EcmaOpcode::CALLARGS2_IMM8_V8_V8: - case EcmaOpcode::CALLARGS3_IMM8_V8_V8_V8: - case EcmaOpcode::CALLRANGE_IMM8_IMM8_V8: - case EcmaOpcode::WIDE_CALLRANGE_PREF_IMM16_V8: - TryInline(gate, false); + case EcmaOpcode::LDOBJBYNAME_IMM8_ID16: + case EcmaOpcode::LDOBJBYNAME_IMM16_ID16: + case EcmaOpcode::LDTHISBYNAME_IMM8_ID16: + case EcmaOpcode::LDTHISBYNAME_IMM16_ID16: + CandidateAccessor(gate, workList, CallKind::CALL_GETTER); + break; + case EcmaOpcode::STOBJBYNAME_IMM8_ID16_V8: + case EcmaOpcode::STOBJBYNAME_IMM16_ID16_V8: + case EcmaOpcode::STTHISBYNAME_IMM8_ID16: + case EcmaOpcode::STTHISBYNAME_IMM16_ID16: + CandidateAccessor(gate, workList, CallKind::CALL_SETTER); break; case EcmaOpcode::CALLTHIS0_IMM8_V8: case EcmaOpcode::CALLTHIS1_IMM8_V8_V8: @@ -53,48 +85,65 @@ void TSInlineLowering::TryInline(GateRef gate) case EcmaOpcode::CALLTHIS3_IMM8_V8_V8_V8_V8: case EcmaOpcode::CALLTHISRANGE_IMM8_IMM8_V8: case EcmaOpcode::WIDE_CALLTHISRANGE_PREF_IMM16_V8: - TryInline(gate, true); + CandidateNormalCall(gate, workList, CallKind::CALL_THIS); + break; + case EcmaOpcode::CALLARG0_IMM8: + case EcmaOpcode::CALLARG1_IMM8_V8: + case EcmaOpcode::CALLARGS2_IMM8_V8_V8: + case EcmaOpcode::CALLARGS3_IMM8_V8_V8_V8: + case EcmaOpcode::CALLRANGE_IMM8_IMM8_V8: + case EcmaOpcode::WIDE_CALLRANGE_PREF_IMM16_V8: + CandidateNormalCall(gate, workList, CallKind::CALL); break; default: break; } } -void TSInlineLowering::TryInline(GateRef gate, bool isCallThis) +void TSInlineLowering::TryInline(CallGateInfo &info, ChunkQueue &workList) { + GateRef gate = info.GetCallGate(); // inline doesn't support try-catch bool inTryCatch = FilterCallInTryCatch(gate); if (inTryCatch) { return; } - // first elem is function in old isa - size_t funcIndex = acc_.GetNumValueIn(gate) - 1; - auto funcType = acc_.GetGateType(acc_.GetValueIn(gate, funcIndex)); + MethodLiteral* inlinedMethod = nullptr; - if (tsManager_->IsFunctionTypeKind(funcType)) { - GlobalTSTypeRef gt = funcType.GetGTRef(); - auto methodOffset = tsManager_->GetFuncMethodOffset(gt); - if (methodOffset == 0 || ctx_->IsSkippedMethod(methodOffset)) { - return; - } - inlinedMethod = ctx_->GetJSPandaFile()->FindMethodLiteral(methodOffset); - if (!CheckParameter(gate, isCallThis, inlinedMethod)) { - return; - } - auto &bytecodeInfo = ctx_->GetBytecodeInfo(); - auto &methodInfo = bytecodeInfo.GetMethodList().at(methodOffset); - auto &methodPcInfos = bytecodeInfo.GetMethodPcInfos(); - auto &methodPcInfo = methodPcInfos[methodInfo.GetMethodPcInfoIndex()]; - if (methodPcInfo.pcOffsets.size() <= maxInlineBytecodesCount_ && - inlinedCall_ < MAX_INLINE_CALL_ALLOWED) { - inlineSuccess_ = FilterInlinedMethod(inlinedMethod, methodPcInfo.pcOffsets); - if (inlineSuccess_) { - GateRef glue = acc_.GetGlueFromArgList(); - CircuitRootScope scope(circuit_); - InlineFuncCheck(gate); - InlineCall(methodInfo, methodPcInfo, inlinedMethod, gate); - ReplaceCallInput(gate, isCallThis, glue, inlinedMethod); - inlinedCall_++; + GlobalTSTypeRef gt = info.GetFuncGT(); + auto methodOffset = tsManager_->GetFuncMethodOffset(gt); + if (methodOffset == 0 || ctx_->IsSkippedMethod(methodOffset)) { + return; + } + if (IsRecursiveFunc(info, methodOffset)) { + return; + } + inlinedMethod = ctx_->GetJSPandaFile()->FindMethodLiteral(methodOffset); + if (!CheckParameter(gate, info, inlinedMethod)) { + return; + } + auto &bytecodeInfo = ctx_->GetBytecodeInfo(); + auto &methodInfo = bytecodeInfo.GetMethodList().at(methodOffset); + auto &methodPcInfos = bytecodeInfo.GetMethodPcInfos(); + auto &methodPcInfo = methodPcInfos[methodInfo.GetMethodPcInfoIndex()]; + GateRef frameState = GetFrameState(info); + GateRef frameArgs = acc_.GetValueIn(frameState); + size_t inlineCallCounts = GetOrInitialInlineCounts(frameArgs); + if (IsSmallMethod(methodPcInfo.pcOffsets.size()) && !IsInlineCountsOverflow(inlineCallCounts)) { + inlineSuccess_ = FilterInlinedMethod(inlinedMethod, methodPcInfo.pcOffsets); + if (inlineSuccess_) { + SetInitCallTargetAndConstPoolId(info); + CircuitRootScope scope(circuit_); + AnalyseFastAccessor(info, methodPcInfo.pcOffsets, methodOffset); + if (!noCheck_) { + InlineCheck(info); + } + InlineCall(methodInfo, methodPcInfo, inlinedMethod, info); + UpdateInlineCounts(frameArgs, inlineCallCounts); + if (info.IsNormalCall()) { + UpdateWorkList(workList); + } else { + lastCallId_ = circuit_->GetGateCount() - 1; } } } @@ -149,7 +198,7 @@ bool TSInlineLowering::FilterInlinedMethod(MethodLiteral* method, std::vectorGetJSPandaFile(); TSManager *tsManager = ctx_->GetTSManager(); @@ -167,36 +216,47 @@ void TSInlineLowering::InlineCall(MethodInfo &methodInfo, MethodPcInfo &methodPC BytecodeCircuitBuilder builder(jsPandaFile, method, methodPCInfo, tsManager, circuit_, ctx_->GetByteCodes(), true, IsLogEnabled(), - enableTypeLowering_, fullName, recordName, ctx_->GetPfDecoder()); + enableTypeLowering_, fullName, recordName, ctx_->GetPfDecoder(), true, + passOptions_->EnableOptTrackField()); { if (enableTypeLowering_) { - BuildFrameStateChain(gate, builder); + BuildFrameStateChain(info, builder); } TimeScope timeScope("BytecodeToCircuit", methodName, method->GetMethodId().GetOffset(), log); builder.BytecodeToCircuit(); } + ReplaceInput(info, glue_, method); + PassData data(&builder, circuit_, ctx_, log, fullName, &methodInfo, hasTyps, recordName, - method, method->GetMethodId().GetOffset(), nativeAreaAllocator_); + method, method->GetMethodId().GetOffset(), nativeAreaAllocator_, ctx_->GetPfDecoder(), passOptions_); PassRunner pipeline(&data); + pipeline.RunPass(); if (builder.EnableLoopOptimization()) { pipeline.RunPass(); + pipeline.RunPass(); } pipeline.RunPass(); + pipeline.RunPass(); } -bool TSInlineLowering::CheckParameter(GateRef gate, bool isCallThis, MethodLiteral* method) +bool TSInlineLowering::CheckParameter(GateRef gate, CallGateInfo &info, MethodLiteral* method) { + if (info.IsCallAccessor()) { + return true; + } size_t numIns = acc_.GetNumValueIn(gate); - size_t fixedInputsNum = isCallThis ? 2 : 1; // 2: calltarget and this + size_t fixedInputsNum = info.IsCallThis() ? 2 : 1; // 2: calltarget and this uint32_t declaredNumArgs = method->GetNumArgsWithCallField(); return declaredNumArgs == (numIns - fixedInputsNum); } -void TSInlineLowering::ReplaceCallInput(GateRef gate, bool isCallThis, GateRef glue, MethodLiteral *method) +void TSInlineLowering::ReplaceCallInput(CallGateInfo &info, GateRef glue, MethodLiteral *method) { + GateRef gate = info.GetCallGate(); + bool isCallThis = info.IsCallThis(); std::vector vec; size_t numIns = acc_.GetNumValueIn(gate); // 1: last one elem is function @@ -225,7 +285,84 @@ void TSInlineLowering::ReplaceCallInput(GateRef gate, bool isCallThis, GateRef g for (size_t i = fixedInputsNum - 1; i < numIns - 1; i++) { vec.emplace_back(acc_.GetValueIn(gate, i)); } - LowerToInlineCall(gate, vec, method); + LowerToInlineCall(info, vec, method); +} + +void TSInlineLowering::ReplaceAccessorInput(CallGateInfo &info, GateRef glue, MethodLiteral *method) +{ + GateRef gate = info.GetCallGate(); + std::vector vec; + GateRef thisObj = GetAccessorReceiver(gate); + GateRef callTarget = Circuit::NullGate(); + // Fast accessor will not load getter or setter func + if (EnableFastAccessor()) { + callTarget = initCallTarget_; + } else { + callTarget = BuildAccessor(info); + } + size_t actualArgc = 0; + if (info.IsCallGetter()) { + actualArgc = NUM_MANDATORY_JSFUNC_ARGS; + } else if (info.IsCallSetter()) { + actualArgc = NUM_MANDATORY_JSFUNC_ARGS + 1; + } else { + UNREACHABLE(); + } + + vec.emplace_back(glue); // glue + if (!method->IsFastCall()) { + vec.emplace_back(builder_.Int64(actualArgc)); // argc + } + vec.emplace_back(callTarget); + if (!method->IsFastCall()) { + vec.emplace_back(builder_.Undefined()); // newTarget + } + vec.emplace_back(thisObj); + + if (info.IsCallSetter()) { + vec.emplace_back(GetCallSetterValue(gate)); + } + LowerToInlineCall(info, vec, method); +} + +GateRef TSInlineLowering::BuildAccessor(CallGateInfo &info) +{ + GateRef gate = info.GetCallGate(); + GateRef depend = acc_.GetDep(gate); + GateRef receiver = GetAccessorReceiver(gate); + GateRef accessor = Circuit::NullGate(); + uint32_t plrData = GetPlrData(receiver, acc_.GetValueIn(gate, 1)); + if (info.IsCallGetter()) { + accessor = circuit_->NewGate(circuit_->LoadGetter(), MachineType::I64, + {depend, receiver, builder_.Int32(plrData)}, GateType::AnyType()); + } else { + accessor = circuit_->NewGate(circuit_->LoadSetter(), MachineType::I64, + {depend, receiver, builder_.Int32(plrData)}, GateType::AnyType()); + } + acc_.ReplaceDependIn(gate, accessor); + return accessor; +} + +uint32_t TSInlineLowering::GetPlrData(GateRef receiver, GateRef constData) +{ + uint16_t propIndex = acc_.GetConstantValue(constData); + auto prop = tsManager_->GetStringFromConstantPool(propIndex); + GateType receiverType = acc_.GetGateType(receiver); + receiverType = tsManager_->TryNarrowUnionType(receiverType); + int hclassIndex = tsManager_->GetHClassIndexByInstanceGateType(receiverType); + JSHClass *hclass = JSHClass::Cast(tsManager_->GetValueFromCache(hclassIndex).GetTaggedObject()); + PropertyLookupResult plr = JSHClass::LookupPropertyInAotHClass(tsManager_->GetThread(), hclass, prop); + return plr.GetData(); +} + +void TSInlineLowering::ReplaceInput(CallGateInfo &info, GateRef glue, MethodLiteral *method) +{ + if (info.IsNormalCall()) { + ReplaceCallInput(info, glue, method); + } else { + ASSERT(info.IsCallAccessor()); + ReplaceAccessorInput(info, glue, method); + } } GateRef TSInlineLowering::MergeAllReturn(const std::vector &returnVector, GateRef &state, GateRef &depend) @@ -307,6 +444,7 @@ void TSInlineLowering::ReplaceReturnGate(GateRef callGate) } else { value = MergeAllReturn(returnVector, state, depend); } + SupplementType(callGate, value); ReplaceHirAndDeleteState(callGate, state, depend, value); } @@ -328,14 +466,18 @@ void TSInlineLowering::ReplaceHirAndDeleteState(GateRef gate, GateRef state, Gat acc_.DeleteGate(gate); } -void TSInlineLowering::LowerToInlineCall(GateRef callGate, const std::vector &args, MethodLiteral* method) +void TSInlineLowering::LowerToInlineCall(CallGateInfo &info, const std::vector &args, MethodLiteral* method) { + GateRef callGate = info.GetCallGate(); // replace in value/args ArgumentAccessor argAcc(circuit_); ASSERT(argAcc.ArgsCount() == args.size()); for (size_t i = 0; i < argAcc.ArgsCount(); i++) { GateRef arg = argAcc.ArgsAt(i); acc_.UpdateAllUses(arg, args.at(i)); + if (acc_.GetGateType(args.at(i)).IsAnyType()) { + acc_.SetGateType(args.at(i), acc_.GetGateType(arg)); + } acc_.DeleteGate(arg); } // replace in depend and state @@ -346,7 +488,8 @@ void TSInlineLowering::LowerToInlineCall(GateRef callGate, const std::vector(CommonArgIdx::FUNC)); } - GateRef callerFunc = argAcc.GetFrameArgsIn(callGate, FrameArgIdx::FUNC); + GateRef frameArgs = GetFrameArgs(info); + GateRef callerFunc = acc_.GetValueIn(frameArgs, 0); ReplaceEntryGate(callGate, callerFunc, inlineFunc, glue); // replace use gate ReplaceReturnGate(callGate); @@ -354,24 +497,52 @@ void TSInlineLowering::LowerToInlineCall(GateRef callGate, const std::vectorGetFuncMethodOffset(funcGt); - GateRef ret = circuit_->NewGate(circuit_->JSInlineTargetTypeCheck(static_cast(type.Value())), + GateRef ret = circuit_->NewGate(circuit_->JSInlineTargetTypeCheck(info.GetType()), MachineType::I1, {callState, callDepend, inlineFunc, builder_.IntPtr(methodOffset), frameState}, GateType::NJSValue()); acc_.ReplaceStateIn(gate, ret); acc_.ReplaceDependIn(gate, ret); } +void TSInlineLowering::InlineAccessorCheck(GateRef gate, GateRef receiver) +{ + GateRef callState = acc_.GetState(gate); + GateRef callDepend = acc_.GetDep(gate); + GateType receiverType = acc_.GetGateType(receiver); + receiverType = tsManager_->TryNarrowUnionType(receiverType); + int hclassIndex = tsManager_->GetHClassIndexByInstanceGateType(receiverType); + GateRef hclassIndexGate = builder_.IntPtr(hclassIndex); + auto frameState = acc_.FindNearestFrameState(callDepend); + GateRef ret = circuit_->NewGate(circuit_->InlineAccessorCheck(static_cast(receiverType.Value())), + MachineType::I1, {callState, callDepend, receiver, hclassIndexGate, frameState}, GateType::NJSValue()); + acc_.ReplaceStateIn(gate, ret); + acc_.ReplaceDependIn(gate, ret); +} + +void TSInlineLowering::InlineCheck(CallGateInfo &info) +{ + if (info.IsNormalCall()) { + InlineFuncCheck(info); + } else { + ASSERT(info.IsCallAccessor()); + GateRef gate = info.GetCallGate(); + GateRef receiver = GetAccessorReceiver(gate); + InlineAccessorCheck(gate, receiver); + } +} + void TSInlineLowering::RemoveRoot() { GateRef circuitRoot = acc_.GetCircuitRoot(); @@ -383,14 +554,13 @@ void TSInlineLowering::RemoveRoot() acc_.DeleteGate(circuitRoot); } -void TSInlineLowering::BuildFrameStateChain(GateRef gate, BytecodeCircuitBuilder &builder) +void TSInlineLowering::BuildFrameStateChain(CallGateInfo &info, BytecodeCircuitBuilder &builder) { - GateRef check = acc_.GetDep(gate); - GateRef stateSplit = acc_.GetDep(check); - ASSERT(acc_.GetOpCode(stateSplit) == OpCode::STATE_SPLIT); - GateRef preFrameState = acc_.GetFrameState(stateSplit); + GateRef preFrameState = GetFrameState(info); ASSERT(acc_.GetOpCode(preFrameState) == OpCode::FRAME_STATE); builder.SetPreFrameState(preFrameState); + GateRef frameArgs = acc_.GetFrameArgs(preFrameState); + builder.SetPreFrameArgs(frameArgs); } bool TSInlineLowering::FilterCallInTryCatch(GateRef gate) @@ -403,4 +573,213 @@ bool TSInlineLowering::FilterCallInTryCatch(GateRef gate) } return false; } + +void TSInlineLowering::SupplementType(GateRef callGate, GateRef targetGate) +{ + GateType callGateType = acc_.GetGateType(callGate); + GateType targetGateType = acc_.GetGateType(targetGate); + if (!callGateType.IsAnyType() && targetGateType.IsAnyType()) { + acc_.SetGateType(targetGate, callGateType); + } +} + +void TSInlineLowering::UpdateWorkList(ChunkQueue &workList) +{ + std::vector gateList; + circuit_->GetAllGates(gateList); + for (const auto &gate : gateList) { + if (acc_.GetId(gate) <= lastCallId_) { + continue; + } + auto op = acc_.GetOpCode(gate); + if (op == OpCode::JS_BYTECODE) { + CandidateInlineCall(gate, workList); + } + } +} + +size_t TSInlineLowering::GetOrInitialInlineCounts(GateRef frameArgs) +{ + auto it = inlinedCallMap_.find(frameArgs); + if (it == inlinedCallMap_.end()) { + inlinedCallMap_[frameArgs] = 0; + } + return inlinedCallMap_[frameArgs]; +} + + +bool TSInlineLowering::IsRecursiveFunc(CallGateInfo &info, size_t calleeMethodOffset) +{ + GateRef frameArgs = GetFrameArgs(info); + GateRef caller = acc_.GetValueIn(frameArgs); + auto funcType = acc_.GetGateType(caller); + GlobalTSTypeRef gt = funcType.GetGTRef(); + if (!tsManager_->IsFunctionTypeKind(gt)) { + return false; + } + auto callerMethodOffset = tsManager_->GetFuncMethodOffset(gt); + return callerMethodOffset == calleeMethodOffset; +} + +bool TSInlineLowering::IsAccessor(GateRef receiver, GateRef constData) +{ + uint16_t propIndex = acc_.GetConstantValue(constData); + auto prop = tsManager_->GetStringFromConstantPool(propIndex); + GateType receiverType = acc_.GetGateType(receiver); + receiverType = tsManager_->TryNarrowUnionType(receiverType); + if (tsManager_->IsClassInstanceTypeKind(receiverType)) { + int hclassIndex = tsManager_->GetHClassIndexByInstanceGateType(receiverType); + if (hclassIndex == -1) { + return false; + } + JSHClass *hclass = JSHClass::Cast(tsManager_->GetValueFromCache(hclassIndex).GetTaggedObject()); + if (!hclass->HasTSSubtyping()) { + return false; + } + PropertyLookupResult plr = JSHClass::LookupPropertyInAotHClass(tsManager_->GetThread(), hclass, prop); + if (!plr.IsFound()) { + return false; + } + + return plr.IsAccessor(); + } + return false; +} + +GlobalTSTypeRef TSInlineLowering::GetAccessorFuncGT(GateRef receiver, GateRef constData, bool isCallSetter) +{ + GateType receiverType = acc_.GetGateType(receiver); + receiverType = tsManager_->TryNarrowUnionType(receiverType); + GlobalTSTypeRef classInstanceGT = receiverType.GetGTRef(); + GlobalTSTypeRef classGT = tsManager_->GetClassType(classInstanceGT); + TSTypeAccessor tsTypeAcc(tsManager_, classGT); + uint16_t propIndex = acc_.GetConstantValue(constData); + auto prop = tsManager_->GetStringFromConstantPool(propIndex); + GlobalTSTypeRef funcGT = tsTypeAcc.GetAccessorGT(prop, isCallSetter); + return funcGT; +} + +void TSInlineLowering::CandidateAccessor(GateRef gate, ChunkQueue &workList, CallKind kind) +{ + GateRef receiver = GetAccessorReceiver(gate); + GateRef constData = acc_.GetValueIn(gate, 1); + if (IsAccessor(receiver, constData)) { + GlobalTSTypeRef gt = GetAccessorFuncGT(receiver, constData, IsCallSetter(kind)); + if (!gt.IsDefault()) { + workList.push(CallGateInfo(gate, kind, gt, 0)); + lastCallId_ = acc_.GetId(gate); + } + } +} + +void TSInlineLowering::CandidateNormalCall(GateRef gate, ChunkQueue &workList, CallKind kind) +{ + size_t funcIndex = acc_.GetNumValueIn(gate) - 1; + auto funcType = acc_.GetGateType(acc_.GetValueIn(gate, funcIndex)); + if (tsManager_->IsFunctionTypeKind(funcType)) { + GlobalTSTypeRef gt = funcType.GetGTRef(); + workList.push(CallGateInfo(gate, kind, gt, funcType.Value())); + lastCallId_ = acc_.GetId(gate); + } +} + +GateRef TSInlineLowering::GetAccessorReceiver(GateRef gate) +{ + EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate); + if (UNLIKELY(ecmaOpcode == EcmaOpcode::STTHISBYNAME_IMM8_ID16 || + ecmaOpcode == EcmaOpcode::STTHISBYNAME_IMM16_ID16)) { + return argAcc_.GetFrameArgsIn(gate, FrameArgIdx::THIS_OBJECT); + } + return acc_.GetValueIn(gate, 2); // 2: receiver +} + +GateRef TSInlineLowering::GetCallSetterValue(GateRef gate) +{ + EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate); + if (ecmaOpcode == EcmaOpcode::STTHISBYNAME_IMM8_ID16 || + ecmaOpcode == EcmaOpcode::STTHISBYNAME_IMM16_ID16) { + return acc_.GetValueIn(gate, 2); // 2: value + } + return acc_.GetValueIn(gate, 3); // 3: value +} + +GateRef TSInlineLowering::GetFrameState(CallGateInfo &info) +{ + GateRef gate = info.GetCallGate(); + if (info.IsNormalCall()) { + return acc_.GetFrameState(gate); + } + ASSERT(info.IsCallAccessor()); + GateRef frameState = acc_.FindNearestFrameState(gate); + return frameState; +} + +GateRef TSInlineLowering::GetFrameArgs(CallGateInfo &info) +{ + GateRef frameState = GetFrameState(info); + return acc_.GetValueIn(frameState); +} + +void TSInlineLowering::SetInitCallTargetAndConstPoolId(CallGateInfo &info) +{ + if (initCallTarget_ == Circuit::NullGate()) { + GateRef frameArgs = GetFrameArgs(info); + initCallTarget_ = acc_.GetValueIn(frameArgs, 0); + const JSPandaFile *pf = ctx_->GetJSPandaFile(); + initConstantPoolId_ = tsManager_->GetConstantPoolIDByMethodOffset(pf, initMethodOffset_); + } +} + +void TSInlineLowering::AnalyseFastAccessor(CallGateInfo &info, std::vector pcOffsets, + uint32_t inlineMethodOffset) +{ + isFastAccessor_ = false; + if (!info.IsCallAccessor()) { + return; + } + const JSPandaFile *pf = ctx_->GetJSPandaFile(); + int32_t constantpoolId = tsManager_->GetConstantPoolIDByMethodOffset(pf, inlineMethodOffset); + if (constantpoolId == initConstantPoolId_) { + for (size_t i = 0; i < pcOffsets.size(); i++) { + auto pc = pcOffsets[i]; + auto ecmaOpcode = ctx_->GetByteCodes()->GetOpcode(pc); + // These bytecodes require calltarget during the lowering process, so the acquisition of the accessor + // function cannot be omitted. + switch (ecmaOpcode) { + case EcmaOpcode::CREATEARRAYWITHBUFFER_IMM8_ID16: + case EcmaOpcode::CREATEARRAYWITHBUFFER_IMM16_ID16: + case EcmaOpcode::STMODULEVAR_IMM8: + case EcmaOpcode::WIDE_STMODULEVAR_PREF_IMM16: + case EcmaOpcode::DYNAMICIMPORT: + case EcmaOpcode::LDLOCALMODULEVAR_IMM8: + case EcmaOpcode::WIDE_LDLOCALMODULEVAR_PREF_IMM16: + case EcmaOpcode::LDEXTERNALMODULEVAR_IMM8: + case EcmaOpcode::WIDE_LDEXTERNALMODULEVAR_PREF_IMM16: + case EcmaOpcode::GETMODULENAMESPACE_IMM8: + case EcmaOpcode::WIDE_GETMODULENAMESPACE_PREF_IMM16: + case EcmaOpcode::SUPERCALLSPREAD_IMM8_V8: + case EcmaOpcode::NEWLEXENVWITHNAME_IMM8_ID16: + case EcmaOpcode::WIDE_NEWLEXENVWITHNAME_PREF_IMM16_ID16: + case EcmaOpcode::LDSUPERBYVALUE_IMM8_V8: + case EcmaOpcode::LDSUPERBYVALUE_IMM16_V8: + case EcmaOpcode::STSUPERBYVALUE_IMM16_V8_V8: + case EcmaOpcode::STSUPERBYVALUE_IMM8_V8_V8: + case EcmaOpcode::LDSUPERBYNAME_IMM8_ID16: + case EcmaOpcode::LDSUPERBYNAME_IMM16_ID16: + case EcmaOpcode::STSUPERBYNAME_IMM8_ID16_V8: + case EcmaOpcode::STSUPERBYNAME_IMM16_ID16_V8: + case EcmaOpcode::DEFINECLASSWITHBUFFER_IMM8_ID16_ID16_IMM16_V8: + case EcmaOpcode::DEFINECLASSWITHBUFFER_IMM16_ID16_ID16_IMM16_V8: + case EcmaOpcode::DEFINEFUNC_IMM8_ID16_IMM8: + case EcmaOpcode::DEFINEFUNC_IMM16_ID16_IMM8: + case EcmaOpcode::DEFINEMETHOD_IMM8_ID16_IMM8: + case EcmaOpcode::DEFINEMETHOD_IMM16_ID16_IMM8: + return; + default: + break; + } + } + isFastAccessor_ = true; + } +} } // namespace panda::ecmascript diff --git a/ecmascript/compiler/ts_inline_lowering.h b/ecmascript/compiler/ts_inline_lowering.h index 0d7e12d2b0e9ee8fefc106c6ca6554112fa8270e..f3a7e6c9dbe9e31234d2090dde1aa9a0e6d80687 100644 --- a/ecmascript/compiler/ts_inline_lowering.h +++ b/ecmascript/compiler/ts_inline_lowering.h @@ -25,6 +25,13 @@ #include "ecmascript/jspandafile/js_pandafile.h" namespace panda::ecmascript::kungfu { +enum CallKind : uint8_t { + CALL, + CALL_THIS, + CALL_SETTER, + CALL_GETTER, + INVALID +}; class CircuitRootScope { public: explicit CircuitRootScope(Circuit *circuit) @@ -42,22 +49,85 @@ private: GateRef root_ { 0 }; }; +class CallGateInfo { +public: + explicit CallGateInfo(GateRef call, CallKind kind, GlobalTSTypeRef gt, uint32_t type) + : call_(call), kind_(kind), gt_(gt), type_(type) + { + } + + ~CallGateInfo() = default; + + GateRef GetCallGate() const + { + return call_; + } + + bool IsCallThis() const + { + return kind_ == CallKind::CALL_THIS; + } + + bool IsNormalCall() const + { + return kind_ == CallKind::CALL || kind_ == CallKind::CALL_THIS; + } + + bool IsCallAccessor() const + { + return kind_ == CallKind::CALL_SETTER || kind_ == CallKind::CALL_GETTER; + } + + bool IsCallGetter() const + { + return kind_ == CallKind::CALL_GETTER; + } + + bool IsCallSetter() const + { + return kind_ == CallKind::CALL_SETTER; + } + + GlobalTSTypeRef GetFuncGT() const + { + return gt_; + } + + uint32_t GetType() const + { + return type_; + } + +private: + GateRef call_ {Circuit::NullGate()}; + CallKind kind_ {CallKind::INVALID}; + GlobalTSTypeRef gt_; + uint32_t type_; +}; + class TSInlineLowering { public: - static constexpr size_t MAX_INLINE_CALL_ALLOWED = 5; + static constexpr size_t MAX_INLINE_CALL_ALLOWED = 6; TSInlineLowering(Circuit *circuit, PassContext *ctx, bool enableLog, const std::string& name, - NativeAreaAllocator* nativeAreaAllocator) + NativeAreaAllocator* nativeAreaAllocator, PassOptions *options, uint32_t methodOffset) : circuit_(circuit), acc_(circuit), + glue_(acc_.GetGlueFromArgList()), builder_(circuit, ctx->GetCompilerConfig()), tsManager_(ctx->GetTSManager()), ctx_(ctx), + passOptions_(options), enableLog_(enableLog), methodName_(name), enableTypeLowering_(ctx->GetEcmaVM()->GetJSOptions().IsEnableTypeLowering()), traceInline_(ctx->GetEcmaVM()->GetJSOptions().GetTraceInline()), maxInlineBytecodesCount_(ctx->GetEcmaVM()->GetJSOptions().GetMaxInlineBytecodes()), - nativeAreaAllocator_(nativeAreaAllocator) {} + nativeAreaAllocator_(nativeAreaAllocator), + noCheck_(ctx->GetEcmaVM()->GetJSOptions().IsCompilerNoCheck()), + chunk_(circuit->chunk()), + inlinedCallMap_(circuit->chunk()), + argAcc_(circuit), + initMethodOffset_(methodOffset) {} ~TSInlineLowering() = default; @@ -74,40 +144,94 @@ private: return methodName_; } - void TryInline(GateRef gate); - void TryInline(GateRef gate, bool isCallThis); + bool IsSmallMethod(size_t bcSize) const + { + return bcSize <= maxInlineBytecodesCount_; + } + + bool IsInlineCountsOverflow(size_t inlineCount) const + { + return inlineCount >= MAX_INLINE_CALL_ALLOWED; + } + + void UpdateInlineCounts(GateRef frameArgs, size_t inlineCallCounts) + { + inlinedCallMap_[frameArgs] = ++inlineCallCounts; + } + + bool EnableFastAccessor() const + { + return isFastAccessor_ && !traceInline_; + } + + bool IsCallSetter(CallKind kind) const + { + return kind == CallKind::CALL_SETTER; + } + + void CollectInlineInfo(); + void GetInlinedMethodId(GateRef gate); + void CandidateInlineCall(GateRef gate, ChunkQueue &workList); + void TryInline(CallGateInfo &info, ChunkQueue &workList); bool FilterInlinedMethod(MethodLiteral* method, std::vector pcOffsets); bool FilterCallInTryCatch(GateRef gate); - void InlineCall(MethodInfo &methodInfo, MethodPcInfo &methodPCInfo, MethodLiteral* method, GateRef gate); - void ReplaceCallInput(GateRef gate, bool isCallThis, GateRef glue, MethodLiteral *method); - + void InlineCall(MethodInfo &methodInfo, MethodPcInfo &methodPCInfo, MethodLiteral* method, CallGateInfo &info); + void ReplaceCallInput(CallGateInfo &info, GateRef glue, MethodLiteral *method); void ReplaceEntryGate(GateRef callGate, GateRef callerFunc, GateRef inlineFunc, GateRef glue); void ReplaceReturnGate(GateRef callGate); - void ReplaceHirAndDeleteState(GateRef gate, GateRef state, GateRef depend, GateRef value); - GateRef MergeAllReturn(const std::vector &returnVector, GateRef &state, GateRef &depend); - bool CheckParameter(GateRef gate, bool isCallThis, MethodLiteral* method); - - void LowerToInlineCall(GateRef gate, const std::vector &args, MethodLiteral* method); + bool CheckParameter(GateRef gate, CallGateInfo &info, MethodLiteral* method); + void LowerToInlineCall(CallGateInfo &info, const std::vector &args, MethodLiteral* method); void RemoveRoot(); - void BuildFrameStateChain(GateRef gate, BytecodeCircuitBuilder &builder); + void BuildFrameStateChain(CallGateInfo &info, BytecodeCircuitBuilder &builder); GateRef TraceInlineFunction(GateRef glue, GateRef depend, std::vector &args, GateRef callGate); - void InlineFuncCheck(GateRef gate); + void InlineFuncCheck(const CallGateInfo &info); + void SupplementType(GateRef callGate, GateRef targetGate); + void UpdateWorkList(ChunkQueue &workList); + size_t GetOrInitialInlineCounts(GateRef frameArgs); + bool IsRecursiveFunc(CallGateInfo &info, size_t calleeMethodOffset); + bool IsAccessor(GateRef receiver, GateRef constData); + GlobalTSTypeRef GetAccessorFuncType(GateRef receiver, GateRef constData); + void CandidateAccessor(GateRef gate, ChunkQueue &workList, CallKind kind); + void CandidateNormalCall(GateRef gate, ChunkQueue &workList, CallKind kind); + void InlineAccessorCheck(GateRef gate, GateRef receiver); + void InlineCheck(CallGateInfo &info); + GateRef GetAccessorReceiver(GateRef gate); + GateRef GetFrameArgs(CallGateInfo &info); + void ReplaceAccessorInput(CallGateInfo &info, GateRef glue, MethodLiteral *method); + void ReplaceInput(CallGateInfo &info, GateRef glue, MethodLiteral *method); + GateRef BuildAccessor(CallGateInfo &info); + uint32_t GetPlrData(GateRef receiver, GateRef constData); + GateRef GetCallSetterValue(GateRef gate); + GlobalTSTypeRef GetAccessorFuncGT(GateRef receiver, GateRef constData, bool isCallSetter); + GateRef GetFrameState(CallGateInfo &info); + void SetInitCallTargetAndConstPoolId(CallGateInfo &info); + void AnalyseFastAccessor(CallGateInfo &info, std::vector pcOffsets, uint32_t inlineMethodOffset); Circuit *circuit_ {nullptr}; GateAccessor acc_; + GateRef glue_; CircuitBuilder builder_; TSManager *tsManager_ {nullptr}; PassContext *ctx_ {nullptr}; + PassOptions *passOptions_ {nullptr}; bool enableLog_ {false}; std::string methodName_; - size_t inlinedCall_ { 0 }; bool enableTypeLowering_ {false}; bool inlineSuccess_ {false}; bool traceInline_ {false}; size_t maxInlineBytecodesCount_ {0}; NativeAreaAllocator *nativeAreaAllocator_ {nullptr}; + bool noCheck_ {false}; + Chunk* chunk_ {nullptr}; + ChunkMap inlinedCallMap_; + size_t lastCallId_ {0}; + ArgumentAccessor argAcc_; + uint32_t initMethodOffset_ {0}; + int32_t initConstantPoolId_ {0}; + GateRef initCallTarget_ {Circuit::NullGate()}; + bool isFastAccessor_ {false}; }; } // panda::ecmascript::kungfu -#endif // ECMASCRIPT_COMPILER_TS_INLINE_LOWERING_H \ No newline at end of file +#endif // ECMASCRIPT_COMPILER_TS_INLINE_LOWERING_H diff --git a/ecmascript/compiler/type.h b/ecmascript/compiler/type.h index 82d85c0346f3602aecacf489dd26205b6ab5f2cd..93a5b70fd156012a096dbbb3caf0f5f8d7ccb0e3 100644 --- a/ecmascript/compiler/type.h +++ b/ecmascript/compiler/type.h @@ -18,6 +18,26 @@ #include "ecmascript/ts_types/global_ts_type_ref.h" +#define VALUE_TYPE_LIST(V) \ + V(BOOL) \ + V(INT32) \ + V(UINT32) \ + V(FLOAT64) \ + V(TAGGED_BOOLEAN) \ + V(TAGGED_INT) \ + V(TAGGED_DOUBLE) \ + V(TAGGED_NUMBER) \ + V(CHAR) \ + V(ECMA_STRING) \ + V(UNDEFINED) \ + V(TAGGED_NULL) + +enum class ValueType : uint8_t { +#define DECLARE_VALUE_TYPE(TYPE) TYPE, + VALUE_TYPE_LIST(DECLARE_VALUE_TYPE) +#undef DECLARE_VALUE_TYPE +}; + namespace panda::ecmascript::kungfu { class GateType { public: @@ -212,6 +232,26 @@ public: return IsNumberType() || IsNullType() || IsUndefinedType() || IsBooleanType() || IsBigIntType(); } + bool IsSymbolType() const + { + GlobalTSTypeRef r = GetGTRef(); + uint32_t m = r.GetModuleId(); + uint32_t l = r.GetLocalId(); + return (m == 0) && (l == static_cast(TSPrimitiveType::SYMBOL)); + } + + // In addition to normal number types, null, undefined, boolean types will be converted to numeric types when doing + // some numerical computation. + bool IsPrimitiveNumberType() const + { + return IsNumberType() || IsBooleanType(); + } + + bool IsPrimitiveIntType() const + { + return IsIntType() || IsBooleanType(); + } + bool IsGCRelated() const { return (type_ & (~GateType::GC_MASK)) == 0; @@ -274,14 +314,10 @@ private: uint32_t type_ {0}; }; -enum class ValueType : uint8_t { - BOOL, - INT32, - FLOAT64, - TAGGED_BOOLEAN, - TAGGED_INT, - TAGGED_DOUBLE, - TAGGED_NUMBER, +enum class ConvertSupport : uint8_t { + ENABLE, + // Not support conversion from srcType to dstType. It is necessary to use 'deopt' to ensure semantic correctness. + DISABLE }; class Type { diff --git a/ecmascript/compiler/type_inference/global_type_infer.cpp b/ecmascript/compiler/type_inference/global_type_infer.cpp index 9be104e15ee50a6ec32074ff174b6432dce02248..f5cf879f819898a72644a6b11120e6a839963613 100644 --- a/ecmascript/compiler/type_inference/global_type_infer.cpp +++ b/ecmascript/compiler/type_inference/global_type_infer.cpp @@ -17,9 +17,9 @@ namespace panda::ecmascript::kungfu { GlobalTypeInfer::GlobalTypeInfer(PassContext *ctx, const uint32_t methodOffset, const CString &recordName, - PGOProfilerDecoder *decoder, bool enableLog) + PGOProfilerDecoder *decoder, bool enableOptTrackField, bool enableLog) : ctx_(ctx), jsPandaFile_(ctx_->GetJSPandaFile()), bcInfo_(ctx->GetBytecodeInfo()), methodOffset_(methodOffset), - recordName_(recordName), decoder_(decoder), enableLog_(enableLog), + recordName_(recordName), decoder_(decoder), enableOptTrackField_(enableOptTrackField), enableLog_(enableLog), enableGlobalTypeInfer_(ctx_->GetTSManager()->GetEcmaVM()->GetJSOptions().IsEnableGlobalTypeInfer()) { CollectNamespaceMethods(methodOffset); @@ -62,16 +62,15 @@ void GlobalTypeInfer::NewTypeInfer(const uint32_t methodOffset) auto methodLiteral = jsPandaFile_->FindMethodLiteral(methodOffset); std::string fullName = module->GetFuncName(methodLiteral, jsPandaFile_); - Circuit *circuit = - new Circuit(ctx_->GetNativeAreaAllocator(), module->GetDebugInfo(), - fullName.c_str(), ctx_->GetCompilerConfig()->Is64Bit()); - circuit->SetFrameType(FrameType::OPTIMIZED_JS_FUNCTION_FRAME); + Circuit *circuit = new Circuit(ctx_->GetNativeAreaAllocator(), module->GetDebugInfo(), + fullName.c_str(), ctx_->GetCompilerConfig()->Is64Bit(), + FrameType::OPTIMIZED_JS_FUNCTION_FRAME); circuits_.emplace_back(circuit); BytecodeCircuitBuilder *builder = new BytecodeCircuitBuilder(jsPandaFile_, methodLiteral, methodPcInfo, ctx_->GetTSManager(), circuit, ctx_->GetByteCodes(), jsPandaFile_->HasTSTypes(recordName_), enableLog_, true, - fullName, recordName_, decoder_); + fullName, recordName_, decoder_, false, enableOptTrackField_); builder->BytecodeToCircuit(); builders_.emplace_back(builder); diff --git a/ecmascript/compiler/type_inference/global_type_infer.h b/ecmascript/compiler/type_inference/global_type_infer.h index 190cc0f63c03692f7f87ce86078afe37727ab0cf..62fd390602e9394408679cb59b453270a645dcca 100644 --- a/ecmascript/compiler/type_inference/global_type_infer.h +++ b/ecmascript/compiler/type_inference/global_type_infer.h @@ -22,7 +22,7 @@ namespace panda::ecmascript::kungfu { class GlobalTypeInfer { public: GlobalTypeInfer(PassContext *ctx, const uint32_t methodOffset, const CString &recordName, - PGOProfilerDecoder *decoder, bool enableLog); + PGOProfilerDecoder *decoder, bool enableOptTrackField, bool enableLog); ~GlobalTypeInfer(); void ProcessTypeInference(BytecodeCircuitBuilder *builder, Circuit *circuit); @@ -55,6 +55,7 @@ private: uint32_t methodOffset_ {0}; const CString &recordName_; PGOProfilerDecoder *decoder_ {nullptr}; + bool enableOptTrackField_ {false}; bool enableLog_ {false}; bool enableGlobalTypeInfer_ {false}; std::set namespaceTypes_ {}; diff --git a/ecmascript/compiler/type_inference/initialization_analysis.cpp b/ecmascript/compiler/type_inference/initialization_analysis.cpp index aadf46717a4b9297f1fbe45d99127f96572093c9..6721f80c1017c7a58f9702b619b366c21d43fc7b 100644 --- a/ecmascript/compiler/type_inference/initialization_analysis.cpp +++ b/ecmascript/compiler/type_inference/initialization_analysis.cpp @@ -46,12 +46,14 @@ void InitializationAnalysis::Analyse(GateRef gate) switch (ecmaOpcode) { case EcmaOpcode::STOBJBYNAME_IMM8_ID16_V8: case EcmaOpcode::STOBJBYNAME_IMM16_ID16_V8: { - CollectInitializationInfo(gate, false); + CollectInitializationType(gate, ThisUsage::INDEFINITE_THIS); + CollectInitializationInfo(gate, ThisUsage::INDEFINITE_THIS); break; } case EcmaOpcode::STTHISBYNAME_IMM8_ID16: case EcmaOpcode::STTHISBYNAME_IMM16_ID16: { - CollectInitializationInfo(gate, true); + CollectInitializationType(gate, ThisUsage::DEFINITE_THIS); + CollectInitializationInfo(gate, ThisUsage::DEFINITE_THIS); break; } case EcmaOpcode::SUPERCALLTHISRANGE_IMM8_IMM8_V8: @@ -69,9 +71,39 @@ void InitializationAnalysis::Analyse(GateRef gate) } } -void InitializationAnalysis::CollectInitializationInfo(GateRef gate, bool isThis) +void InitializationAnalysis::CollectInitializationType(GateRef gate, ThisUsage thisUsage) { - if (!isThis) { + GateRef value = acc_.GetValueIn(gate, 3); // 3: index of value + GateType valueType = acc_.GetGateType(value); + if (valueType.IsAnyType()) { + return; + } + if (thisUsage == ThisUsage::INDEFINITE_THIS) { + GateRef receiver = acc_.GetValueIn(gate, 2); // 2: index of receiver + GateType receiverType = acc_.GetGateType(receiver); + auto receiverGT = receiverType.GetGTRef(); + if (tsManager_->IsClassInstanceTypeKind(receiverType)) { + receiverGT = tsManager_->GetClassType(receiverType); + } + if (!CheckIsThisObject(receiver) && receiverGT != classType_.GetGTRef()) { + return; + } + } + + uint16_t index = acc_.GetConstantValue(acc_.GetValueIn(gate, 1)); // 1: stringId + JSTaggedValue propKey = tsManager_->GetStringFromConstantPool(index); + + if (valueType.IsNumberType()) { + valueType = GateType::NumberType(); + } + + TSTypeAccessor typeAccessor(tsManager_, classType_); + typeAccessor.UpdateNonStaticProp(propKey, valueType.GetGTRef()); +} + +void InitializationAnalysis::CollectInitializationInfo(GateRef gate, ThisUsage thisUsage) +{ + if (thisUsage == ThisUsage::INDEFINITE_THIS) { GateRef receiver = acc_.GetValueIn(gate, 2); // 2: index of receiver if (!CheckIsThisObject(receiver)) { return; @@ -150,11 +182,11 @@ bool InitializationAnalysis::CheckSimpleJSGate(GateRef gate, const uint16_t inde switch (ecmaOpcode) { case EcmaOpcode::LDOBJBYNAME_IMM8_ID16: case EcmaOpcode::LDOBJBYNAME_IMM16_ID16: { - return CheckLdObjByName(gate, index, false); + return CheckLdObjByName(gate, index, ThisUsage::INDEFINITE_THIS); } case EcmaOpcode::LDTHISBYNAME_IMM8_ID16: case EcmaOpcode::LDTHISBYNAME_IMM16_ID16: { - return CheckLdObjByName(gate, index, true); + return CheckLdObjByName(gate, index, ThisUsage::DEFINITE_THIS); } case EcmaOpcode::LDTHISBYVALUE_IMM8: case EcmaOpcode::LDTHISBYVALUE_IMM16: { @@ -182,9 +214,9 @@ bool InitializationAnalysis::CheckSimpleJSGate(GateRef gate, const uint16_t inde return false; } -bool InitializationAnalysis::CheckLdObjByName(GateRef gate, const uint16_t index, bool isThis) const +bool InitializationAnalysis::CheckLdObjByName(GateRef gate, const uint16_t index, ThisUsage thisUsage) const { - if (!isThis) { + if (thisUsage == ThisUsage::INDEFINITE_THIS) { GateRef receiver = acc_.GetValueIn(gate, 2); // 2: index of receiver if (!CheckIsThisObject(receiver)) { return false; diff --git a/ecmascript/compiler/type_inference/initialization_analysis.h b/ecmascript/compiler/type_inference/initialization_analysis.h index fc5113bb0c02c33e3c05dad7ecfc974200c00498..408c7dd7f30521da615438fecc48319e111f3737 100644 --- a/ecmascript/compiler/type_inference/initialization_analysis.h +++ b/ecmascript/compiler/type_inference/initialization_analysis.h @@ -22,6 +22,11 @@ #include "ecmascript/ts_types/ts_manager.h" namespace panda::ecmascript::kungfu { +enum class ThisUsage : uint8_t { + INDEFINITE_THIS = 0, + DEFINITE_THIS, +}; + class InitializationAnalysis { public: InitializationAnalysis(Circuit *circuit, TSManager *tsManager, const CString &recordName, @@ -52,13 +57,14 @@ private: } void Analyse(GateRef gate); - void CollectInitializationInfo(GateRef gate, bool isThis); + void CollectInitializationInfo(GateRef gate, ThisUsage thisUsage); + void CollectInitializationType(GateRef gate, ThisUsage thisUsage); bool CheckIsThisObject(GateRef receiver) const; void MarkSuperClass(); bool CheckSimpleCFG(GateRef gate, const uint16_t index) const; bool CheckSimpleGate(GateRef gate, const uint16_t index) const; bool CheckSimpleJSGate(GateRef gate, const uint16_t index) const; - bool CheckLdObjByName(GateRef gate, const uint16_t index, bool isThis) const; + bool CheckLdObjByName(GateRef gate, const uint16_t index, ThisUsage thisUsage) const; bool CheckLdObjByIndexOrValue(GateRef gate) const; bool CheckCall() const; void StoreThisObject(); diff --git a/ecmascript/compiler/type_inference/method_type_infer.cpp b/ecmascript/compiler/type_inference/method_type_infer.cpp index c64eb8d87ce10bdd1db4f6aed3d4e8be8d94cf35..9f968e1da453dc529257fda23c10324c91bad8fd 100644 --- a/ecmascript/compiler/type_inference/method_type_infer.cpp +++ b/ecmascript/compiler/type_inference/method_type_infer.cpp @@ -14,8 +14,8 @@ */ #include "ecmascript/compiler/type_inference/method_type_infer.h" - #include "ecmascript/jspandafile/js_pandafile_manager.h" +#include "ecmascript/ts_types/ts_type_accessor.h" #include "ecmascript/ts_types/ts_type_parser.h" namespace panda::ecmascript::kungfu { @@ -130,7 +130,7 @@ bool MethodTypeInfer::UpdateType(GateRef gate, const GateType type, bool savePre } if (type != preType) { - gateAccessor_.SetGateType(gate, type); + gateAccessor_.SetGateType(gate, HandleTypeCompatibility(preType, type)); return true; } return false; @@ -142,6 +142,14 @@ bool MethodTypeInfer::UpdateType(GateRef gate, const GlobalTSTypeRef &typeRef, b return UpdateType(gate, type, savePreType); } +GateType MethodTypeInfer::HandleTypeCompatibility(const GateType preType, const GateType type) const +{ + if (tsManager_->IsArrayTypeKind(preType) && tsManager_->IsBuiltinInstanceType(BuiltinTypeId::ARRAY, type)) { + return preType; + } + return type; +} + bool MethodTypeInfer::IsNewLexEnv(EcmaOpcode opcode) const { switch (opcode) { @@ -197,12 +205,7 @@ bool MethodTypeInfer::Infer(GateRef gate) switch (bytecodeInfo.GetOpcode()) { case EcmaOpcode::LDNAN: case EcmaOpcode::LDINFINITY: - case EcmaOpcode::MOD2_IMM8_V8: - case EcmaOpcode::AND2_IMM8_V8: - case EcmaOpcode::OR2_IMM8_V8: - case EcmaOpcode::XOR2_IMM8_V8: case EcmaOpcode::TONUMBER_IMM8: - case EcmaOpcode::TONUMERIC_IMM8: case EcmaOpcode::NEG_IMM8: case EcmaOpcode::EXP_IMM8_V8: case EcmaOpcode::STARRAYSPREAD_V8_V8: @@ -211,6 +214,9 @@ bool MethodTypeInfer::Infer(GateRef gate) case EcmaOpcode::ASHR2_IMM8_V8: case EcmaOpcode::SHR2_IMM8_V8: case EcmaOpcode::NOT_IMM8: + case EcmaOpcode::AND2_IMM8_V8: + case EcmaOpcode::OR2_IMM8_V8: + case EcmaOpcode::XOR2_IMM8_V8: return SetIntType(gate); case EcmaOpcode::LDBIGINT_ID16: return SetBigIntType(gate); @@ -253,6 +259,8 @@ bool MethodTypeInfer::Infer(GateRef gate) return InferSub2(gate); case EcmaOpcode::MUL2_IMM8_V8: return InferMul2(gate); + case EcmaOpcode::MOD2_IMM8_V8: + return InferMod2(gate); case EcmaOpcode::DIV2_IMM8_V8: return InferDiv2(gate); case EcmaOpcode::INC_IMM8: @@ -280,20 +288,23 @@ bool MethodTypeInfer::Infer(GateRef gate) return InferLdObjByName(gate); case EcmaOpcode::LDA_STR_ID16: return InferLdStr(gate); + case EcmaOpcode::TONUMERIC_IMM8: + return InferToNumberic(gate); case EcmaOpcode::CALLARG0_IMM8: case EcmaOpcode::CALLARG1_IMM8_V8: case EcmaOpcode::CALLARGS2_IMM8_V8_V8: case EcmaOpcode::CALLARGS3_IMM8_V8_V8_V8: case EcmaOpcode::CALLRANGE_IMM8_IMM8_V8: case EcmaOpcode::WIDE_CALLRANGE_PREF_IMM16_V8: + case EcmaOpcode::APPLY_IMM8_V8_V8: + return InferCallFunction(gate); case EcmaOpcode::CALLTHIS0_IMM8_V8: case EcmaOpcode::CALLTHIS1_IMM8_V8_V8: case EcmaOpcode::CALLTHIS2_IMM8_V8_V8_V8: case EcmaOpcode::CALLTHIS3_IMM8_V8_V8_V8_V8: case EcmaOpcode::CALLTHISRANGE_IMM8_IMM8_V8: case EcmaOpcode::WIDE_CALLTHISRANGE_PREF_IMM16_V8: - case EcmaOpcode::APPLY_IMM8_V8_V8: - return InferCallFunction(gate); + return InferCallMethod(gate); case EcmaOpcode::LDOBJBYVALUE_IMM8_V8: case EcmaOpcode::LDOBJBYVALUE_IMM16_V8: return InferLdObjByValue(gate); @@ -542,6 +553,31 @@ bool MethodTypeInfer::InferMul2(GateRef gate) return UpdateType(gate, GateType::NumberType()); } +/* + * Type Infer rule(satisfy commutative law): + * number % number = number + * int % number = number + * double % number = double + * int % int = int + * int % double = double + * double % double = double + */ +bool MethodTypeInfer::InferMod2(GateRef gate) +{ + // 2: number of value inputs + ASSERT(gateAccessor_.GetNumValueIn(gate) == 2); + auto firInType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, 0)); + auto secInType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, 1)); + if ((firInType.IsNumberType() && secInType.IsDoubleType()) || + (firInType.IsDoubleType() && secInType.IsNumberType())) { + return UpdateType(gate, GateType::DoubleType()); + } + if ((firInType.IsIntType() && secInType.IsIntType())) { + return UpdateType(gate, GateType::IntType()); + } + return UpdateType(gate, GateType::NumberType()); +} + /* * Type Infer rule(satisfy commutative law): * in type lowering, both elements will be changed to float64 firstly. @@ -589,6 +625,16 @@ bool MethodTypeInfer::InferIncDec(GateRef gate) return UpdateType(gate, GateType::NumberType()); } +bool MethodTypeInfer::InferToNumberic(GateRef gate) +{ + GateRef src = gateAccessor_.GetValueIn(gate, 0); + GateType srcType = gateAccessor_.GetGateType(src); + if (srcType.IsNumberType()) { + return UpdateType(gate, srcType); + } + return UpdateType(gate, GateType::NumberType()); +} + bool MethodTypeInfer::InferLdObjByIndex(GateRef gate) { // 2: number of value inputs @@ -599,6 +645,14 @@ bool MethodTypeInfer::InferLdObjByIndex(GateRef gate) auto type = tsManager_->GetArrayParameterTypeGT(inValueType); return UpdateType(gate, type); } + + if (tsManager_->IsIntTypedArrayType(inValueType)) { + return UpdateType(gate, GateType::IntType()); + } + + if (tsManager_->IsDoubleTypedArrayType(inValueType)) { + return UpdateType(gate, GateType::DoubleType()); + } if (tsManager_->IsTypedArrayType(inValueType)) { return UpdateType(gate, GateType::NumberType()); @@ -704,14 +758,24 @@ bool MethodTypeInfer::InferStObjByName(GateRef gate) GateRef receiver = gateAccessor_.GetValueIn(gate, 2); // 2: index of receiver GateType receiverType = gateAccessor_.GetGateType(receiver); - if (!tsManager_->IsNamespaceTypeKind(receiverType)) { - return false; - } uint16_t index = gateAccessor_.GetConstantValue(gateAccessor_.GetValueIn(gate, 1)); // 1: index of key JSTaggedValue propKey = tsManager_->GetStringFromConstantPool(index); - tsManager_->AddNamespacePropType(receiverType, propKey, valueType); - return true; + if (tsManager_->IsNamespaceTypeKind(receiverType)) { + tsManager_->AddNamespacePropType(receiverType, propKey, valueType); + return true; + } + + if (valueType.IsNumberType()) { + valueType = GateType::NumberType(); + } + + if (tsManager_->IsClassTypeKind(receiverType)) { + TSTypeAccessor typeAccessor(tsManager_, receiverType); + typeAccessor.UpdateStaticProp(propKey, valueType.GetGTRef()); + return true; + } + return false; } bool MethodTypeInfer::InferNewObject(GateRef gate) @@ -737,7 +801,7 @@ bool MethodTypeInfer::InferLdStr(GateRef gate) bool MethodTypeInfer::GetObjPropWithName(GateRef gate, GateType objType, uint64_t index) { JSTaggedValue name = tsManager_->GetStringFromConstantPool(index); - if (tsManager_->IsBuiltinArrayType(objType) || tsManager_->IsTypedArrayType(objType)) { + if (tsManager_->IsBuiltinInstanceType(BuiltinTypeId::ARRAY, objType) || tsManager_->IsTypedArrayType(objType)) { auto thread = tsManager_->GetThread(); JSTaggedValue lengthKey = thread->GlobalConstants()->GetLengthString(); if (JSTaggedValue::SameValue(name, lengthKey)) { @@ -758,22 +822,51 @@ bool MethodTypeInfer::GetObjPropWithName(GateRef gate, GateType objType, uint64_ return UpdateType(gate, type); } -bool MethodTypeInfer::InferCallFunction(GateRef gate) +bool MethodTypeInfer::InferCallMethod(GateRef gate) { // 1: last one elem is function size_t funcIndex = gateAccessor_.GetNumValueIn(gate) - 1; auto funcType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, funcIndex)); if (tsManager_->IsFunctionTypeKind(funcType)) { - auto returnType = tsManager_->GetFuncReturnValueTypeGT(funcType); - return UpdateType(gate, returnType); - } + // forEach CallBack + TSTypeAccessor typeAccessor(tsManager_, funcType); + if (typeAccessor.GetFunctionName() == "forEach") { + auto funcGate = gateAccessor_.GetValueIn(gate, funcIndex); + auto &bytecodeInfo = GetByteCodeInfo(funcGate); + if (bytecodeInfo.GetOpcode() == EcmaOpcode::LDOBJBYNAME_IMM8_ID16 || + bytecodeInfo.GetOpcode() == EcmaOpcode::LDOBJBYNAME_IMM16_ID16) { + // 2: the third parameter is receiver + auto objType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(funcGate, 2)); + // get callBack function type + auto callBackType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, 1)); + TSTypeAccessor newTypeAccessor(tsManager_, callBackType); + newTypeAccessor.UpdateForEachCBPara(objType); + } + } - if (tsManager_->IsIteratorInstanceTypeKind(funcType)) { + // normal Call + auto returnType = tsManager_->GetFuncReturnValueTypeGT(funcType); + GateRef thisObj = gateAccessor_.GetValueIn(gate, 0); // 0: index of thisObject + auto thisObjType = gateAccessor_.GetGateType(thisObj); + return UpdateType(gate, HandleTypeCompatibility(thisObjType, GateType(returnType))); + } else if (tsManager_->IsIteratorInstanceTypeKind(funcType)) { GlobalTSTypeRef elementGT = tsManager_->GetIteratorInstanceElementGt(funcType); GlobalTSTypeRef iteratorResultInstanceType = tsManager_->GetOrCreateTSIteratorInstanceType( TSRuntimeType::ITERATOR_RESULT, elementGT); return UpdateType(gate, iteratorResultInstanceType); } + return UpdateType(gate, GateType::AnyType()); +} + +bool MethodTypeInfer::InferCallFunction(GateRef gate) +{ + // 1: last one elem is function + size_t funcIndex = gateAccessor_.GetNumValueIn(gate) - 1; + auto funcType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, funcIndex)); + if (tsManager_->IsFunctionTypeKind(funcType)) { + auto returnType = tsManager_->GetFuncReturnValueTypeGT(funcType); + return UpdateType(gate, returnType); + } /* According to the ECMAScript specification, user-defined classes can only be instantiated by constructing (with * new keyword). However, a few builtin types can be called like a function. Upon the results of calling and * constructing, there are 4 categories of builtin types: @@ -798,8 +891,11 @@ bool MethodTypeInfer::InferCallFunction(GateRef gate) * See the list of builtin types' constructors at: * https://tc39.es/ecma262/2021/#sec-constructor-properties-of-the-global-object */ - if (tsManager_->IsClassTypeKind(funcType) && tsManager_->IsBuiltin(funcType)) { + if (tsManager_->IsBuiltinObjectType(funcType)) { // For simplicity, calling and constructing are considered equivalent. + if (tsManager_->IsBuiltinClassType(BuiltinTypeId::ARRAY, funcType)) { + return UpdateType(gate, tsManager_->CreateArrayType()); + } return UpdateType(gate, tsManager_->CreateClassInstanceType(funcType)); } return UpdateType(gate, GateType::AnyType()); @@ -814,6 +910,12 @@ bool MethodTypeInfer::InferLdObjByValue(GateRef gate) auto elementType = tsManager_->GetArrayParameterTypeGT(objType); return UpdateType(gate, elementType); } + if (tsManager_->IsIntTypedArrayType(objType)) { + return UpdateType(gate, GateType::IntType()); + } + if (tsManager_->IsDoubleTypedArrayType(objType)) { + return UpdateType(gate, GateType::DoubleType()); + } // handle object if (ShouldInferWithLdObjByValue(objType)) { auto valueGate = gateAccessor_.GetValueIn(gate, 2); // 2: value input slot @@ -1016,7 +1118,7 @@ bool MethodTypeInfer::InferLdExternalModuleVar(GateRef gate) JSHandle moduleArray(thread, moduleEnvironment); JSTaggedValue resolvedBinding = moduleArray->Get(index); // if resolvedBinding.IsHole(), means that importname is * or it belongs to empty Aot module. - if (resolvedBinding.IsHole()) { + if (!resolvedBinding.IsResolvedIndexBinding()) { return UpdateType(gate, GateType::AnyType()); } ResolvedIndexBinding *binding = ResolvedIndexBinding::Cast(resolvedBinding.GetTaggedObject()); diff --git a/ecmascript/compiler/type_inference/method_type_infer.h b/ecmascript/compiler/type_inference/method_type_infer.h index 072e17290a244aa40c963830856895a2e05b76bf..31d8f99d877e36396c27ad1528c62064c23691f2 100644 --- a/ecmascript/compiler/type_inference/method_type_infer.h +++ b/ecmascript/compiler/type_inference/method_type_infer.h @@ -66,6 +66,7 @@ private: // savePreType: save the previous type, which is true by default bool UpdateType(GateRef gate, const GateType type, bool savePreType = true); bool UpdateType(GateRef gate, const GlobalTSTypeRef &typeRef, bool savePreType = true); + GateType HandleTypeCompatibility(const GateType preType, const GateType type) const; bool ShouldInfer(const GateRef gate) const; bool Infer(GateRef gate); bool InferPhiGate(GateRef gate); @@ -83,8 +84,10 @@ private: bool InferAdd2(GateRef gate); bool InferSub2(GateRef gate); bool InferMul2(GateRef gate); + bool InferMod2(GateRef gate); bool InferDiv2(GateRef gate); bool InferIncDec(GateRef gate); + bool InferToNumberic(GateRef gate); bool InferLdObjByIndex(GateRef gate); bool InferLdGlobalVar(GateRef gate); bool InferReturnUndefined(GateRef gate); @@ -94,6 +97,7 @@ private: bool SetStGlobalBcType(GateRef gate, bool hasIC = false); bool InferLdStr(GateRef gate); bool InferCallFunction(GateRef gate); + bool InferCallMethod(GateRef gate); bool InferLdObjByValue(GateRef gate); bool InferGetNextPropName(GateRef gate); bool InferDefineGetterSetterByValue(GateRef gate); diff --git a/ecmascript/compiler/type_inference/pgo_type_infer.cpp b/ecmascript/compiler/type_inference/pgo_type_infer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6840fce4c0c74ab815967f9da1d0eb3175b42fcc --- /dev/null +++ b/ecmascript/compiler/type_inference/pgo_type_infer.cpp @@ -0,0 +1,268 @@ +/* + * 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/compiler/type_inference/pgo_type_infer.h" +#include "ecmascript/ts_types/ts_type_accessor.h" +#include "ecmascript/compiler/bytecode_circuit_builder.h" + +namespace panda::ecmascript::kungfu { +void PGOTypeInfer::Run() +{ + std::vector gateList; + circuit_->GetAllGates(gateList); + for (const auto &gate : gateList) { + auto op = acc_.GetOpCode(gate); + if (op == OpCode::JS_BYTECODE) { + RunTypeInfer(gate); + } + } + + if (IsLogEnabled()) { + Print(); + } +} + +void PGOTypeInfer::RunTypeInfer(GateRef gate) +{ + ASSERT(acc_.GetOpCode(gate) == OpCode::JS_BYTECODE); + EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate); + switch (ecmaOpcode) { + case EcmaOpcode::LDOBJBYNAME_IMM8_ID16: + case EcmaOpcode::LDOBJBYNAME_IMM16_ID16: + case EcmaOpcode::LDTHISBYNAME_IMM8_ID16: + case EcmaOpcode::LDTHISBYNAME_IMM16_ID16: + InferLdObjByName(gate); + break; + case EcmaOpcode::STOBJBYNAME_IMM8_ID16_V8: + case EcmaOpcode::STOBJBYNAME_IMM16_ID16_V8: + InferStObjByName(gate, false); + break; + case EcmaOpcode::STTHISBYNAME_IMM8_ID16: + case EcmaOpcode::STTHISBYNAME_IMM16_ID16: + InferStObjByName(gate, true); + break; + case EcmaOpcode::STOWNBYNAME_IMM8_ID16_V8: + case EcmaOpcode::STOWNBYNAME_IMM16_ID16_V8: + InferStOwnByName(gate); + break; + case EcmaOpcode::LDOBJBYVALUE_IMM8_V8: + case EcmaOpcode::LDOBJBYVALUE_IMM16_V8: + case EcmaOpcode::LDTHISBYVALUE_IMM8: + case EcmaOpcode::LDTHISBYVALUE_IMM16: + case EcmaOpcode::STOBJBYVALUE_IMM8_V8_V8: + case EcmaOpcode::STOBJBYVALUE_IMM16_V8_V8: + case EcmaOpcode::STTHISBYVALUE_IMM8_V8: + case EcmaOpcode::STTHISBYVALUE_IMM16_V8: + InferAccessObjByValue(gate); + break; + case EcmaOpcode::CREATEEMPTYARRAY_IMM8: + case EcmaOpcode::CREATEEMPTYARRAY_IMM16: + case EcmaOpcode::CREATEARRAYWITHBUFFER_IMM8_ID16: + case EcmaOpcode::CREATEARRAYWITHBUFFER_IMM16_ID16: + InferCreateArray(gate); + break; + default: + break; + } +} + +void PGOTypeInfer::Print() const +{ + LOG_COMPILER(INFO) << " "; + LOG_COMPILER(INFO) << "\033[34m" << "=================" + << " After pgo type infer " + << "[" << GetMethodName() << "] " + << "=================" << "\033[0m"; + + for (const auto &value : profiler_.datas) { + std::string log("\"id\"=" + std::to_string(acc_.GetId(value.gate)) + + ", \"bytecode\"=\"" + GetEcmaOpcodeStr(acc_.GetByteCodeOpcode(value.gate)) + "\", "); + log += "TSType: [type:" + tsManager_->GetTypeStr(value.tsType) + ", " + + "moduleId: " + std::to_string(value.tsType.GetGTRef().GetModuleId()) + ", " + + "localId: " + std::to_string(value.tsType.GetGTRef().GetLocalId()) + "], "; + log += "PGOTypes: ["; + for (auto pgoType : value.pgoTypes) { + log += "[type:" + tsManager_->GetTypeStr(pgoType) + ", " + + "moduleId: " + std::to_string(pgoType.GetGTRef().GetModuleId()) + ", " + + "localId: " + std::to_string(pgoType.GetGTRef().GetLocalId()) + "], "; + } + log += "] InferTypes: ["; + for (auto type : value.inferTypes) { + log += "[type:" + tsManager_->GetTypeStr(type) + ", " + + "moduleId: " + std::to_string(type.GetGTRef().GetModuleId()) + ", " + + "localId: " + std::to_string(type.GetGTRef().GetLocalId()) + "]"; + } + log += "]"; + LOG_COMPILER(INFO) << std::dec << log; + } + LOG_COMPILER(INFO) << "\033[34m" << "=========================== End ===========================" << "\033[0m"; +} + +void PGOTypeInfer::AddProfiler(GateRef gate, GateType tsType, PGORWOpType pgoType, ChunkSet& inferTypes) +{ + if (enableLog_) { + Profiler::Value value; + value.gate = gate; + value.tsType = tsType; + for (uint32_t i = 0; i < pgoType.GetCount(); i++) { + value.pgoTypes.emplace_back(tsManager_->GetGateTypeByPt(pgoType.GetObjectInfo(i).GetProfileType())); + } + for (GateType type : inferTypes) { + value.inferTypes.emplace_back(type); + } + profiler_.datas.emplace_back(value); + } +} + +void PGOTypeInfer::InferLdObjByName(GateRef gate) +{ + if (!builder_->ShouldPGOTypeInfer(gate)) { + return; + } + GateRef constData = acc_.GetValueIn(gate, 1); // 1: valueIn 1 + uint16_t propIndex = acc_.GetConstantValue(constData); + JSTaggedValue prop = tsManager_->GetStringFromConstantPool(propIndex); + GateRef receiver = acc_.GetValueIn(gate, 2); // 2: acc or this object + + UpdateTypeForRWOp(gate, receiver, prop); +} + +void PGOTypeInfer::InferStObjByName(GateRef gate, bool isThis) +{ + if (!builder_->ShouldPGOTypeInfer(gate)) { + return; + } + GateRef constData = acc_.GetValueIn(gate, 1); // 1: valueIn 1 + uint16_t propIndex = acc_.GetConstantValue(constData); + JSTaggedValue prop = tsManager_->GetStringFromConstantPool(propIndex); + GateRef receiver = Circuit::NullGate(); + if (isThis) { + // 3: number of value inputs + ASSERT(acc_.GetNumValueIn(gate) == 3); + receiver = argAcc_.GetFrameArgsIn(gate, FrameArgIdx::THIS_OBJECT); + } else { + // 4: number of value inputs + ASSERT(acc_.GetNumValueIn(gate) == 4); + receiver = acc_.GetValueIn(gate, 2); // 2: receiver + } + + UpdateTypeForRWOp(gate, receiver, prop); +} + +void PGOTypeInfer::InferStOwnByName(GateRef gate) +{ + if (!builder_->ShouldPGOTypeInfer(gate)) { + return; + } + // 3: number of value inputs + ASSERT(acc_.GetNumValueIn(gate) == 3); + GateRef constData = acc_.GetValueIn(gate, 0); + uint16_t propIndex = acc_.GetConstantValue(constData); + JSTaggedValue prop = tsManager_->GetStringFromConstantPool(propIndex); + GateRef receiver = acc_.GetValueIn(gate, 1); + + UpdateTypeForRWOp(gate, receiver, prop); +} + +void PGOTypeInfer::InferCreateArray(GateRef gate) +{ + if (!builder_->ShouldPGOTypeInfer(gate)) { + return; + } + + ElementsKind kind = builder_->GetArrayElementsKind(gate); + if (Elements::IsGeneric(kind)) { + return; + } + + acc_.TrySetElementsKind(gate, kind); +} + +void PGOTypeInfer::InferAccessObjByValue(GateRef gate) +{ + if (!builder_->ShouldPGOTypeInfer(gate)) { + return; + } + GateRef receiver = Circuit::NullGate(); + GateRef propKey = Circuit::NullGate(); + EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate); + switch (ecmaOpcode) { + case EcmaOpcode::LDOBJBYVALUE_IMM8_V8: + case EcmaOpcode::LDOBJBYVALUE_IMM16_V8: + case EcmaOpcode::STOBJBYVALUE_IMM8_V8_V8: + case EcmaOpcode::STOBJBYVALUE_IMM16_V8_V8: + receiver = acc_.GetValueIn(gate, 1); // 1: receiver + propKey = acc_.GetValueIn(gate, 2); // 2: the third parameter + break; + case EcmaOpcode::LDTHISBYVALUE_IMM8: + case EcmaOpcode::LDTHISBYVALUE_IMM16: + case EcmaOpcode::STTHISBYVALUE_IMM8_V8: + case EcmaOpcode::STTHISBYVALUE_IMM16_V8: + receiver = argAcc_.GetFrameArgsIn(gate, FrameArgIdx::THIS_OBJECT); + propKey = acc_.GetValueIn(gate, 1); + break; + default: + LOG_ECMA(FATAL) << "this branch is unreachable"; + UNREACHABLE(); + } + UpdateTypeForRWOp(gate, receiver); + TrySetPropKeyKind(gate, propKey); + TrySetElementsKind(gate); +} + +void PGOTypeInfer::UpdateTypeForRWOp(GateRef gate, GateRef receiver, JSTaggedValue prop) +{ + GateType tsType = acc_.GetGateType(receiver); + PGORWOpType pgoTypes = builder_->GetPGOType(gate); + + CollectedType types(chunk_); + helper_.CollectGateType(types, tsType, pgoTypes); + + // polymorphism is not currently supported, + // all types must in the same kind + if (!types.AllInSameKind()) { + return; + } + + // polymorphism is not currently supported + ChunkSet inferTypes = helper_.GetInferTypes(chunk_, types, prop); + AddProfiler(gate, tsType, pgoTypes, inferTypes); + if (!IsMonoTypes(inferTypes)) { + return; + } + + acc_.SetGateType(receiver, *inferTypes.begin()); +} + +void PGOTypeInfer::TrySetElementsKind(GateRef gate) +{ + auto kinds = builder_->LoadElementsKinds(gate); + if (kinds.empty()) { + return; + } + for (auto kind : kinds) { + acc_.TrySetElementsKind(gate, kind); + } +} + +void PGOTypeInfer::TrySetPropKeyKind(GateRef gate, GateRef propKey) +{ + PGORWOpType pgoTypes = builder_->GetPGOType(gate); + GateType oldType = acc_.GetGateType(propKey); + if (oldType.IsAnyType() && IsMonoNumberType(pgoTypes)) { + acc_.SetGateType(propKey, GateType::NumberType()); + } +} +} // namespace panda::ecmascript diff --git a/ecmascript/compiler/type_inference/pgo_type_infer.h b/ecmascript/compiler/type_inference/pgo_type_infer.h new file mode 100644 index 0000000000000000000000000000000000000000..45469155ecac6537057af8ae4d3a6a8029ce43b9 --- /dev/null +++ b/ecmascript/compiler/type_inference/pgo_type_infer.h @@ -0,0 +1,96 @@ +/* + * 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_COMPILER_TYPE_INFERENCE_PGO_TYPE_INFER_H +#define ECMASCRIPT_COMPILER_TYPE_INFERENCE_PGO_TYPE_INFER_H + +#include "ecmascript/compiler/type_inference/pgo_type_infer_helper.h" +#include "ecmascript/compiler/gate_accessor.h" +#include "ecmascript/ts_types/ts_manager.h" +#include "ecmascript/compiler/argument_accessor.h" + +namespace panda::ecmascript::kungfu { +struct CollectedType; +class PGOTypeInfer { +public: + PGOTypeInfer(Circuit *circuit, TSManager *tsManager, BytecodeCircuitBuilder *builder, + const std::string &name, Chunk *chunk, bool enableLog) + : circuit_(circuit), acc_(circuit), argAcc_(circuit), tsManager_(tsManager), helper_(tsManager), + builder_(builder), methodName_(name), chunk_(chunk), enableLog_(enableLog), profiler_(chunk) {} + ~PGOTypeInfer() = default; + + void Run(); + +private: + struct Profiler { + struct Value { + GateRef gate; + GateType tsType; + CVector pgoTypes; + CVector inferTypes; + }; + Profiler(Chunk *chunk) : datas(chunk) {} + ChunkVector datas; + }; + + inline bool IsLogEnabled() const + { + return enableLog_; + } + + inline const std::string &GetMethodName() const + { + return methodName_; + } + + inline bool IsMonoTypes(const ChunkSet &types) const + { + return types.size() == 1; + } + + inline bool IsMonoNumberType(PGORWOpType &pgoTypes) const + { + // "ldobjbyvalue" will collect the type of the variable inside the square brackets while pgo collecting. + // If the type is "number", it will be marked as an "Element". + return pgoTypes.GetCount() == 1 && pgoTypes.GetObjectInfo(0).InElement(); + } + + void RunTypeInfer(GateRef gate); + void InferLdObjByName(GateRef gate); + void InferStObjByName(GateRef gate, bool isThis); + void InferStOwnByName(GateRef gate); + void InferAccessObjByValue(GateRef gate); + void InferCreateArray(GateRef gate); + + void UpdateTypeForRWOp(GateRef gate, GateRef receiver, JSTaggedValue prop = JSTaggedValue::Undefined()); + void TrySetElementsKind(GateRef gate); + void TrySetPropKeyKind(GateRef gate, GateRef propKey); + + void Print() const; + void AddProfiler(GateRef gate, GateType tsType, PGORWOpType pgoType, ChunkSet& inferTypes); + + Circuit *circuit_ {nullptr}; + GateAccessor acc_; + ArgumentAccessor argAcc_; + TSManager *tsManager_ {nullptr}; + PGOTypeInferHelper helper_; + BytecodeCircuitBuilder *builder_ {nullptr}; + const std::string &methodName_; + Chunk *chunk_ {nullptr}; + bool enableLog_ {false}; + Profiler profiler_; +}; +} // panda::ecmascript::kungfu +#endif // ECMASCRIPT_COMPILER_TYPE_INFERENCE_PGO_TYPE_INFER_H diff --git a/ecmascript/compiler/type_inference/pgo_type_infer_helper.cpp b/ecmascript/compiler/type_inference/pgo_type_infer_helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..356ef156f8ca207d90d95079280fcf2121d185fd --- /dev/null +++ b/ecmascript/compiler/type_inference/pgo_type_infer_helper.cpp @@ -0,0 +1,251 @@ +/* + * 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/compiler/type_inference/pgo_type_infer_helper.h" +#include "ecmascript/ts_types/ts_type_accessor.h" + +namespace panda::ecmascript::kungfu { +bool ClassTypeStrategy::CheckAndInsert(CollectedType &types, const GateType &type, TSManager *tsManager) +{ + if (tsManager->IsUserDefinedClassTypeKind(type)) { + int hclassIndex = tsManager->GetConstructorHClassIndexByClassGateType(type); + if (hclassIndex == -1) { + return true; + } + types.classTypes.insert(type); + return true; + } + return false; +} + +void ClassTypeStrategy::Merge(ChunkSet &inferTypes, CollectedType &types, + [[maybe_unused]] TSManager *tsManager) +{ + for (GateType type : types.classTypes) { + inferTypes.insert(type); + } +} + +bool ClassInstanceTypeStrategy::CheckAndInsert(CollectedType &types, const GateType &type, TSManager *tsManager) +{ + if (tsManager->IsClassInstanceTypeKind(type) && + tsManager->IsUserDefinedClassTypeKind(tsManager->GetClassType(type))) { + int hclassIndex = tsManager->GetHClassIndexByInstanceGateType(type); + if (hclassIndex == -1) { + return true; + } + JSHClass *hclass = JSHClass::Cast(tsManager->GetValueFromCache(hclassIndex).GetTaggedObject()); + if (hclass->HasTSSubtyping()) { + GlobalTSTypeRef instanceGT = type.GetGTRef(); + GateType classType = GateType(tsManager->GetClassType(instanceGT)); + types.classInstanceTypes.insert(classType); + } + return true; + } + return false; +} + +void ClassInstanceTypeStrategy::Merge(ChunkSet &inferTypes, CollectedType &types, TSManager *tsManager) +{ + for (GateType type : types.classInstanceTypes) { + GlobalTSTypeRef gt = type.GetGTRef(); + GlobalTSTypeRef instanceGT = tsManager->CreateClassInstanceType(gt); + inferTypes.insert(GateType(instanceGT)); + } +} + +bool BuiltinTypeStrategy::CheckAndInsert(CollectedType &types, const GateType &type, TSManager *tsManager) +{ + if (tsManager->IsValidTypedArrayType(type)) { + types.builtinTypes.insert(type); + return true; + } + return false; +} + +void BuiltinTypeStrategy::Merge(ChunkSet &inferTypes, CollectedType &types, + [[maybe_unused]] TSManager *tsManager) +{ + for (GateType type : types.builtinTypes) { + inferTypes.insert(type); + } +} + +bool OtherTypeStrategy::CheckAndInsert(CollectedType &types, const GateType &type, [[maybe_unused]]TSManager *tsManager) +{ + if (!type.IsAnyType()) { + types.otherTypes.insert(type); + return true; + } + return false; +} + +void OtherTypeStrategy::Merge(ChunkSet &inferTypes, CollectedType &types, + [[maybe_unused]] TSManager *tsManager) +{ + for (GateType type : types.otherTypes) { + inferTypes.insert(type); + } +} + +void PGOTypeInferHelper::CollectGateType(CollectedType &types, GateType tsType, PGORWOpType pgoTypes) +{ + // for static TS uinon type + if (tsManager_->IsUnionTypeKind(tsType)) { + JSHandle unionType(tsManager_->GetTSType(tsType.GetGTRef())); + TaggedArray *components = TaggedArray::Cast(unionType->GetComponents().GetTaggedObject()); + uint32_t length = components->GetLength(); + for (uint32_t i = 0; i < length; ++i) { + GlobalTSTypeRef gt(components->Get(i).GetInt()); + CheckAndInsert(types, GateType(gt)); + } + } else { + CheckAndInsert(types, tsType); + } + + // for pgo type + for (uint32_t i = 0; i < pgoTypes.GetCount(); i++) { + ProfileType profileType = pgoTypes.GetObjectInfo(i).GetProfileType(); + GateType pgoType = tsManager_->GetGateTypeByPt(profileType); + if (pgoType.IsAnyType()) { + tsManager_->AddToSkipTrackFieldSet(profileType); + continue; + } + if (tsManager_->IsClassTypeKind(pgoType) && !pgoTypes.GetObjectInfo(i).InConstructor()) { + pgoType = GateType(tsManager_->CreateClassInstanceType(pgoType)); + } + CheckAndInsert(types, pgoType); + } +} + +ChunkSet PGOTypeInferHelper::GetInferTypes(Chunk *chunk, CollectedType &types, JSTaggedValue prop) +{ + UpdateType(types, prop); + ChunkSet inferTypes(chunk); + for (auto &strategy : strategies_) { + strategy->Merge(inferTypes, types, tsManager_); + } + return inferTypes; +} + +void PGOTypeInferHelper::CheckAndInsert(CollectedType &types, GateType type) +{ + for (auto &strategy : strategies_) { + if (strategy->CheckAndInsert(types, type, tsManager_)) { + return; + } + } +} + +void PGOTypeInferHelper::UpdateType(CollectedType &types, JSTaggedValue prop) +{ + ChunkSet &classTypes = types.classTypes; + InferTypeForClass(classTypes, prop); + + ChunkSet &classInstanceTypes = types.classInstanceTypes; + InferTypeForClass(classInstanceTypes, prop); + + ChunkSet &builtinTypes = types.builtinTypes; + InferTypeForBuiltin(builtinTypes); +} + +void PGOTypeInferHelper::InferTypeForClass(ChunkSet &types, JSTaggedValue prop) +{ + if (NoNeedUpdate(types)) { + return; + } + EliminateSubclassTypes(types); + ComputeCommonSuperClassTypes(types, prop); +} + +void PGOTypeInferHelper::InferTypeForBuiltin(ChunkSet &types) +{ + if (NoNeedUpdate(types)) { + return; + } + std::set deletedGates; + std::set builtinGTSet; + + for (GateType type : types) { + GlobalTSTypeRef classGT = tsManager_->GetClassType(type); + auto it = builtinGTSet.find(classGT); + if (it != builtinGTSet.end()) { + deletedGates.insert(type); + continue; + } + builtinGTSet.insert(classGT); + } + for (GateType gateType : deletedGates) { + types.erase(gateType); + } +} + +void PGOTypeInferHelper::EliminateSubclassTypes(ChunkSet &types) +{ + std::set deletedGates; + for (GateType type : types) { + if (deletedGates.find(type) != deletedGates.end()) { + continue; + } + + std::stack ancestors; + GateType curType = type; + do { + if (types.find(curType) != types.end()) { + ancestors.push(curType); + } + } while (tsManager_->GetSuperGateType(curType)); + + ancestors.pop(); // top type is alive + while (!ancestors.empty()) { + curType = ancestors.top(); + ancestors.pop(); + auto it = deletedGates.find(curType); + if (it != deletedGates.end()) { + deletedGates.insert(curType); + } + } + } + for (GateType gateType : deletedGates) { + types.erase(gateType); + } +} + +void PGOTypeInferHelper::ComputeCommonSuperClassTypes(ChunkSet &types, JSTaggedValue prop) +{ + JSThread *thread = tsManager_->GetEcmaVM()->GetJSThread(); + std::map removeTypes; + for (GateType type : types) { + GateType curType = type; + GateType preType = type; + while (tsManager_->GetSuperGateType(curType)) { + JSHClass *ihc = JSHClass::Cast(tsManager_->GetTSHClass(curType).GetTaggedObject()); + PropertyLookupResult plr = JSHClass::LookupPropertyInAotHClass(thread, ihc, prop); + if (!plr.IsFound()) { + break; + } + preType = curType; + } + if (type != preType) { + removeTypes[type] = preType; + } + } + + for (auto item : removeTypes) { + types.erase(item.first); + types.insert(item.second); + } +} +} // namespace panda::ecmascript diff --git a/ecmascript/compiler/type_inference/pgo_type_infer_helper.h b/ecmascript/compiler/type_inference/pgo_type_infer_helper.h new file mode 100644 index 0000000000000000000000000000000000000000..f4427776135ff5b831ed5774b8787bd6e6aec1fe --- /dev/null +++ b/ecmascript/compiler/type_inference/pgo_type_infer_helper.h @@ -0,0 +1,104 @@ +/* + * 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_COMPILER_TYPE_INFERENCE_PGO_TYPE_INFER_HELPER_H +#define ECMASCRIPT_COMPILER_TYPE_INFERENCE_PGO_TYPE_INFER_HELPER_H + +#include "ecmascript/ts_types/ts_manager.h" + +namespace panda::ecmascript::kungfu { +#define TYPE_LIST(V) \ + V(Class, class) \ + V(ClassInstance, classInstance) \ + V(Builtin, builtin) \ + V(Other, other) + +struct CollectedType { + explicit CollectedType(Chunk *chunk) : + classTypes(chunk), + classInstanceTypes(chunk), + builtinTypes(chunk), + otherTypes(chunk) + {} + + bool AllInSameKind() const + { + uint8_t kind = classTypes.empty() ? 0 : 1; + kind += classInstanceTypes.empty() ? 0 : 1; + kind += builtinTypes.empty() ? 0 : 1; + kind += otherTypes.empty() ? 0 : 1; + return kind == 1; + } + +#define DEFINE_TYPE_SET(V, name) \ + ChunkSet name##Types; + + TYPE_LIST(DEFINE_TYPE_SET) +#undef DEFINE_TYPE_SET +}; + +class TypeStrategy { +public: + TypeStrategy() = default; + virtual ~TypeStrategy() = default; + virtual bool CheckAndInsert(CollectedType &types, const GateType &type, TSManager *tsManager) = 0; + virtual void Merge(ChunkSet &inferTypes, CollectedType &types, TSManager *tsManager) = 0; +}; + +#define DEFINE_TYPE_STRATEGY_DERIVED_CLASS(name, ...) \ + class name##TypeStrategy : public TypeStrategy { \ + public: \ + virtual bool CheckAndInsert(CollectedType &types, const GateType &type, TSManager *tsManager) override; \ + virtual void Merge(ChunkSet &inferTypes, CollectedType &types, TSManager *tsManager) override; \ + }; + + TYPE_LIST(DEFINE_TYPE_STRATEGY_DERIVED_CLASS) +#undef DEFINE_TYPE_STRATEGY_DERIVED_CLASS + +class PGOTypeInferHelper { +public: + PGOTypeInferHelper(TSManager *tsManager) : tsManager_(tsManager) + { +#define ADD_STRATEGY(name, ...) \ + strategies_.push_back(std::make_unique()); + + TYPE_LIST(ADD_STRATEGY) +#undef ADD_STRATEGY + } + ~PGOTypeInferHelper() = default; + + void CollectGateType(CollectedType &types, GateType tsType, PGORWOpType pgoTypes); + + ChunkSet GetInferTypes(Chunk *chunk, CollectedType &types, JSTaggedValue prop); + +private: + inline bool NoNeedUpdate(const ChunkSet &types) const + { + return types.size() <= 1; + } + + void CheckAndInsert(CollectedType &types, GateType type); + void UpdateType(CollectedType &types, JSTaggedValue prop); + void InferTypeForClass(ChunkSet &types, JSTaggedValue prop); + void EliminateSubclassTypes(ChunkSet &types); + void ComputeCommonSuperClassTypes(ChunkSet &types, JSTaggedValue prop); + void InferTypeForBuiltin(ChunkSet &types); + + TSManager *tsManager_ {nullptr}; + std::vector> strategies_ {}; +}; +#undef TYPE_LIST +} // panda::ecmascript::kungfu +#endif // ECMASCRIPT_COMPILER_TYPE_INFERENCE_PGO_TYPE_INFER_HELPER_H diff --git a/ecmascript/compiler/type_mcr_lowering.cpp b/ecmascript/compiler/type_mcr_lowering.cpp index 5d1a25a720c911d9fb3931a87de5a712bb5693c6..e5314007a86da85e7df2c9b558893978d6912654 100644 --- a/ecmascript/compiler/type_mcr_lowering.cpp +++ b/ecmascript/compiler/type_mcr_lowering.cpp @@ -15,6 +15,7 @@ #include "ecmascript/compiler/type_mcr_lowering.h" #include "ecmascript/compiler/builtins_lowering.h" +#include "ecmascript/compiler/share_gate_meta_data.h" #include "ecmascript/compiler/new_object_stub_builder.h" #include "ecmascript/deoptimizer/deoptimizer.h" #include "ecmascript/js_arraybuffer.h" @@ -23,26 +24,8 @@ #include "ecmascript/vtable.h" #include "ecmascript/message_string.h" namespace panda::ecmascript::kungfu { -void TypeMCRLowering::RunTypeMCRLowering() -{ - std::vector gateList; - circuit_->GetAllGates(gateList); - for (const auto &gate : gateList) { - LowerType(gate); - } - - if (IsLogEnabled()) { - LOG_COMPILER(INFO) << ""; - LOG_COMPILER(INFO) << "\033[34m" << "==================" - << " after TypeMCRlowering " - << "[" << GetMethodName() << "] " - << "==================" << "\033[0m"; - circuit_->PrintAllGatesWithBytecode(); - LOG_COMPILER(INFO) << "\033[34m" << "=========================== End =========================" << "\033[0m"; - } -} -void TypeMCRLowering::LowerType(GateRef gate) +GateRef TypeMCRLowering::VisitGate(GateRef gate) { GateRef glue = acc_.GetGlueFromArgList(); auto op = acc_.GetOpCode(gate); @@ -56,26 +39,32 @@ void TypeMCRLowering::LowerType(GateRef gate) case OpCode::TYPED_ARRAY_CHECK: LowerTypedArrayCheck(gate); break; - case OpCode::OBJECT_TYPE_CHECK: - LowerObjectTypeCheck(gate); + case OpCode::ECMA_STRING_CHECK: + LowerEcmaStringCheck(gate); break; - case OpCode::INDEX_CHECK: - LowerIndexCheck(gate); + case OpCode::FLATTEN_TREE_STRING_CHECK: + LowerFlattenTreeStringCheck(gate, glue); break; - case OpCode::JSCALLTARGET_FROM_DEFINEFUNC_CHECK: - LowerJSCallTargetFromDefineFuncCheck(gate); + case OpCode::LOAD_STRING_LENGTH: + LowerStringLength(gate); break; - case OpCode::JSCALLTARGET_TYPE_CHECK: - LowerJSCallTargetTypeCheck(gate); + case OpCode::LOAD_TYPED_ARRAY_LENGTH: + LowerLoadTypedArrayLength(gate); break; - case OpCode::JSFASTCALLTARGET_TYPE_CHECK: - LowerJSFastCallTargetTypeCheck(gate); + case OpCode::OBJECT_TYPE_CHECK: + LowerObjectTypeCheck(gate); break; - case OpCode::JSCALLTHISTARGET_TYPE_CHECK: - LowerJSCallThisTargetTypeCheck(gate); + case OpCode::OBJECT_TYPE_COMPARE: + LowerObjectTypeCompare(gate); break; - case OpCode::JSFASTCALLTHISTARGET_TYPE_CHECK: - LowerJSFastCallThisTargetTypeCheck(gate); + case OpCode::RANGE_CHECK_PREDICATE: + LowerRangeCheckPredicate(gate); + break; + case OpCode::INDEX_CHECK: + LowerIndexCheck(gate); + break; + case OpCode::TYPED_CALLTARGETCHECK_OP: + LowerJSCallTargetCheck(gate); break; case OpCode::TYPED_CALL_CHECK: LowerCallTargetCheck(gate); @@ -120,12 +109,69 @@ void TypeMCRLowering::LowerType(GateRef gate) case OpCode::GET_SUPER_CONSTRUCTOR: LowerGetSuperConstructor(gate); break; - case OpCode::CREATE_ARRAY: - LowerCreateArray(gate); + case OpCode::COW_ARRAY_CHECK: + LowerCowArrayCheck(gate, glue); + break; + case OpCode::LOAD_GETTER: + LowerLoadGetter(gate); + break; + case OpCode::LOAD_SETTER: + LowerLoadSetter(gate); + break; + case OpCode::INLINE_ACCESSOR_CHECK: + LowerInlineAccessorCheck(gate); + break; + case OpCode::STRING_EQUAL: + LowerStringEqual(gate, glue); + break; + case OpCode::TYPE_OF_CHECK: + LowerTypeOfCheck(gate); + break; + case OpCode::TYPE_OF: + LowerTypeOf(gate, glue); break; default: break; } + return Circuit::NullGate(); +} + +void TypeMCRLowering::LowerJSCallTargetCheck(GateRef gate) +{ + TypedCallTargetCheckOp Op = acc_.GetTypedCallTargetCheckOp(gate); + switch (Op) { + case TypedCallTargetCheckOp::JSCALL_IMMEDIATE_AFTER_FUNC_DEF: { + LowerJSCallTargetFromDefineFuncCheck(gate); + break; + } + case TypedCallTargetCheckOp::JSCALL: { + LowerJSCallTargetTypeCheck(gate); + break; + } + case TypedCallTargetCheckOp::JSCALL_FAST: { + LowerJSFastCallTargetTypeCheck(gate); + break; + } + case TypedCallTargetCheckOp::JSCALLTHIS: { + LowerJSCallThisTargetTypeCheck(gate); + break; + } + case TypedCallTargetCheckOp::JSCALLTHIS_FAST: { + LowerJSFastCallThisTargetTypeCheck(gate); + break; + } + case TypedCallTargetCheckOp::JSCALLTHIS_NOGC: { + LowerJSNoGCCallThisTargetTypeCheck(gate); + break; + } + case TypedCallTargetCheckOp::JSCALLTHIS_FAST_NOGC: { + LowerJSNoGCFastCallThisTargetTypeCheck(gate); + break; + } + default: + LOG_ECMA(FATAL) << "this branch is unreachable"; + UNREACHABLE(); + } } void TypeMCRLowering::LowerPrimitiveTypeCheck(GateRef gate) @@ -200,87 +246,218 @@ void TypeMCRLowering::LowerStableArrayCheck(GateRef gate) GateRef receiverHClass = builder_.LoadConstOffset( VariableType::JS_POINTER(), receiver, TaggedObject::HCLASS_OFFSET); - builder_.HClassStableArrayCheck(receiverHClass, frameState); + ArrayMetaDataAccessor accessor = acc_.GetArrayMetaDataAccessor(gate); + builder_.HClassStableArrayCheck(receiverHClass, frameState, accessor); builder_.ArrayGuardianCheck(frameState); acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), Circuit::NullGate()); } -void TypeMCRLowering::LowerTypedArrayCheck(GateRef gate) +void TypeMCRLowering::SetDeoptTypeInfo(BuiltinTypeId id, DeoptType &type, size_t &funcIndex) { - Environment env(gate, circuit_, &builder_); - auto type = acc_.GetParamGateType(gate); - if (tsManager_->IsFloat32ArrayType(type)) { - LowerFloat32ArrayCheck(gate); - } else { - LOG_ECMA(FATAL) << "this branch is unreachable"; - UNREACHABLE(); + type = DeoptType::NOTARRAY; + switch (id) { + case BuiltinTypeId::INT8_ARRAY: + funcIndex = GlobalEnv::INT8_ARRAY_FUNCTION_INDEX; + break; + case BuiltinTypeId::UINT8_ARRAY: + funcIndex = GlobalEnv::UINT8_ARRAY_FUNCTION_INDEX; + break; + case BuiltinTypeId::UINT8_CLAMPED_ARRAY: + funcIndex = GlobalEnv::UINT8_CLAMPED_ARRAY_FUNCTION_INDEX; + break; + case BuiltinTypeId::INT16_ARRAY: + funcIndex = GlobalEnv::INT16_ARRAY_FUNCTION_INDEX; + break; + case BuiltinTypeId::UINT16_ARRAY: + funcIndex = GlobalEnv::UINT16_ARRAY_FUNCTION_INDEX; + break; + case BuiltinTypeId::INT32_ARRAY: + funcIndex = GlobalEnv::INT32_ARRAY_FUNCTION_INDEX; + break; + case BuiltinTypeId::UINT32_ARRAY: + funcIndex = GlobalEnv::UINT32_ARRAY_FUNCTION_INDEX; + break; + case BuiltinTypeId::FLOAT32_ARRAY: + funcIndex = GlobalEnv::FLOAT32_ARRAY_FUNCTION_INDEX; + break; + case BuiltinTypeId::FLOAT64_ARRAY: + funcIndex = GlobalEnv::FLOAT64_ARRAY_FUNCTION_INDEX; + break; + default: + LOG_ECMA(FATAL) << "this branch is unreachable"; + UNREACHABLE(); } } -void TypeMCRLowering::LowerFloat32ArrayCheck(GateRef gate) +void TypeMCRLowering::LowerTypedArrayCheck(GateRef gate) { - GateRef frameState = GetFrameState(gate); + Environment env(gate, circuit_, &builder_); + auto type = acc_.GetParamGateType(gate); + size_t typedArrayFuncIndex = GlobalEnv::TYPED_ARRAY_FUNCTION_INDEX; + auto deoptType = DeoptType::NOTCHECK; + + auto builtinTypeId = tsManager_->GetTypedArrayBuiltinId(type); + SetDeoptTypeInfo(builtinTypeId, deoptType, typedArrayFuncIndex); + GateRef frameState = GetFrameState(gate); GateRef glueGlobalEnv = builder_.GetGlobalEnv(); GateRef receiver = acc_.GetValueIn(gate, 0); GateRef receiverHClass = builder_.LoadHClass(receiver); - GateRef protoOrHclass = builder_.GetGlobalEnvObjHClass(glueGlobalEnv, GlobalEnv::FLOAT32_ARRAY_FUNCTION_INDEX); + GateRef protoOrHclass = builder_.GetGlobalEnvObjHClass(glueGlobalEnv, typedArrayFuncIndex); GateRef check = builder_.Equal(receiverHClass, protoOrHclass); - builder_.DeoptCheck(check, frameState, DeoptType::NOTF32ARRAY); + builder_.DeoptCheck(check, frameState, deoptType); + + if (IsOnHeap()) { + GateRef isOnHeap = builder_.LoadConstOffset(VariableType::BOOL(), receiver, JSTypedArray::ON_HEAP_OFFSET); + builder_.DeoptCheck(isOnHeap, frameState, DeoptType::NOTONHEAP); + } + + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), Circuit::NullGate()); +} + +void TypeMCRLowering::LowerEcmaStringCheck(GateRef gate) +{ + Environment env(gate, circuit_, &builder_); + GateRef frameState = GetFrameState(gate); + GateRef receiver = acc_.GetValueIn(gate, 0); + builder_.HeapObjectCheck(receiver, frameState); + GateRef isString = builder_.TaggedObjectIsString(receiver); + builder_.DeoptCheck(isString, frameState, DeoptType::NOTSTRING); acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), Circuit::NullGate()); } +void TypeMCRLowering::LowerFlattenTreeStringCheck(GateRef gate, GateRef glue) +{ + Environment env(gate, circuit_, &builder_); + GateRef str = acc_.GetValueIn(gate, 0); + DEFVAlUE(result, (&builder_), VariableType::JS_POINTER(), str); + Label isTreeString(&builder_); + Label exit(&builder_); + + builder_.Branch(builder_.IsTreeString(str), &isTreeString, &exit); + builder_.Bind(&isTreeString); + { + Label isFlat(&builder_); + Label needFlat(&builder_); + builder_.Branch(builder_.TreeStringIsFlat(str), &isFlat, &needFlat); + builder_.Bind(&isFlat); + { + result = builder_.GetFirstFromTreeString(str); + builder_.Jump(&exit); + } + builder_.Bind(&needFlat); + { + result = LowerCallRuntime(glue, gate, RTSTUB_ID(SlowFlattenString), { str }, true); + builder_.Jump(&exit); + } + } + + builder_.Bind(&exit); + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), *result); +} + +GateRef TypeMCRLowering::GetLengthFromString(GateRef gate) +{ + GateRef shiftCount = builder_.Int32(EcmaString::STRING_LENGTH_SHIFT_COUNT); + return builder_.Int32LSR( + builder_.LoadConstOffset(VariableType::INT32(), gate, EcmaString::MIX_LENGTH_OFFSET), shiftCount); +} + +void TypeMCRLowering::LowerStringLength(GateRef gate) +{ + Environment env(gate, circuit_, &builder_); + GateRef receiver = acc_.GetValueIn(gate, 0); + GateRef length = GetLengthFromString(receiver); + + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), length); +} + +void TypeMCRLowering::LowerLoadTypedArrayLength(GateRef gate) +{ + Environment env(gate, circuit_, &builder_); + GateRef receiver = acc_.GetValueIn(gate, 0); + GateRef length = builder_.LoadConstOffset(VariableType::INT32(), receiver, JSTypedArray::ARRAY_LENGTH_OFFSET); + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), length); +} + void TypeMCRLowering::LowerObjectTypeCheck(GateRef gate) { Environment env(gate, circuit_, &builder_); - auto type = acc_.GetParamGateType(gate); + GateType type = acc_.GetObjectTypeAccessor(gate).GetType(); if (tsManager_->IsClassInstanceTypeKind(type)) { LowerTSSubtypingCheck(gate); - } else if (tsManager_->IsClassTypeKind(type)) { - LowerConstruntorTypeCheck(gate); + } else if (tsManager_->IsClassTypeKind(type) || + tsManager_->IsObjectTypeKind(type)) { + LowerSimpleHClassCheck(gate); } else { - LOG_ECMA(FATAL) << "this branch is unreachable"; + LOG_COMPILER(FATAL) << "this branch is unreachable"; UNREACHABLE(); } } -void TypeMCRLowering::LowerConstruntorTypeCheck(GateRef gate) +void TypeMCRLowering::LowerTSSubtypingCheck(GateRef gate) +{ + GateRef frameState = GetFrameState(gate); + Label levelValid(&builder_); + Label exit(&builder_); + GateRef compare = BuildCompareSubTyping(gate, frameState, &levelValid, &exit); + builder_.DeoptCheck(compare, frameState, DeoptType::INCONSISTENTHCLASS); + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), Circuit::NullGate()); +} + +void TypeMCRLowering::LowerSimpleHClassCheck(GateRef gate) +{ + GateRef frameState = GetFrameState(gate); + GateRef compare = BuildCompareHClass(gate, frameState); + builder_.DeoptCheck(compare, frameState, DeoptType::INCONSISTENTHCLASS); + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), Circuit::NullGate()); +} + +void TypeMCRLowering::LowerObjectTypeCompare(GateRef gate) { Environment env(gate, circuit_, &builder_); - auto type = acc_.GetParamGateType(gate); - if (tsManager_->IsClassTypeKind(type)) { - GateRef frameState = GetFrameState(gate); - GateRef receiver = acc_.GetValueIn(gate, 0); - builder_.HeapObjectCheck(receiver, frameState); - GateRef aotHCIndex = acc_.GetValueIn(gate, 1); - auto hclassIndex = acc_.GetConstantValue(aotHCIndex); - ArgumentAccessor argAcc(circuit_); - GateRef jsFunc = argAcc.GetFrameArgsIn(frameState, FrameArgIdx::FUNC); - GateRef aotHCGate = LoadFromConstPool(jsFunc, hclassIndex); - GateRef receiverHClass = builder_.LoadConstOffset( - VariableType::JS_POINTER(), receiver, TaggedObject::HCLASS_OFFSET); - GateRef check = builder_.Equal(aotHCGate, receiverHClass); - builder_.DeoptCheck(check, frameState, DeoptType::INCONSISTENTHCLASS); - acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), Circuit::NullGate()); + auto type = acc_.GetObjectTypeAccessor(gate).GetType(); + if (tsManager_->IsClassInstanceTypeKind(type)) { + LowerTSSubtypingCompare(gate); + } else if (tsManager_->IsClassTypeKind(type) || + tsManager_->IsObjectTypeKind(type)) { + LowerSimpleHClassCompare(gate); } else { - LOG_ECMA(FATAL) << "this branch is unreachable"; + LOG_COMPILER(FATAL) << "this branch is unreachable"; UNREACHABLE(); } } -void TypeMCRLowering::LowerTSSubtypingCheck(GateRef gate) +void TypeMCRLowering::LowerSimpleHClassCompare(GateRef gate) { GateRef frameState = GetFrameState(gate); + GateRef compare = BuildCompareHClass(gate, frameState) ; + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), compare); +} + +void TypeMCRLowering::LowerTSSubtypingCompare(GateRef gate) +{ + GateRef frameState = GetFrameState(gate); + Label levelValid(&builder_); + Label exit(&builder_); + GateRef compare = BuildCompareSubTyping(gate, frameState, &levelValid, &exit); + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), compare); +} + +GateRef TypeMCRLowering::BuildCompareSubTyping(GateRef gate, GateRef frameState, Label *levelValid, Label *exit) +{ GateRef receiver = acc_.GetValueIn(gate, 0); - builder_.HeapObjectCheck(receiver, frameState); + bool isHeapObject = acc_.GetObjectTypeAccessor(gate).IsHeapObject(); + if (!isHeapObject) { + builder_.HeapObjectCheck(receiver, frameState); + } GateRef aotHCIndex = acc_.GetValueIn(gate, 1); ArgumentAccessor argAcc(circuit_); GateRef jsFunc = argAcc.GetFrameArgsIn(frameState, FrameArgIdx::FUNC); - DEFVAlUE(check, (&builder_), VariableType::BOOL(), builder_.False()); - JSTaggedValue aotHC = tsManager_->GetHClassFromCache(acc_.TryGetValue(aotHCIndex)); + JSTaggedValue aotHC = tsManager_->GetValueFromCache(acc_.TryGetValue(aotHCIndex)); ASSERT(aotHC.IsJSHClass()); int32_t level = JSHClass::Cast(aotHC.GetTaggedObject())->GetLevel(); @@ -294,69 +471,86 @@ void TypeMCRLowering::LowerTSSubtypingCheck(GateRef gate) GateRef aotHCGate = LoadFromConstPool(jsFunc, hclassIndex); if (LIKELY(static_cast(level) < SubtypingOperator::DEFAULT_SUPERS_CAPACITY)) { - check = builder_.Equal(aotHCGate, GetValueFromSupers(supers, level)); - } else { - GateRef levelGate = builder_.Int32(level); - GateRef length = GetLengthFromSupers(supers); - - Label levelValid(&builder_); - Label exit(&builder_); - builder_.Branch(builder_.Int32LessThan(levelGate, length), &levelValid, &exit); - builder_.Bind(&levelValid); - { - check = builder_.Equal(aotHCGate, GetValueFromSupers(supers, level)); - builder_.Jump(&exit); - } - builder_.Bind(&exit); + return builder_.Equal(aotHCGate, GetValueFromSupers(supers, level), "checkHClass"); } - builder_.DeoptCheck(*check, frameState, DeoptType::INCONSISTENTHCLASS); - acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), Circuit::NullGate()); -} + DEFVAlUE(check, (&builder_), VariableType::BOOL(), builder_.False()); + GateRef levelGate = builder_.Int32(level); + GateRef length = GetLengthFromSupers(supers); -void TypeMCRLowering::LowerIndexCheck(GateRef gate) -{ - Environment env(gate, circuit_, &builder_); - auto type = acc_.GetParamGateType(gate); - if (tsManager_->IsArrayTypeKind(type)) { - LowerArrayIndexCheck(gate); - } else if (tsManager_->IsFloat32ArrayType(type)) { - LowerFloat32ArrayIndexCheck(gate); - } else { - LOG_ECMA(FATAL) << "this branch is unreachable"; - UNREACHABLE(); + GateRef cmp = builder_.Int32LessThan(levelGate, length, "checkSubtyping"); + builder_.Branch(cmp, levelValid, exit, BranchWeight::DEOPT_WEIGHT, BranchWeight::ONE_WEIGHT); + builder_.Bind(levelValid); + { + check = builder_.Equal(aotHCGate, GetValueFromSupers(supers, level), "checkSubtyping"); + builder_.Jump(exit); } + builder_.Bind(exit); + return *check; } -void TypeMCRLowering::LowerArrayIndexCheck(GateRef gate) +GateRef TypeMCRLowering::BuildCompareHClass(GateRef gate, GateRef frameState) { - GateRef frameState = GetFrameState(gate); - GateRef receiver = acc_.GetValueIn(gate, 0); - GateRef hclassIndex = acc_.GetValueIn(gate, 1); - GateRef length = acc_.GetGateType(receiver).IsNJSValueType() ? receiver : - builder_.Load(VariableType::INT32(), receiver, builder_.IntPtr(JSArray::LENGTH_OFFSET)); - GateRef lengthCheck = builder_.Int32UnsignedLessThan(hclassIndex, length); - GateRef nonNegativeCheck = builder_.Int32LessThanOrEqual(builder_.Int32(0), hclassIndex); - GateRef check = builder_.BoolAnd(lengthCheck, nonNegativeCheck); - builder_.DeoptCheck(check, frameState, DeoptType::NOTARRAYIDX); + bool isHeapObject = acc_.GetObjectTypeAccessor(gate).IsHeapObject(); + if (!isHeapObject) { + builder_.HeapObjectCheck(receiver, frameState); + } - acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), hclassIndex); + GateRef aotHCIndex = acc_.GetValueIn(gate, 1); + auto hclassIndex = acc_.GetConstantValue(aotHCIndex); + ArgumentAccessor argAcc(circuit_); + GateRef jsFunc = argAcc.GetFrameArgsIn(frameState, FrameArgIdx::FUNC); + GateRef aotHCGate = LoadFromConstPool(jsFunc, hclassIndex); + GateRef receiverHClass = builder_.LoadConstOffset( + VariableType::JS_POINTER(), receiver, TaggedObject::HCLASS_OFFSET); + return builder_.Equal(aotHCGate, receiverHClass, "checkHClass"); } -void TypeMCRLowering::LowerFloat32ArrayIndexCheck(GateRef gate) +void TypeMCRLowering::LowerRangeCheckPredicate(GateRef gate) { + Environment env(gate, circuit_, &builder_); + auto deoptType = DeoptType::NOTARRAY; GateRef frameState = GetFrameState(gate); + GateRef x = acc_.GetValueIn(gate, 0); + GateRef y = acc_.GetValueIn(gate, 1); + TypedBinaryAccessor accessor = acc_.GetTypedBinaryAccessor(gate); + TypedBinOp cond = accessor.GetTypedBinOp(); + GateRef check = Circuit::NullGate(); + // check the condition + switch (cond) { + case TypedBinOp::TYPED_GREATER: + check = builder_.Int32GreaterThan(x, y); + break; + case TypedBinOp::TYPED_GREATEREQ: + check = builder_.Int32GreaterThanOrEqual(x, y); + break; + case TypedBinOp::TYPED_LESS: + check = builder_.Int32LessThan(x, y); + break; + case TypedBinOp::TYPED_LESSEQ: + check = builder_.Int32LessThanOrEqual(x, y); + break; + default: + UNREACHABLE(); + break; + } + builder_.DeoptCheck(check, frameState, deoptType); + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), Circuit::NullGate()); +} - GateRef receiver = acc_.GetValueIn(gate, 0); +void TypeMCRLowering::LowerIndexCheck(GateRef gate) +{ + Environment env(gate, circuit_, &builder_); + auto deoptType = DeoptType::NOTLEGALIDX; + + GateRef frameState = GetFrameState(gate); + GateRef length = acc_.GetValueIn(gate, 0); GateRef index = acc_.GetValueIn(gate, 1); - GateRef length = acc_.GetGateType(receiver).IsNJSValueType() ? receiver : - builder_.Load(VariableType::INT32(), receiver, builder_.IntPtr(JSTypedArray::ARRAY_LENGTH_OFFSET)); - GateRef nonNegativeCheck = builder_.Int32LessThanOrEqual(builder_.Int32(0), index); + ASSERT(acc_.GetGateType(length).IsNJSValueType()); + // UnsignedLessThan can check both lower and upper bounds GateRef lengthCheck = builder_.Int32UnsignedLessThan(index, length); - GateRef check = builder_.BoolAnd(nonNegativeCheck, lengthCheck); - builder_.DeoptCheck(check, frameState, DeoptType::NOTF32ARRAYIDX); - + builder_.DeoptCheck(lengthCheck, frameState, deoptType); acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), index); } @@ -423,7 +617,7 @@ void TypeMCRLowering::LowerPrimitiveToNumber(GateRef dst, GateRef src, GateType GateRef TypeMCRLowering::LoadFromConstPool(GateRef jsFunc, size_t index) { GateRef constPool = builder_.GetConstPool(jsFunc); - return LoadFromTaggedArray(constPool, index); + return builder_.LoadFromTaggedArray(constPool, index); } GateRef TypeMCRLowering::GetObjectFromConstPool(GateRef jsFunc, GateRef index) @@ -465,8 +659,7 @@ void TypeMCRLowering::LowerCallGetter(GateRef gate, GateRef glue) PropertyLookupResult plr(acc_.TryGetValue(propertyLookupResult)); ASSERT(plr.IsAccessor()); GateRef accessor = LoadFromVTable(receiver, plr.GetOffset()); - GateRef getter = builder_.Load(VariableType::JS_ANY(), accessor, - builder_.IntPtr(AccessorData::GETTER_OFFSET)); + GateRef getter = builder_.LoadConstOffset(VariableType::JS_ANY(), accessor, AccessorData::GETTER_OFFSET); DEFVAlUE(result, (&builder_), VariableType::JS_ANY(), builder_.UndefineConstant()); Label callGetter(&builder_); @@ -494,7 +687,7 @@ void TypeMCRLowering::LowerStoreProperty(GateRef gate) if (op == OpCode::STORE_PROPERTY) { builder_.StoreConstOffset(VariableType::JS_ANY(), receiver, plr.GetOffset(), value); } else if (op == OpCode::STORE_PROPERTY_NO_BARRIER) { - builder_.StoreConstOffset(VariableType::INT64(), receiver, plr.GetOffset(), value); + builder_.StoreConstOffset(GetVarType(plr), receiver, plr.GetOffset(), value); } else { UNREACHABLE(); } @@ -512,8 +705,7 @@ void TypeMCRLowering::LowerCallSetter(GateRef gate, GateRef glue) PropertyLookupResult plr(acc_.TryGetValue(propertyLookupResult)); ASSERT(plr.IsAccessor()); GateRef accessor = LoadFromVTable(receiver, plr.GetOffset()); - GateRef setter = builder_.Load(VariableType::JS_ANY(), - accessor, builder_.IntPtr(AccessorData::SETTER_OFFSET)); + GateRef setter = builder_.LoadConstOffset(VariableType::JS_ANY(), accessor, AccessorData::SETTER_OFFSET); Label callSetter(&builder_); Label exit(&builder_); @@ -531,22 +723,112 @@ void TypeMCRLowering::LowerLoadArrayLength(GateRef gate) { Environment env(gate, circuit_, &builder_); GateRef array = acc_.GetValueIn(gate, 0); - GateRef offset = builder_.IntPtr(JSArray::LENGTH_OFFSET); - GateRef result = builder_.Load(VariableType::INT32(), array, offset); + GateRef result = builder_.LoadConstOffset(VariableType::INT32(), array, JSArray::LENGTH_OFFSET); acc_.SetGateType(gate, GateType::NJSValue()); acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result); } +GateRef TypeMCRLowering::GetElementSize(BuiltinTypeId id) +{ + GateRef elementSize = Circuit::NullGate(); + switch (id) { + case BuiltinTypeId::INT8_ARRAY: + case BuiltinTypeId::UINT8_ARRAY: + case BuiltinTypeId::UINT8_CLAMPED_ARRAY: + elementSize = builder_.Int32(sizeof(uint8_t)); + break; + case BuiltinTypeId::INT16_ARRAY: + case BuiltinTypeId::UINT16_ARRAY: + elementSize = builder_.Int32(sizeof(uint16_t)); + break; + case BuiltinTypeId::INT32_ARRAY: + case BuiltinTypeId::UINT32_ARRAY: + case BuiltinTypeId::FLOAT32_ARRAY: + elementSize = builder_.Int32(sizeof(uint32_t)); + break; + case BuiltinTypeId::FLOAT64_ARRAY: + elementSize = builder_.Int32(sizeof(double)); + break; + default: + LOG_ECMA(FATAL) << "this branch is unreachable"; + UNREACHABLE(); + } + return elementSize; +} + +VariableType TypeMCRLowering::GetVariableType(BuiltinTypeId id) +{ + VariableType type = VariableType::JS_ANY(); + switch (id) { + case BuiltinTypeId::INT8_ARRAY: + case BuiltinTypeId::UINT8_ARRAY: + case BuiltinTypeId::UINT8_CLAMPED_ARRAY: + type = VariableType::INT8(); + break; + case BuiltinTypeId::INT16_ARRAY: + case BuiltinTypeId::UINT16_ARRAY: + type = VariableType::INT16(); + break; + case BuiltinTypeId::INT32_ARRAY: + case BuiltinTypeId::UINT32_ARRAY: + type = VariableType::INT32(); + break; + case BuiltinTypeId::FLOAT32_ARRAY: + type = VariableType::FLOAT32(); + break; + case BuiltinTypeId::FLOAT64_ARRAY: + type = VariableType::FLOAT64(); + break; + default: + LOG_ECMA(FATAL) << "this branch is unreachable"; + UNREACHABLE(); + } + return type; +} + void TypeMCRLowering::LowerLoadElement(GateRef gate) { Environment env(gate, circuit_, &builder_); auto op = acc_.GetTypedLoadOp(gate); switch (op) { - case TypedLoadOp::ARRAY_LOAD_ELEMENT: - LowerArrayLoadElement(gate); + case TypedLoadOp::ARRAY_LOAD_INT_ELEMENT: + case TypedLoadOp::ARRAY_LOAD_DOUBLE_ELEMENT: + case TypedLoadOp::ARRAY_LOAD_OBJECT_ELEMENT: + case TypedLoadOp::ARRAY_LOAD_TAGGED_ELEMENT: + LowerArrayLoadElement(gate, ArrayState::PACKED); + break; + case TypedLoadOp::ARRAY_LOAD_HOLE_TAGGED_ELEMENT: + LowerArrayLoadElement(gate, ArrayState::HOLEY); + break; + case TypedLoadOp::INT8ARRAY_LOAD_ELEMENT: + LowerTypedArrayLoadElement(gate, BuiltinTypeId::INT8_ARRAY); + break; + case TypedLoadOp::UINT8ARRAY_LOAD_ELEMENT: + LowerTypedArrayLoadElement(gate, BuiltinTypeId::UINT8_ARRAY); + break; + case TypedLoadOp::UINT8CLAMPEDARRAY_LOAD_ELEMENT: + LowerTypedArrayLoadElement(gate, BuiltinTypeId::UINT8_CLAMPED_ARRAY); + break; + case TypedLoadOp::INT16ARRAY_LOAD_ELEMENT: + LowerTypedArrayLoadElement(gate, BuiltinTypeId::INT16_ARRAY); + break; + case TypedLoadOp::UINT16ARRAY_LOAD_ELEMENT: + LowerTypedArrayLoadElement(gate, BuiltinTypeId::UINT16_ARRAY); + break; + case TypedLoadOp::INT32ARRAY_LOAD_ELEMENT: + LowerTypedArrayLoadElement(gate, BuiltinTypeId::INT32_ARRAY); + break; + case TypedLoadOp::UINT32ARRAY_LOAD_ELEMENT: + LowerTypedArrayLoadElement(gate, BuiltinTypeId::UINT32_ARRAY); break; case TypedLoadOp::FLOAT32ARRAY_LOAD_ELEMENT: - LowerFloat32ArrayLoadElement(gate); + LowerTypedArrayLoadElement(gate, BuiltinTypeId::FLOAT32_ARRAY); + break; + case TypedLoadOp::FLOAT64ARRAY_LOAD_ELEMENT: + LowerTypedArrayLoadElement(gate, BuiltinTypeId::FLOAT64_ARRAY); + break; + case TypedLoadOp::STRING_LOAD_ELEMENT: + LowerStringLoadElement(gate); break; default: LOG_ECMA(FATAL) << "this branch is unreachable"; @@ -554,54 +836,128 @@ void TypeMCRLowering::LowerLoadElement(GateRef gate) } } +void TypeMCRLowering::LowerCowArrayCheck(GateRef gate, GateRef glue) +{ + Environment env(gate, circuit_, &builder_); + GateRef receiver = acc_.GetValueIn(gate, 0); + Label notCOWArray(&builder_); + Label isCOWArray(&builder_); + builder_.Branch(builder_.IsJsCOWArray(receiver), &isCOWArray, ¬COWArray); + builder_.Bind(&isCOWArray); + { + LowerCallRuntime(glue, gate, RTSTUB_ID(CheckAndCopyArray), {receiver}, true); + builder_.Jump(¬COWArray); + } + builder_.Bind(¬COWArray); + + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), Circuit::NullGate()); +} + // for JSArray -void TypeMCRLowering::LowerArrayLoadElement(GateRef gate) +void TypeMCRLowering::LowerArrayLoadElement(GateRef gate, ArrayState arrayState) { Environment env(gate, circuit_, &builder_); GateRef receiver = acc_.GetValueIn(gate, 0); GateRef index = acc_.GetValueIn(gate, 1); GateRef element = builder_.LoadConstOffset(VariableType::JS_POINTER(), receiver, JSObject::ELEMENTS_OFFSET); GateRef result = builder_.GetValueFromTaggedArray(element, index); - result = builder_.ConvertHoleAsUndefined(result); + if (arrayState == ArrayState::HOLEY) { + result = builder_.ConvertHoleAsUndefined(result); + } acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result); } -// for Float32Array -void TypeMCRLowering::LowerFloat32ArrayLoadElement(GateRef gate) +void TypeMCRLowering::LowerTypedArrayLoadElement(GateRef gate, BuiltinTypeId id) { Environment env(gate, circuit_, &builder_); - Label isArrayBuffer(&builder_); - Label isByteArray(&builder_); - Label exit(&builder_); - DEFVAlUE(res, (&builder_), VariableType::FLOAT32(), builder_.Double(0)); GateRef receiver = acc_.GetValueIn(gate, 0); - GateRef arrbuffer = - builder_.Load(VariableType::JS_POINTER(), receiver, builder_.IntPtr(JSTypedArray::VIEWED_ARRAY_BUFFER_OFFSET)); GateRef index = acc_.GetValueIn(gate, 1); - GateRef elementSize = builder_.Int32(4); // 4: float32 occupy 4 bytes + GateRef elementSize = GetElementSize(id); GateRef offset = builder_.PtrMul(index, elementSize); - GateRef byteOffset = - builder_.Load(VariableType::INT32(), receiver, builder_.IntPtr(JSTypedArray::BYTE_OFFSET_OFFSET)); - // viewed arraybuffer could be JSArrayBuffer or ByteArray + VariableType type = GetVariableType(id); + + GateRef result = Circuit::NullGate(); + if (IsOnHeap()) { + result = BuildOnHeapTypedArrayLoadElement(receiver, offset, type); + } else { + Label isByteArray(&builder_); + Label isArrayBuffer(&builder_); + Label exit(&builder_); + result = BuildTypedArrayLoadElement(receiver, offset, type, &isByteArray, &isArrayBuffer, &exit); + } + + switch (id) { + case BuiltinTypeId::INT8_ARRAY: + result = builder_.SExtInt8ToInt32(result); + break; + case BuiltinTypeId::UINT8_ARRAY: + case BuiltinTypeId::UINT8_CLAMPED_ARRAY: + result = builder_.ZExtInt8ToInt32(result); + break; + case BuiltinTypeId::INT16_ARRAY: + result = builder_.SExtInt16ToInt32(result); + break; + case BuiltinTypeId::UINT16_ARRAY: + result = builder_.ZExtInt16ToInt32(result); + break; + case BuiltinTypeId::FLOAT32_ARRAY: + result = builder_.ExtFloat32ToDouble(result); + break; + default: + break; + } + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result); +} + +GateRef TypeMCRLowering::BuildOnHeapTypedArrayLoadElement(GateRef receiver, GateRef offset, VariableType type) +{ + GateRef arrbuffer = + builder_.LoadConstOffset(VariableType::JS_POINTER(), receiver, JSTypedArray::VIEWED_ARRAY_BUFFER_OFFSET); + GateRef data = builder_.PtrAdd(arrbuffer, builder_.IntPtr(ByteArray::DATA_OFFSET)); + GateRef result = builder_.Load(type, data, offset); + return result; +} + +GateRef TypeMCRLowering::BuildTypedArrayLoadElement(GateRef receiver, GateRef offset, VariableType type, + Label *isByteArray, Label *isArrayBuffer, Label *exit) +{ + GateRef byteArrayOrArraybuffer = + builder_.LoadConstOffset(VariableType::JS_POINTER(), receiver, JSTypedArray::VIEWED_ARRAY_BUFFER_OFFSET); + DEFVAlUE(data, (&builder_), VariableType::JS_ANY(), builder_.Undefined()); + DEFVAlUE(result, (&builder_), type, builder_.Double(0)); + GateRef isOnHeap = builder_.Load(VariableType::BOOL(), receiver, builder_.IntPtr(JSTypedArray::ON_HEAP_OFFSET)); - builder_.Branch(isOnHeap, &isByteArray, &isArrayBuffer); - builder_.Bind(&isByteArray); + builder_.Branch(isOnHeap, isByteArray, isArrayBuffer); + builder_.Bind(isByteArray); { - GateRef data = builder_.PtrAdd(arrbuffer, builder_.IntPtr(ByteArray::DATA_OFFSET)); - res = builder_.Load(VariableType::FLOAT32(), data, builder_.PtrAdd(offset, byteOffset)); - builder_.Jump(&exit); + data = builder_.PtrAdd(byteArrayOrArraybuffer, builder_.IntPtr(ByteArray::DATA_OFFSET)); + result = builder_.Load(type, *data, offset); + builder_.Jump(exit); } - builder_.Bind(&isArrayBuffer); + builder_.Bind(isArrayBuffer); { - GateRef data = builder_.Load(VariableType::JS_POINTER(), arrbuffer, - builder_.IntPtr(JSArrayBuffer::DATA_OFFSET)); - GateRef block = builder_.Load(VariableType::JS_ANY(), data, builder_.IntPtr(JSNativePointer::POINTER_OFFSET)); - res = builder_.Load(VariableType::FLOAT32(), block, builder_.PtrAdd(offset, byteOffset)); - builder_.Jump(&exit); + data = builder_.Load(VariableType::JS_POINTER(), byteArrayOrArraybuffer, + builder_.IntPtr(JSArrayBuffer::DATA_OFFSET)); + GateRef block = builder_.Load(VariableType::JS_ANY(), *data, builder_.IntPtr(JSNativePointer::POINTER_OFFSET)); + GateRef byteOffset = + builder_.Load(VariableType::INT32(), receiver, builder_.IntPtr(JSTypedArray::BYTE_OFFSET_OFFSET)); + result = builder_.Load(type, block, builder_.PtrAdd(offset, byteOffset)); + builder_.Jump(exit); } + builder_.Bind(exit); - builder_.Bind(&exit); - GateRef result = builder_.Float32ToTaggedDoublePtr(*res); + return *result; +} + +void TypeMCRLowering::LowerStringLoadElement(GateRef gate) +{ + Environment env(gate, circuit_, &builder_); + GateRef glue = acc_.GetGlueFromArgList(); + GateRef receiver = acc_.GetValueIn(gate, 0); + GateRef index = acc_.GetValueIn(gate, 1); + + GateRef result = builder_.CallStub(glue, gate, CommonStubCSigns::GetSingleCharCodeByIndex, + { glue, receiver, index }); acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result); } @@ -613,8 +969,32 @@ void TypeMCRLowering::LowerStoreElement(GateRef gate, GateRef glue) case TypedStoreOp::ARRAY_STORE_ELEMENT: LowerArrayStoreElement(gate, glue); break; + case TypedStoreOp::INT8ARRAY_STORE_ELEMENT: + LowerTypedArrayStoreElement(gate, BuiltinTypeId::INT8_ARRAY); + break; + case TypedStoreOp::UINT8ARRAY_STORE_ELEMENT: + LowerTypedArrayStoreElement(gate, BuiltinTypeId::UINT8_ARRAY); + break; + case TypedStoreOp::UINT8CLAMPEDARRAY_STORE_ELEMENT: + LowerUInt8ClampedArrayStoreElement(gate); + break; + case TypedStoreOp::INT16ARRAY_STORE_ELEMENT: + LowerTypedArrayStoreElement(gate, BuiltinTypeId::INT16_ARRAY); + break; + case TypedStoreOp::UINT16ARRAY_STORE_ELEMENT: + LowerTypedArrayStoreElement(gate, BuiltinTypeId::UINT16_ARRAY); + break; + case TypedStoreOp::INT32ARRAY_STORE_ELEMENT: + LowerTypedArrayStoreElement(gate, BuiltinTypeId::INT32_ARRAY); + break; + case TypedStoreOp::UINT32ARRAY_STORE_ELEMENT: + LowerTypedArrayStoreElement(gate, BuiltinTypeId::UINT32_ARRAY); + break; case TypedStoreOp::FLOAT32ARRAY_STORE_ELEMENT: - LowerFloat32ArrayStoreElement(gate, glue); + LowerTypedArrayStoreElement(gate, BuiltinTypeId::FLOAT32_ARRAY); + break; + case TypedStoreOp::FLOAT64ARRAY_STORE_ELEMENT: + LowerTypedArrayStoreElement(gate, BuiltinTypeId::FLOAT64_ARRAY); break; default: LOG_ECMA(FATAL) << "this branch is unreachable"; @@ -630,84 +1010,129 @@ void TypeMCRLowering::LowerArrayStoreElement(GateRef gate, GateRef glue) GateRef index = acc_.GetValueIn(gate, 1); // 1: index GateRef value = acc_.GetValueIn(gate, 2); // 2: value - Label storeWithCOWArray(&builder_); - Label storeDirectly(&builder_); - Label exit(&builder_); - builder_.Branch(builder_.IsJsCOWArray(receiver), &storeWithCOWArray, &storeDirectly); - builder_.Bind(&storeWithCOWArray); - { - GateRef newElement = LowerCallRuntime(glue, gate, RTSTUB_ID(CheckAndCopyArray), {receiver}, true); - builder_.SetValueToTaggedArray(VariableType::JS_ANY(), glue, newElement, index, value); - builder_.Jump(&exit); - } - builder_.Bind(&storeDirectly); - { - GateRef element = builder_.LoadConstOffset(VariableType::JS_POINTER(), receiver, JSObject::ELEMENTS_OFFSET); - builder_.SetValueToTaggedArray(VariableType::JS_ANY(), glue, element, index, value); - builder_.Jump(&exit); - } - builder_.Bind(&exit); + GateRef element = builder_.LoadConstOffset(VariableType::JS_POINTER(), receiver, JSObject::ELEMENTS_OFFSET); + builder_.SetValueToTaggedArray(VariableType::JS_ANY(), glue, element, index, value); + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), Circuit::NullGate()); } -// for Float32Array -void TypeMCRLowering::LowerFloat32ArrayStoreElement(GateRef gate, GateRef glue) +// for JSTypedArray +void TypeMCRLowering::LowerTypedArrayStoreElement(GateRef gate, BuiltinTypeId id) { Environment env(gate, circuit_, &builder_); - Label isArrayBuffer(&builder_); - Label isByteArray(&builder_); - Label afterGetValue(&builder_); - Label exit(&builder_); GateRef receiver = acc_.GetValueIn(gate, 0); GateRef index = acc_.GetValueIn(gate, 1); - GateRef elementSize = builder_.Int32(4); // 4: float32 occupy 4 bytes + GateRef value = acc_.GetValueIn(gate, 2); + + GateRef elementSize = GetElementSize(id); GateRef offset = builder_.PtrMul(index, elementSize); - GateRef byteOffset = - builder_.Load(VariableType::INT32(), receiver, builder_.IntPtr(JSTypedArray::BYTE_OFFSET_OFFSET)); + switch (id) { + case BuiltinTypeId::INT8_ARRAY: + case BuiltinTypeId::UINT8_ARRAY: + value = builder_.TruncInt32ToInt8(value); + break; + case BuiltinTypeId::INT16_ARRAY: + case BuiltinTypeId::UINT16_ARRAY: + value = builder_.TruncInt32ToInt16(value); + break; + case BuiltinTypeId::FLOAT32_ARRAY: + value = builder_.TruncDoubleToFloat32(value); + break; + default: + break; + } - GateRef value = acc_.GetValueIn(gate, 2); - GateType valueType = acc_.GetGateType(value); - DEFVAlUE(storeValue, (&builder_), VariableType::FLOAT32(), builder_.Double(0)); - if (valueType.IsIntType()) { - storeValue = builder_.TaggedIntPtrToFloat32(value); - } else if (valueType.IsDoubleType()) { - storeValue = builder_.TaggedDoublePtrToFloat32(value); + if (IsOnHeap()) { + BuildOnHeapTypedArrayStoreElement(receiver, offset, value); } else { - Label valueIsInt(&builder_); - Label valueIsDouble(&builder_); - builder_.Branch(builder_.TaggedIsInt(value), &valueIsInt, &valueIsDouble); - builder_.Bind(&valueIsInt); - { - storeValue = builder_.TaggedIntPtrToFloat32(value); - builder_.Jump(&afterGetValue); - } - builder_.Bind(&valueIsDouble); - { - storeValue = builder_.TaggedDoublePtrToFloat32(value); - builder_.Jump(&afterGetValue); - } - builder_.Bind(&afterGetValue); + Label isByteArray(&builder_); + Label isArrayBuffer(&builder_); + Label exit(&builder_); + BuildTypedArrayStoreElement(receiver, offset, value, &isByteArray, &isArrayBuffer, &exit); } - GateRef arrbuffer = - builder_.Load(VariableType::JS_POINTER(), receiver, builder_.IntPtr(JSTypedArray::VIEWED_ARRAY_BUFFER_OFFSET)); - // viewed arraybuffer could be JSArrayBuffer or ByteArray + + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), Circuit::NullGate()); +} + +void TypeMCRLowering::BuildOnHeapTypedArrayStoreElement(GateRef receiver, GateRef offset, GateRef value) +{ + GateRef arrbuffer = builder_.LoadConstOffset(VariableType::JS_POINTER(), receiver, + JSTypedArray::VIEWED_ARRAY_BUFFER_OFFSET); + GateRef data = builder_.PtrAdd(arrbuffer, builder_.IntPtr(ByteArray::DATA_OFFSET)); + + builder_.StoreMemory(MemoryType::ELEMENT_TYPE, VariableType::VOID(), data, offset, value); +} + +void TypeMCRLowering::BuildTypedArrayStoreElement(GateRef receiver, GateRef offset, GateRef value, + Label *isByteArray, Label *isArrayBuffer, Label *exit) +{ + GateRef byteArrayOrArraybuffer = builder_.LoadConstOffset(VariableType::JS_POINTER(), receiver, + JSTypedArray::VIEWED_ARRAY_BUFFER_OFFSET); GateRef isOnHeap = builder_.Load(VariableType::BOOL(), receiver, builder_.IntPtr(JSTypedArray::ON_HEAP_OFFSET)); - builder_.Branch(isOnHeap, &isByteArray, &isArrayBuffer); - builder_.Bind(&isByteArray); + DEFVAlUE(data, (&builder_), VariableType::JS_ANY(), builder_.Undefined()); + builder_.Branch(isOnHeap, isByteArray, isArrayBuffer); + builder_.Bind(isByteArray); { - GateRef data = builder_.PtrAdd(arrbuffer, builder_.IntPtr(ByteArray::DATA_OFFSET)); - builder_.Store(VariableType::VOID(), glue, data, builder_.PtrAdd(offset, byteOffset), *storeValue); - builder_.Jump(&exit); + data = builder_.PtrAdd(byteArrayOrArraybuffer, builder_.IntPtr(ByteArray::DATA_OFFSET)); + builder_.StoreMemory(MemoryType::ELEMENT_TYPE, VariableType::VOID(), *data, offset, value); + builder_.Jump(exit); } - builder_.Bind(&isArrayBuffer); + builder_.Bind(isArrayBuffer); { - GateRef data = builder_.Load(VariableType::JS_POINTER(), arrbuffer, - builder_.IntPtr(JSArrayBuffer::DATA_OFFSET)); - GateRef block = builder_.Load(VariableType::JS_ANY(), data, builder_.IntPtr(JSNativePointer::POINTER_OFFSET)); - builder_.Store(VariableType::VOID(), glue, block, builder_.PtrAdd(offset, byteOffset), *storeValue); + data = builder_.Load(VariableType::JS_POINTER(), byteArrayOrArraybuffer, + builder_.IntPtr(JSArrayBuffer::DATA_OFFSET)); + GateRef block = builder_.Load(VariableType::JS_ANY(), *data, builder_.IntPtr(JSNativePointer::POINTER_OFFSET)); + GateRef byteOffset = + builder_.Load(VariableType::INT32(), receiver, builder_.IntPtr(JSTypedArray::BYTE_OFFSET_OFFSET)); + builder_.StoreMemory(MemoryType::ELEMENT_TYPE, VariableType::VOID(), block, + builder_.PtrAdd(offset, byteOffset), value); + builder_.Jump(exit); + } + builder_.Bind(exit); +} + +// for UInt8ClampedArray +void TypeMCRLowering::LowerUInt8ClampedArrayStoreElement(GateRef gate) +{ + Environment env(gate, circuit_, &builder_); + + GateRef receiver = acc_.GetValueIn(gate, 0); + GateRef index = acc_.GetValueIn(gate, 1); + GateRef elementSize = builder_.Int32(sizeof(uint8_t)); + GateRef offset = builder_.PtrMul(index, elementSize); + GateRef value = acc_.GetValueIn(gate, 2); + + DEFVAlUE(result, (&builder_), VariableType::INT32(), value); + GateRef topValue = builder_.Int32(static_cast(UINT8_MAX)); + GateRef bottomValue = builder_.Int32(static_cast(0)); + Label isOverFlow(&builder_); + Label notOverFlow(&builder_); + Label exit(&builder_); + builder_.Branch(builder_.Int32GreaterThan(value, topValue), &isOverFlow, ¬OverFlow); + builder_.Bind(&isOverFlow); + { + result = topValue; builder_.Jump(&exit); } + builder_.Bind(¬OverFlow); + { + Label isUnderSpill(&builder_); + builder_.Branch(builder_.Int32LessThan(value, bottomValue), &isUnderSpill, &exit); + builder_.Bind(&isUnderSpill); + { + result = bottomValue; + builder_.Jump(&exit); + } + } builder_.Bind(&exit); + value = builder_.TruncInt32ToInt8(*result); + + GateRef arrbuffer = builder_.LoadConstOffset(VariableType::JS_POINTER(), receiver, + JSTypedArray::VIEWED_ARRAY_BUFFER_OFFSET); + + GateRef data = builder_.PtrAdd(arrbuffer, builder_.IntPtr(ByteArray::DATA_OFFSET)); + + builder_.StoreMemory(MemoryType::ELEMENT_TYPE, VariableType::VOID(), data, offset, value); acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), Circuit::NullGate()); } @@ -810,7 +1235,7 @@ void TypeMCRLowering::LowerJSCallThisTargetTypeCheck(GateRef gate) GateRef frameState = GetFrameState(gate); auto func = acc_.GetValueIn(gate, 0); GateRef isObj = builder_.TaggedIsHeapObject(func); - GateRef isOptimized = builder_.IsOptimized(func); + GateRef isOptimized = builder_.IsOptimizedAndNotFastCall(func); GateRef check = builder_.BoolAnd(isObj, isOptimized); builder_.DeoptCheck(check, frameState, DeoptType::NOTJSCALLTGT); acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), Circuit::NullGate()); @@ -820,6 +1245,26 @@ void TypeMCRLowering::LowerJSCallThisTargetTypeCheck(GateRef gate) } } +void TypeMCRLowering::LowerJSNoGCCallThisTargetTypeCheck(GateRef gate) +{ + Environment env(gate, circuit_, &builder_); + auto type = acc_.GetParamGateType(gate); + if (tsManager_->IsFunctionTypeKind(type)) { + GateRef frameState = GetFrameState(gate); + auto func = acc_.GetValueIn(gate, 0); + GateRef isObj = builder_.TaggedIsHeapObject(func); + GateRef isOptimized = builder_.IsOptimizedAndNotFastCall(func); + GateRef methodId = builder_.GetMethodId(func); + GateRef checkOptimized = builder_.BoolAnd(isObj, isOptimized); + GateRef check = builder_.BoolAnd(checkOptimized, builder_.Equal(methodId, acc_.GetValueIn(gate, 1))); + builder_.DeoptCheck(check, frameState, DeoptType::NOTJSCALLTGT); + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), Circuit::NullGate()); + } else { + LOG_COMPILER(FATAL) << "this branch is unreachable"; + UNREACHABLE(); + } +} + void TypeMCRLowering::LowerJSFastCallThisTargetTypeCheck(GateRef gate) { Environment env(gate, circuit_, &builder_); @@ -838,6 +1283,26 @@ void TypeMCRLowering::LowerJSFastCallThisTargetTypeCheck(GateRef gate) } } +void TypeMCRLowering::LowerJSNoGCFastCallThisTargetTypeCheck(GateRef gate) +{ + Environment env(gate, circuit_, &builder_); + auto type = acc_.GetParamGateType(gate); + if (tsManager_->IsFunctionTypeKind(type)) { + GateRef frameState = GetFrameState(gate); + auto func = acc_.GetValueIn(gate, 0); + GateRef isObj = builder_.TaggedIsHeapObject(func); + GateRef canFastCall = builder_.CanFastCall(func); + GateRef methodId = builder_.GetMethodId(func); + GateRef checkOptimized = builder_.BoolAnd(isObj, canFastCall); + GateRef check = builder_.BoolAnd(checkOptimized, builder_.Equal(methodId, acc_.GetValueIn(gate, 1))); + builder_.DeoptCheck(check, frameState, DeoptType::NOTJSFASTCALLTGT); + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), Circuit::NullGate()); + } else { + LOG_COMPILER(FATAL) << "this branch is unreachable"; + UNREACHABLE(); + } +} + void TypeMCRLowering::LowerCallTargetCheck(GateRef gate) { Environment env(gate, circuit_, &builder_); @@ -845,8 +1310,13 @@ void TypeMCRLowering::LowerCallTargetCheck(GateRef gate) BuiltinLowering lowering(circuit_); GateRef funcheck = lowering.LowerCallTargetCheck(&env, gate); - GateRef check = lowering.CheckPara(gate, funcheck); - builder_.DeoptCheck(check, frameState, DeoptType::NOTCALLTGT); + GateRef constId = acc_.GetValueIn(gate, 1); // 1: stub id + if (acc_.GetConstantValue(constId) != static_cast(BuiltinsStubCSigns::ID::STRINGIFY)) { + GateRef check = lowering.CheckPara(gate, funcheck); + builder_.DeoptCheck(check, frameState, DeoptType::NOTCALLTGT); + } else { + builder_.DeoptCheck(funcheck, frameState, DeoptType::NOTCALLTGT); + } acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), Circuit::NullGate()); } @@ -884,8 +1354,8 @@ void TypeMCRLowering::LowerTypedNewAllocateThis(GateRef gate, GateRef glue) { // add typecheck to detect protoOrHclass is equal with ihclass, // if pass typecheck: 1.no need to check whether hclass is valid 2.no need to check return result - GateRef protoOrHclass = builder_.Load(VariableType::JS_ANY(), ctor, - builder_.IntPtr(JSFunction::PROTO_OR_DYNCLASS_OFFSET)); + GateRef protoOrHclass = builder_.LoadConstOffset(VariableType::JS_ANY(), ctor, + JSFunction::PROTO_OR_DYNCLASS_OFFSET); GateRef ihclassIndex = acc_.GetValueIn(gate, 1); GateRef ihclass = GetObjectFromConstPool(jsFunc, ihclassIndex); GateRef check = builder_.Equal(protoOrHclass, ihclass); @@ -912,8 +1382,8 @@ void TypeMCRLowering::LowerTypedSuperAllocateThis(GateRef gate, GateRef glue) builder_.Branch(isBase, &allocate, &exit); builder_.Bind(&allocate); { - GateRef protoOrHclass = builder_.Load(VariableType::JS_ANY(), newTarget, - builder_.IntPtr(JSFunction::PROTO_OR_DYNCLASS_OFFSET)); + GateRef protoOrHclass = builder_.LoadConstOffset(VariableType::JS_ANY(), newTarget, + JSFunction::PROTO_OR_DYNCLASS_OFFSET); GateRef check = builder_.IsJSHClass(protoOrHclass); GateRef frameState = GetFrameState(gate); builder_.DeoptCheck(check, frameState, DeoptType::NOTNEWOBJ); @@ -930,60 +1400,10 @@ void TypeMCRLowering::LowerGetSuperConstructor(GateRef gate) Environment env(gate, circuit_, &builder_); GateRef ctor = acc_.GetValueIn(gate, 0); GateRef hclass = builder_.LoadHClass(ctor); - GateRef protoOffset = builder_.IntPtr(JSHClass::PROTOTYPE_OFFSET); - GateRef superCtor = builder_.Load(VariableType::JS_ANY(), hclass, protoOffset); + GateRef superCtor = builder_.LoadConstOffset(VariableType::JS_ANY(), hclass, JSHClass::PROTOTYPE_OFFSET); acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), superCtor); } -void TypeMCRLowering::LowerCreateArray(GateRef gate) -{ - Environment env(gate, circuit_, &builder_); - if (acc_.IsEmptyArray(gate)) { - LowerCreateEmptyArray(gate); - } else { - LOG_ECMA(FATAL) << "this branch is unreachable"; - UNREACHABLE(); - } -} - -void TypeMCRLowering::LowerCreateEmptyArray(GateRef gate) -{ - JSHandle arrayFunc(tsManager_->GetEcmaVM()->GetGlobalEnv()->GetArrayFunction()); - JSTaggedValue protoOrHClass = arrayFunc->GetProtoOrHClass(); - JSHClass *arrayHC = JSHClass::Cast(protoOrHClass.GetTaggedObject()); - size_t arraySize = arrayHC->GetObjectSize(); - size_t lengthAccessorOffset = arrayHC->GetInlinedPropertiesOffset(JSArray::LENGTH_INLINE_PROPERTY_INDEX); - - GateRef obj = acc_.GetValueIn(gate); - GateRef globalEnv = builder_.GetGlobalEnv(); - GateRef accessor = builder_.GetGlobalConstantValue(ConstantIndex::ARRAY_LENGTH_ACCESSOR); - GateRef hclass = builder_.GetGlobalEnvObjHClass(globalEnv, GlobalEnv::ARRAY_FUNCTION_INDEX); - GateRef size = builder_.IntPtr(arrayHC->GetObjectSize()); - - builder_.StartAllocate(); - GateRef array = builder_.HeapAlloc(size, GateType::TaggedValue(), RegionSpaceFlag::IN_YOUNG_SPACE); - // initialization - for (size_t offset = JSArray::SIZE; offset < arraySize; offset += JSTaggedValue::TaggedTypeSize()) { - builder_.StoreConstOffset(VariableType::INT64(), array, offset, builder_.Undefined()); - } - builder_.StoreConstOffset(VariableType::INT64(), array, ECMAObject::HASH_OFFSET, - builder_.Int64(JSTaggedValue(0).GetRawData())); - builder_.StoreConstOffset(VariableType::JS_POINTER(), array, 0, hclass); - builder_.StoreConstOffset(VariableType::JS_POINTER(), array, JSObject::PROPERTIES_OFFSET, obj); - builder_.StoreConstOffset(VariableType::JS_POINTER(), array, JSObject::ELEMENTS_OFFSET, obj); - builder_.StoreConstOffset(VariableType::JS_ANY(), array, JSArray::LENGTH_OFFSET, - builder_.Int32ToTaggedInt(builder_.Int32(0))); - builder_.StoreConstOffset(VariableType::JS_POINTER(), array, lengthAccessorOffset, accessor); - builder_.FinishAllocate(); - acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), array); -} - -GateRef TypeMCRLowering::LoadFromTaggedArray(GateRef array, size_t index) -{ - auto dataOffset = TaggedArray::DATA_OFFSET + index * JSTaggedValue::TaggedTypeSize(); - return builder_.LoadConstOffset(VariableType::JS_ANY(), array, dataOffset); -} - GateRef TypeMCRLowering::LoadFromVTable(GateRef receiver, size_t index) { GateRef hclass = builder_.LoadConstOffset( @@ -991,11 +1411,22 @@ GateRef TypeMCRLowering::LoadFromVTable(GateRef receiver, size_t index) GateRef vtable = builder_.LoadConstOffset(VariableType::JS_ANY(), hclass, JSHClass::VTABLE_OFFSET); - GateRef itemOwner = LoadFromTaggedArray(vtable, VTable::TupleItem::OWNER + index); - GateRef itemOffset = LoadFromTaggedArray(vtable, VTable::TupleItem::OFFSET + index); + GateRef itemOwner = builder_.LoadFromTaggedArray(vtable, VTable::TupleItem::OWNER + index); + GateRef itemOffset = builder_.LoadFromTaggedArray(vtable, VTable::TupleItem::OFFSET + index); return builder_.Load(VariableType::JS_ANY(), itemOwner, builder_.TaggedGetInt(itemOffset)); } +VariableType TypeMCRLowering::GetVarType(PropertyLookupResult plr) +{ + if (plr.GetRepresentation() == Representation::DOUBLE) { + return kungfu::VariableType::FLOAT64(); + } else if (plr.GetRepresentation() == Representation::INT) { + return kungfu::VariableType::INT32(); + } else { + return kungfu::VariableType::INT64(); + } +} + GateRef TypeMCRLowering::LoadSupers(GateRef hclass) { return builder_.LoadConstOffset(VariableType::JS_ANY(), hclass, JSHClass::SUPERS_OFFSET); @@ -1008,12 +1439,12 @@ GateRef TypeMCRLowering::GetLengthFromSupers(GateRef supers) GateRef TypeMCRLowering::GetValueFromSupers(GateRef supers, size_t index) { - GateRef val = LoadFromTaggedArray(supers, index); + GateRef val = builder_.LoadFromTaggedArray(supers, index); return builder_.LoadObjectFromWeakRef(val); } -GateRef TypeMCRLowering::CallAccessor(GateRef glue, GateRef gate, GateRef function, GateRef receiver, AccessorMode mode, - GateRef value) +GateRef TypeMCRLowering::CallAccessor(GateRef glue, GateRef gate, GateRef function, GateRef receiver, + AccessorMode mode, GateRef value) { const CallSignature *cs = RuntimeStubCSigns::Get(RTSTUB_ID(JSCall)); GateRef target = builder_.IntPtr(RTSTUB_ID(JSCall)); @@ -1041,4 +1472,176 @@ void TypeMCRLowering::ReplaceHirWithPendingException(GateRef hirGate, GateRef gl StateDepend exception(ifTrue, eDepend); acc_.ReplaceHirWithIfBranch(hirGate, success, exception, value); } + +void TypeMCRLowering::LowerLoadGetter(GateRef gate) +{ + Environment env(gate, circuit_, &builder_); + ASSERT(acc_.GetNumValueIn(gate) == 2); // 2: receiver, plr + GateRef receiver = acc_.GetValueIn(gate, 0); + GateRef propertyLookupResult = acc_.GetValueIn(gate, 1); + + PropertyLookupResult plr(acc_.TryGetValue(propertyLookupResult)); + ASSERT(plr.IsAccessor()); + GateRef accessor = LoadFromVTable(receiver, plr.GetOffset()); + GateRef getter = builder_.Load(VariableType::JS_ANY(), accessor, + builder_.IntPtr(AccessorData::GETTER_OFFSET)); + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), getter); +} + +void TypeMCRLowering::LowerLoadSetter(GateRef gate) +{ + Environment env(gate, circuit_, &builder_); + ASSERT(acc_.GetNumValueIn(gate) == 2); // 2: receiver, plr + GateRef receiver = acc_.GetValueIn(gate, 0); + GateRef propertyLookupResult = acc_.GetValueIn(gate, 1); + + PropertyLookupResult plr(acc_.TryGetValue(propertyLookupResult)); + ASSERT(plr.IsAccessor()); + GateRef accessor = LoadFromVTable(receiver, plr.GetOffset()); + GateRef setter = builder_.Load(VariableType::JS_ANY(), + accessor, builder_.IntPtr(AccessorData::SETTER_OFFSET)); + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), setter); +} + +// subtyping check and hclss check +void TypeMCRLowering::LowerInlineAccessorCheck(GateRef gate) +{ + Environment env(gate, circuit_, &builder_); + GateRef receiver = acc_.GetValueIn(gate, 0); + GateRef frameState = acc_.GetFrameState(gate); + builder_.HeapObjectCheck(receiver, frameState); + + GateRef aotHCIndex = acc_.GetValueIn(gate, 1); + ArgumentAccessor argAcc(circuit_); + GateRef jsFunc = argAcc.GetFrameArgsIn(frameState, FrameArgIdx::FUNC); + JSTaggedValue aotHC = tsManager_->GetValueFromCache(acc_.TryGetValue(aotHCIndex)); + ASSERT(aotHC.IsJSHClass()); + + int32_t level = JSHClass::Cast(aotHC.GetTaggedObject())->GetLevel(); + ASSERT(level >= 0); + + GateRef receiverHClass = builder_.LoadConstOffset( + VariableType::JS_POINTER(), receiver, TaggedObject::HCLASS_OFFSET); + GateRef supers = LoadSupers(receiverHClass); + + auto hclassIndex = acc_.GetConstantValue(aotHCIndex); + GateRef aotHCGate = LoadFromConstPool(jsFunc, hclassIndex); + GateRef hclassCompare = builder_.Equal(aotHCGate, receiverHClass); + if (LIKELY(static_cast(level) < SubtypingOperator::DEFAULT_SUPERS_CAPACITY)) { + GateRef subtypingCompare = builder_.Equal(aotHCGate, GetValueFromSupers(supers, level)); + GateRef compare = builder_.BoolAnd(hclassCompare, subtypingCompare); + builder_.DeoptCheck(compare, frameState, DeoptType::INCONSISTENTHCLASS); + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), Circuit::NullGate()); + return; + } + + Label levelValid(&builder_); + Label exit(&builder_); + DEFVAlUE(check, (&builder_), VariableType::BOOL(), builder_.False()); + GateRef levelGate = builder_.Int32(level); + GateRef length = GetLengthFromSupers(supers); + + builder_.Branch(builder_.Int32LessThan(levelGate, length), &levelValid, &exit); + builder_.Bind(&levelValid); + { + check = builder_.Equal(aotHCGate, GetValueFromSupers(supers, level)); + builder_.Jump(&exit); + } + builder_.Bind(&exit); + + GateRef compare = builder_.BoolAnd(hclassCompare, *check); + builder_.DeoptCheck(compare, frameState, DeoptType::INCONSISTENTHCLASS); + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), Circuit::NullGate()); +} + +void TypeMCRLowering::LowerStringEqual(GateRef gate, GateRef glue) +{ + Environment env(gate, circuit_, &builder_); + GateRef left = acc_.GetValueIn(gate, 0); + GateRef right = acc_.GetValueIn(gate, 1); + GateRef leftLength = GetLengthFromString(left); + GateRef rightLength = GetLengthFromString(right); + + DEFVAlUE(result, (&builder_), VariableType::BOOL(), builder_.False()); + Label lenEqual(&builder_); + Label exit(&builder_); + builder_.Branch(builder_.Equal(leftLength, rightLength), &lenEqual, &exit); + builder_.Bind(&lenEqual); + { + result = builder_.CallStub(glue, gate, CommonStubCSigns::FastStringEqual, { glue, left, right }); + builder_.Jump(&exit); + } + builder_.Bind(&exit); + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), *result); +} + +void TypeMCRLowering::LowerTypeOfCheck(GateRef gate) +{ + Environment env(gate, circuit_, &builder_); + GateRef frameState = GetFrameState(gate); + GateRef value = acc_.GetValueIn(gate, 0); + GateType type = acc_.GetParamGateType(gate); + GateRef check = Circuit::NullGate(); + if (type.IsNumberType()) { + check = builder_.TaggedIsNumber(value); + } else if (type.IsBooleanType()) { + check = builder_.TaggedIsBoolean(value); + } else if (type.IsNullType()) { + check = builder_.TaggedIsNull(value); + } else if (type.IsUndefinedType()) { + check = builder_.TaggedIsUndefined(value); + } else if (type.IsStringType()) { + check = builder_.BoolAnd(builder_.TaggedIsHeapObject(value), builder_.TaggedIsString(value)); + } else if (type.IsBigIntType()) { + check = builder_.BoolAnd(builder_.TaggedIsHeapObject(value), builder_.IsJsType(value, JSType::BIGINT)); + } else if (type.IsSymbolType()) { + check = builder_.BoolAnd(builder_.TaggedIsHeapObject(value), builder_.IsJsType(value, JSType::SYMBOL)); + } else if (tsManager_->IsFunctionTypeKind(type) || tsManager_->IsClassTypeKind(type)) { + check = builder_.BoolAnd(builder_.TaggedIsHeapObject(value), builder_.IsCallable(value)); + } else if (tsManager_->IsObjectTypeKind(type) || tsManager_->IsClassInstanceTypeKind(type)) { + check = builder_.BoolAnd(builder_.TaggedIsHeapObject(value), builder_.IsJsType(value, JSType::JS_OBJECT)); + } else if (tsManager_->IsArrayTypeKind(type)) { + check = builder_.BoolAnd(builder_.TaggedIsHeapObject(value), builder_.IsJsType(value, JSType::JS_ARRAY)); + } else { + UNREACHABLE(); + } + + builder_.DeoptCheck(check, frameState, DeoptType::INCONSISTENTTYPE); + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), Circuit::NullGate()); +} + +void TypeMCRLowering::LowerTypeOf(GateRef gate, GateRef glue) +{ + Environment env(gate, circuit_, &builder_); + GateType type = acc_.GetParamGateType(gate); + GateRef gConstAddr = builder_.Load(VariableType::JS_POINTER(), glue, + builder_.IntPtr(JSThread::GlueData::GetGlobalConstOffset(builder_.GetCompilationConfig()->Is32Bit()))); + ConstantIndex index; + if (type.IsNumberType()) { + index = ConstantIndex::NUMBER_STRING_INDEX; + } else if (type.IsBooleanType()) { + index = ConstantIndex::BOOLEAN_STRING_INDEX; + } else if (type.IsNullType()) { + index = ConstantIndex::OBJECT_STRING_INDEX; + } else if (type.IsUndefinedType()) { + index = ConstantIndex::UNDEFINED_STRING_INDEX; + } else if (type.IsStringType()) { + index = ConstantIndex::STRING_STRING_INDEX; + } else if (type.IsBigIntType()) { + index = ConstantIndex::BIGINT_STRING_INDEX; + } else if (type.IsSymbolType()) { + index = ConstantIndex::SYMBOL_STRING_INDEX; + } else if (tsManager_->IsFunctionTypeKind(type) || tsManager_->IsClassTypeKind(type)) { + index = ConstantIndex::FUNCTION_STRING_INDEX; + } else if (tsManager_->IsObjectTypeKind(type) || tsManager_->IsClassInstanceTypeKind(type)) { + index = ConstantIndex::OBJECT_STRING_INDEX; + } else if (tsManager_->IsArrayTypeKind(type)) { + index = ConstantIndex::OBJECT_STRING_INDEX; + } else { + UNREACHABLE(); + } + + GateRef result = builder_.Load(VariableType::JS_POINTER(), gConstAddr, builder_.GetGlobalConstantString(index)); + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result); +} } // namespace panda::ecmascript::kungfu diff --git a/ecmascript/compiler/type_mcr_lowering.h b/ecmascript/compiler/type_mcr_lowering.h index 562159d8b0d0e2feece26631d0533b8bfa77be17..c2567ea6dc05a83ead01d22b14f503b66c51fe05 100644 --- a/ecmascript/compiler/type_mcr_lowering.h +++ b/ecmascript/compiler/type_mcr_lowering.h @@ -19,6 +19,7 @@ #include "ecmascript/compiler/argument_accessor.h" #include "ecmascript/compiler/bytecode_circuit_builder.h" #include "ecmascript/compiler/circuit_builder-inl.h" +#include "ecmascript/compiler/combined_pass_visitor.h" namespace panda::ecmascript::kungfu { // TypeMCRLowering Process @@ -95,29 +96,22 @@ namespace panda::ecmascript::kungfu { // | JS_BYTECODE | // +------------------------+ -class TypeMCRLowering { +class TypeMCRLowering : public PassVisitor { public: - TypeMCRLowering(Circuit *circuit, CompilationConfig *cmpCfg, TSManager *tsManager, - bool enableLog, const std::string& name) - : circuit_(circuit), acc_(circuit), builder_(circuit, cmpCfg), - dependEntry_(circuit->GetDependRoot()), tsManager_(tsManager), - enableLog_(enableLog), methodName_(name) {} + TypeMCRLowering(Circuit *circuit, RPOVisitor *visitor, + CompilationConfig *cmpCfg, TSManager *tsManager, Chunk *chunk, bool onHeapCheck) + : PassVisitor(circuit, chunk, visitor), circuit_(circuit), acc_(circuit), builder_(circuit, cmpCfg), + dependEntry_(circuit->GetDependRoot()), tsManager_(tsManager), onHeapCheck_(onHeapCheck) {} ~TypeMCRLowering() = default; - void RunTypeMCRLowering(); + GateRef VisitGate(GateRef gate) override; private: - bool IsLogEnabled() const + bool IsOnHeap() const { - return enableLog_; + return onHeapCheck_; } - - const std::string& GetMethodName() const - { - return methodName_; - } - void Lower(GateRef gate); void LowerType(GateRef gate); void LowerPrimitiveTypeCheck(GateRef gate); @@ -129,13 +123,20 @@ private: void LowerBooleanCheck(GateRef gate); void LowerIndexCheck(GateRef gate); void LowerObjectTypeCheck(GateRef gate); - void LowerConstruntorTypeCheck(GateRef gate); + void LowerSimpleHClassCheck(GateRef gate); void LowerTSSubtypingCheck(GateRef gate); - void LowerFloat32ArrayCheck(GateRef gate); + void LowerObjectTypeCompare(GateRef gate); + void LowerSimpleHClassCompare(GateRef gate); + void LowerTSSubtypingCompare(GateRef gate); + GateRef BuildCompareSubTyping(GateRef gate, GateRef frameState, Label *levelValid, Label *exit); + GateRef BuildCompareHClass(GateRef gate, GateRef frameState); + void BuildCompareSubTyping(GateRef gate); void LowerStableArrayCheck(GateRef gate); void LowerTypedArrayCheck(GateRef gate); - void LowerFloat32ArrayIndexCheck(GateRef gate); - void LowerArrayIndexCheck(GateRef gate); + void LowerEcmaStringCheck(GateRef gate); + void LowerFlattenTreeStringCheck(GateRef gate, GateRef glue); + void LowerLoadTypedArrayLength(GateRef gate); + void LowerStringLength(GateRef gate); void LowerLoadProperty(GateRef gate); void LowerCallGetter(GateRef gate, GateRef glue); void LowerStoreProperty(GateRef gate); @@ -145,23 +146,46 @@ private: void LowerLoadElement(GateRef gate); void LowerLoadFromTaggedArray(GateRef gate); void LowerStoreToTaggedArray(GateRef gate, GateRef glue); - void LowerArrayLoadElement(GateRef gate); - void LowerFloat32ArrayLoadElement(GateRef gate); + void LowerRangeCheckPredicate(GateRef gate); + + enum class ArrayState : uint8_t { + PACKED = 0, + HOLEY, + }; + void LowerArrayLoadElement(GateRef gate, ArrayState arrayState); + void LowerCowArrayCheck(GateRef gate, GateRef glue); + void LowerTypedArrayLoadElement(GateRef gate, BuiltinTypeId id); + void LowerStringLoadElement(GateRef gate); + GateRef BuildOnHeapTypedArrayLoadElement(GateRef receiver, GateRef offset, VariableType type); + GateRef BuildTypedArrayLoadElement(GateRef receiver, GateRef offset, VariableType type, Label *isByteArray, + Label *isArrayBuffer, Label *exit); void LowerArrayStoreElement(GateRef gate, GateRef glue); - void LowerFloat32ArrayStoreElement(GateRef gate, GateRef glue); + void LowerTypedArrayStoreElement(GateRef gate, BuiltinTypeId id); + void BuildOnHeapTypedArrayStoreElement(GateRef receiver, GateRef offset, GateRef value); + void BuildTypedArrayStoreElement(GateRef receiver, GateRef offset, GateRef value, Label *isByteArray, + Label *isArrayBuffer, Label *exit); + void LowerUInt8ClampedArrayStoreElement(GateRef gate); void LowerTypedCallBuitin(GateRef gate); void LowerCallTargetCheck(GateRef gate); + void LowerJSCallTargetCheck(GateRef gate); void LowerJSCallTargetFromDefineFuncCheck(GateRef gate); void LowerJSCallTargetTypeCheck(GateRef gate); void LowerJSFastCallTargetTypeCheck(GateRef gate); void LowerJSCallThisTargetTypeCheck(GateRef gate); void LowerJSFastCallThisTargetTypeCheck(GateRef gate); + void LowerJSNoGCCallThisTargetTypeCheck(GateRef gate); + void LowerJSNoGCFastCallThisTargetTypeCheck(GateRef gate); void LowerTypedNewAllocateThis(GateRef gate, GateRef glue); void LowerTypedSuperAllocateThis(GateRef gate, GateRef glue); void LowerGetSuperConstructor(GateRef gate); void LowerJSInlineTargetTypeCheck(GateRef gate); - void LowerCreateArray(GateRef gate); - void LowerCreateEmptyArray(GateRef gate); + void SetDeoptTypeInfo(BuiltinTypeId id, DeoptType &type, size_t &funcIndex); + void LowerLoadGetter(GateRef gate); + void LowerLoadSetter(GateRef gate); + void LowerInlineAccessorCheck(GateRef gate); + void LowerStringEqual(GateRef gate, GateRef glue); + void LowerTypeOfCheck(GateRef gate); + void LowerTypeOf(GateRef gate, GateRef glue); GateRef LowerCallRuntime(GateRef glue, GateRef hirGate, int index, const std::vector &args, bool useLabel = false); @@ -182,26 +206,29 @@ private: GateType GetLeftType(GateRef gate); GateType GetRightType(GateRef gate); GateRef GetObjectFromConstPool(GateRef jsFunc, GateRef index); + GateRef GetElementSize(BuiltinTypeId id); + VariableType GetVariableType(BuiltinTypeId id); GateRef GetFrameState(GateRef gate) const { return acc_.GetFrameState(gate); } + VariableType GetVarType(PropertyLookupResult plr); GateRef LoadSupers(GateRef hclass); GateRef GetLengthFromSupers(GateRef supers); GateRef GetValueFromSupers(GateRef supers, size_t index); GateRef LoadFromTaggedArray(GateRef array, size_t index); GateRef LoadFromConstPool(GateRef jsFunc, size_t index); GateRef LoadFromVTable(GateRef receiver, size_t index); + GateRef GetLengthFromString(GateRef gate); Circuit *circuit_; GateAccessor acc_; CircuitBuilder builder_; GateRef dependEntry_; [[maybe_unused]] TSManager *tsManager_ {nullptr}; - bool enableLog_ {false}; - std::string methodName_; + bool onHeapCheck_ {false}; }; } // panda::ecmascript::kungfu #endif // ECMASCRIPT_COMPILER_TYPE_MCR_LOWERING_H diff --git a/ecmascript/compiler/type_recorder.cpp b/ecmascript/compiler/type_recorder.cpp index 4e0122821d0fe31324d7d7c7c0080bc1cf5ee1bb..1bb306d7dc2502e3acb6e3496ad5b8f8a7c0de0e 100644 --- a/ecmascript/compiler/type_recorder.cpp +++ b/ecmascript/compiler/type_recorder.cpp @@ -19,20 +19,26 @@ #include "ecmascript/jspandafile/type_literal_extractor.h" #include "ecmascript/pgo_profiler/pgo_profiler_decoder.h" #include "ecmascript/pgo_profiler/pgo_profiler_layout.h" +#include "ecmascript/pgo_profiler/types/pgo_profiler_type.h" #include "ecmascript/ts_types/ts_type_parser.h" namespace panda::ecmascript::kungfu { +using PGOType = pgo::PGOType; +using PGOObjectInfo = pgo::PGOObjectInfo; TypeRecorder::TypeRecorder(const JSPandaFile *jsPandaFile, const MethodLiteral *methodLiteral, - TSManager *tsManager, const CString &recordName, PGOProfilerDecoder *decoder) + TSManager *tsManager, const CString &recordName, PGOProfilerDecoder *decoder, + const MethodPcInfo &methodPCInfo, const Bytecodes *bytecodes, bool enableOptTrackField) : argTypes_(methodLiteral->GetNumArgsWithCallField() + static_cast(TypedArgIdx::NUM_OF_TYPED_ARGS), - GateType::AnyType()), decoder_(decoder) + GateType::AnyType()), decoder_(decoder), enableOptTrackField_(enableOptTrackField), + pcOffsets_(methodPCInfo.pcOffsets), bytecodes_(bytecodes) { TSHClassGenerator generator(tsManager); if (jsPandaFile->HasTSTypes(recordName)) { LoadTypes(jsPandaFile, methodLiteral, tsManager, recordName); - generator.GenerateTSHClasses(); } LoadTypesFromPGO(jsPandaFile, methodLiteral, recordName); + CreateTypesForPGO(jsPandaFile, methodLiteral, tsManager, recordName); + generator.GenerateTSHClasses(); } void TypeRecorder::LoadTypes(const JSPandaFile *jsPandaFile, const MethodLiteral *methodLiteral, @@ -40,30 +46,90 @@ void TypeRecorder::LoadTypes(const JSPandaFile *jsPandaFile, const MethodLiteral { TSTypeParser typeParser(tsManager); panda_file::File::EntityId fieldId = methodLiteral->GetMethodId(); - TypeAnnotationExtractor annoExtractor(jsPandaFile, fieldId.GetOffset()); + uint32_t methodOffset = fieldId.GetOffset(); + TypeAnnotationExtractor annoExtractor(jsPandaFile, methodOffset); GlobalTSTypeRef funcGT = typeParser.CreateGT(jsPandaFile, recordName, annoExtractor.GetMethodTypeOffset()); GlobalTSTypeRef thisGT; annoExtractor.EnumerateInstsAndTypes([this, &typeParser, &jsPandaFile, &recordName, - &thisGT](const int32_t bcOffset, const uint32_t typeId) { + &thisGT, tsManager, methodOffset](const int32_t bcIdx, const uint32_t typeId) { GlobalTSTypeRef gt = typeParser.CreateGT(jsPandaFile, recordName, typeId); if (TypeNeedFilter(gt)) { return; } + TypeLocation loc(jsPandaFile, methodOffset, bcIdx); + CollectLiteralGT(tsManager, loc, gt); // The type of a function is recorded as (-1, funcTypeId). If the function is a member of a class, // the type of the class or its instance is is recorded as (-2, classTypeId). If it is a static // member, the type id refers to the type of the class; otherwise, it links to the type of the // instances of the class. - if (bcOffset == METHOD_ANNOTATION_THIS_TYPE_OFFSET) { + if (bcIdx == METHOD_ANNOTATION_THIS_TYPE_OFFSET) { thisGT = gt; return; } auto type = GateType(gt); - bcOffsetGtMap_.emplace(bcOffset, type); + bcOffsetGtMap_.emplace(bcIdx, type); }); + const auto &methodList = typeParser.GetMethodList(); + auto methodIter = methodList.find(methodOffset); + if (methodIter != methodList.end()) { + const auto &methodInfo = methodIter->second; + const auto &bcTypes = methodInfo.GetBCAndTypes(); + for (const auto &pair : bcTypes) { + GlobalTSTypeRef gt = typeParser.CreateGT(jsPandaFile, recordName, pair.second); + // if the function type has already recorded in the next pc, we should skip it. + if (CheckTypeMarkForDefineFunc(pair.first)) { + continue; + } + bcOffsetGtMap_.emplace(pair.first, GateType(gt)); + } + } LoadArgTypes(tsManager, funcGT, thisGT); } +void TypeRecorder::CollectLiteralGT(TSManager *tsManager, TypeLocation &loc, GlobalTSTypeRef gt) +{ + int32_t bcIdx = loc.GetBcIdx(); + if (bcIdx < 0 || bcIdx >= static_cast(pcOffsets_.size())) { + return; + } + + while (bytecodes_->GetOpcode(pcOffsets_[bcIdx]) == EcmaOpcode::STA_V8) { + // bcIndex of literal marked in es2abc maybe in the next bc whose opcode should be sta. + bcIdx--; + loc.SetBcIdx(bcIdx); + } + + EcmaOpcode ecmaOpcode = bytecodes_->GetOpcode(pcOffsets_[bcIdx]); + + switch (ecmaOpcode) { + case BytecodeInstruction::Opcode::DEFINECLASSWITHBUFFER_IMM16_ID16_ID16_IMM16_V8: + case BytecodeInstruction::Opcode::DEFINECLASSWITHBUFFER_IMM8_ID16_ID16_IMM16_V8: { + if (tsManager->IsUserDefinedClassTypeKind(gt)) { + tsManager->InsertLiteralGTMap(loc, gt); + } + return; + } + case BytecodeInstruction::Opcode::CREATEOBJECTWITHBUFFER_IMM8_ID16: + case BytecodeInstruction::Opcode::CREATEOBJECTWITHBUFFER_IMM16_ID16: { + if (tsManager->IsObjectTypeKind(gt)) { + tsManager->InsertLiteralGTMap(loc, gt); + } + return; + } + default: + return; + } +} + +bool TypeRecorder::CheckTypeMarkForDefineFunc(uint32_t checkBc) const +{ + // bcOffset of definefunc marked in es2abc maybe in the next bc whose opcode should be sta. + uint32_t staBc = checkBc + 1; + return bcOffsetGtMap_.find(staBc) != bcOffsetGtMap_.end() && + staBc < pcOffsets_.size() && bytecodes_->GetOpcode(pcOffsets_[staBc]) == EcmaOpcode::STA_V8; +} + void TypeRecorder::LoadTypesFromPGO(const JSPandaFile *jsPandaFile, const MethodLiteral *methodLiteral, const CString &recordName) { @@ -81,6 +147,58 @@ void TypeRecorder::LoadTypesFromPGO(const JSPandaFile *jsPandaFile, const Method } } +void TypeRecorder::CreateTypesForPGO(const JSPandaFile *jsPandaFile, const MethodLiteral *methodLiteral, + TSManager *tsManager, const CString &recordName) +{ + TSTypeParser typeParser(tsManager); + uint32_t methodOffset = methodLiteral->GetMethodId().GetOffset(); + PGOBCInfo *bcInfo = tsManager->GetBytecodeInfoCollector()->GetPGOBCInfo(); + bcInfo->IterateInfoAndType(methodOffset, [this, &typeParser, methodOffset, &recordName, &jsPandaFile, tsManager] + (const PGOBCInfo::Type type, const uint32_t bcIdx, const uint32_t bcOffset, const uint32_t cpIdx) { + auto it = bcOffsetPGOOpTypeMap_.find(bcOffset); + if (it == bcOffsetPGOOpTypeMap_.end()) { + return; + } + + EcmaOpcode ecmaOpcode = bytecodes_->GetOpcode(pcOffsets_[bcIdx]); + if (jsPandaFile->HasTSTypes(recordName) && Bytecodes::IsCallOp(ecmaOpcode)) { + uint32_t callTargetMethodOffset = it->second.GetProfileType().GetId(); + if (callTargetMethodOffset == 0) { + return; + } + // Recompiling the application ABC changes the content, and there may be a calltargetMethodOffset + // that does not exist in ABC. Type resolution should be skipped when it does not exist, + // or parsing pandafile will fail + if (jsPandaFile->GetMethodLiteralByIndex(callTargetMethodOffset) == nullptr) { + return; + } + TypeAnnotationExtractor annoExtractor(jsPandaFile, callTargetMethodOffset); + GlobalTSTypeRef funcGT = + typeParser.CreateGT(jsPandaFile, recordName, annoExtractor.GetMethodTypeOffset()); + if (funcGT.IsDefault()) { + return; + } + GateType callTargetType = GateType(funcGT); + bcOffsetCallTargetGtMap_.emplace(bcIdx, callTargetType); + return; + } + + TypeLocation loc(jsPandaFile, methodOffset, bcIdx); + if (!tsManager->GetLiteralGT(loc).IsDefault()) { + return; + } + + GlobalTSTypeRef gt = typeParser.CreatePGOGT(TSTypeParser::PGOInfo { + jsPandaFile, recordName, methodOffset, cpIdx, it->second, type, decoder_, enableOptTrackField_ }); + if (TypeNeedFilter(gt)) { + return; + } + CollectLiteralGT(tsManager, loc, gt); + GateType gateType = GateType(gt); + bcOffsetGtMap_.emplace(bcIdx, gateType); + }); +} + void TypeRecorder::LoadArgTypes(const TSManager *tsManager, GlobalTSTypeRef funcGT, GlobalTSTypeRef thisGT) { argTypes_[static_cast(TypedArgIdx::FUNC)] = TryGetFuncType(funcGT); @@ -157,33 +275,81 @@ GateType TypeRecorder::UpdateType(const int32_t offset, const GateType &type) co return type; } +ElementsKind TypeRecorder::GetElementsKind(PGOSampleType type) const +{ + PGOHClassLayoutDesc *desc; + if (type.IsProfileType() && decoder_->GetHClassLayoutDesc(type, &desc)) { + auto elementsKind = desc->GetElementsKind(); + return elementsKind; + } + return ElementsKind::GENERIC; +} + PGOSampleType TypeRecorder::GetOrUpdatePGOType(TSManager *tsManager, int32_t offset, const GateType &type) const { if (bcOffsetPGOOpTypeMap_.find(offset) != bcOffsetPGOOpTypeMap_.end()) { const auto iter = bcOffsetPGOOpTypeMap_.at(offset); - if (iter.IsClassType()) { + if (iter.IsProfileType()) { PGOHClassLayoutDesc *desc; if (!decoder_->GetHClassLayoutDesc(iter, &desc)) { - return PGOSampleType::NoneClassType(); - } - auto hclassValue = tsManager->GetTSHClass(type); - if (hclassValue.IsJSHClass()) { - auto hclass = JSHClass::Cast(hclassValue.GetTaggedObject()); - TSHClassGenerator generator(tsManager); - generator.UpdateTSHClassFromPGO(hclass, *desc); + return PGOSampleType::NoneProfileType(); } + TSHClassGenerator generator(tsManager); + generator.UpdateTSHClassFromPGO(type, *desc, enableOptTrackField_); } return iter; } + return PGOSampleType::NoneType(); +} + +GateType TypeRecorder::GetCallTargetType(int32_t offset) const +{ + if (bcOffsetCallTargetGtMap_.find(offset) != bcOffsetCallTargetGtMap_.end()) { + return bcOffsetCallTargetGtMap_.at(offset); + } + return GateType::AnyType(); +} + +PGORWOpType TypeRecorder::GetRwOpType(int32_t offset) const +{ if (bcOffsetPGORwTypeMap_.find(offset) != bcOffsetPGORwTypeMap_.end()) { - auto defineType = bcOffsetPGORwTypeMap_.at(offset); - // pass mono first - if (defineType.GetCount() == 1) { - return PGOSampleType(defineType.GetType(0)); + return bcOffsetPGORwTypeMap_.at(offset); + } + return PGORWOpType(); +} + +std::vector TypeRecorder::LoadElementsKinds(int32_t offset) const +{ + std::vector elementsKinds; + if (bcOffsetPGORwTypeMap_.find(offset) == bcOffsetPGORwTypeMap_.end()) { + elementsKinds.emplace_back(ElementsKind::GENERIC); + return elementsKinds; + } + + PGORWOpType rwType = bcOffsetPGORwTypeMap_.at(offset); + if (rwType.GetCount() == 0) { + elementsKinds.emplace_back(ElementsKind::GENERIC); + return elementsKinds; + } + for (uint32_t i = 0; i < rwType.GetCount(); i++) { + PGOObjectInfo info = rwType.GetObjectInfo(i); + auto profileType = info.GetProfileType(); + if (profileType.IsElementType()) { + elementsKinds.emplace_back(ElementsKind(profileType.GetId())); + continue; + } + PGOSampleType type(profileType); + PGOHClassLayoutDesc *desc; + if (!decoder_->GetHClassLayoutDesc(type, &desc)) { + elementsKinds.emplace_back(ElementsKind::GENERIC); + continue; } + auto elementsKind = desc->GetElementsKind(); + elementsKinds.emplace_back(elementsKind); } - return PGOSampleType::NoneType(); + + return elementsKinds; } bool TypeRecorder::TypeNeedFilter(GlobalTSTypeRef gt) const diff --git a/ecmascript/compiler/type_recorder.h b/ecmascript/compiler/type_recorder.h index 30fcb110c42e4983e45941c798c56e820281b95a..8d2ac5a3bdea98a0e591b3b7bb2714689a61ecc0 100644 --- a/ecmascript/compiler/type_recorder.h +++ b/ecmascript/compiler/type_recorder.h @@ -33,13 +33,18 @@ enum class TypedArgIdx : uint8_t { class TypeRecorder { public: TypeRecorder(const JSPandaFile *jsPandaFile, const MethodLiteral *methodLiteral, - TSManager *tsManager, const CString &recordName, PGOProfilerDecoder *decoder); + TSManager *tsManager, const CString &recordName, PGOProfilerDecoder *decoder, + const MethodPcInfo &methodPCInfo, const Bytecodes *bytecodes, bool enableOptTrackField); ~TypeRecorder() = default; GateType GetType(const int32_t offset) const; + ElementsKind GetElementsKind(PGOSampleType type) const; PGOSampleType GetOrUpdatePGOType(TSManager *tsManager, int32_t offset, const GateType &type) const; + PGORWOpType GetRwOpType(int32_t offset) const; + std::vector LoadElementsKinds(int32_t offset) const; GateType GetArgType(const uint32_t argIndex) const; GateType UpdateType(const int32_t offset, const GateType &type) const; + GateType GetCallTargetType(int32_t offset) const; static constexpr int METHOD_ANNOTATION_THIS_TYPE_OFFSET = -2; @@ -47,6 +52,9 @@ private: void LoadTypes(const JSPandaFile *jsPandaFile, const MethodLiteral *methodLiteral, TSManager *tsManager, const CString &recordName); + void CreateTypesForPGO(const JSPandaFile *jsPandaFile, const MethodLiteral *methodLiteral, + TSManager *tsManager, const CString &recordName); + void LoadTypesFromPGO(const JSPandaFile *jsPandaFile, const MethodLiteral *methodLiteral, const CString &recordName); @@ -65,11 +73,19 @@ private: bool TypeNeedFilter(GlobalTSTypeRef gt) const; + bool CheckTypeMarkForDefineFunc(uint32_t checkBc) const; + + void CollectLiteralGT(TSManager *tsManager, TypeLocation &tLoc, GlobalTSTypeRef gt); + std::unordered_map bcOffsetGtMap_ {}; + std::unordered_map bcOffsetCallTargetGtMap_ {}; std::unordered_map bcOffsetPGOOpTypeMap_ {}; std::unordered_map bcOffsetPGORwTypeMap_ {}; std::vector argTypes_; PGOProfilerDecoder *decoder_ {nullptr}; + bool enableOptTrackField_ {false}; + const std::vector &pcOffsets_; + const Bytecodes *bytecodes_ {nullptr}; }; } // panda::ecmascript::kungfu #endif // ECMASCRIPT_COMPILER_TYPE_RECORDER_H diff --git a/ecmascript/compiler/typed_array_stub_builder.cpp b/ecmascript/compiler/typed_array_stub_builder.cpp index a0300e83ac4389e22f989a73739064f7aa8c1d3f..9df29670fb0b46dc1dd0e640186efe9fb2712759 100644 --- a/ecmascript/compiler/typed_array_stub_builder.cpp +++ b/ecmascript/compiler/typed_array_stub_builder.cpp @@ -104,7 +104,7 @@ GateRef TypedArrayStubBuilder::FastGetPropertyByIndex(GateRef glue, GateRef arra { GateRef offset = GetByteOffset(array); result = GetValueFromBuffer(buffer, index, offset, jsType); - Jump(&exit); + Branch(TaggedIsNumber(*result), &exit, &slowPath); } } Bind(&slowPath); diff --git a/ecmascript/compiler/value_numbering.cpp b/ecmascript/compiler/value_numbering.cpp index dfd6a85ca60001b8bbc7e46fa5f8b4c87a01e351..2eee303cb42acb9dd865102eb7e3578527121b95 100644 --- a/ecmascript/compiler/value_numbering.cpp +++ b/ecmascript/compiler/value_numbering.cpp @@ -15,26 +15,11 @@ #include "ecmascript/compiler/value_numbering.h" namespace panda::ecmascript::kungfu { -void ValueNumbering::Run() -{ - VisitGraph(); - if (IsLogEnabled()) { - LOG_COMPILER(INFO) << ""; - LOG_COMPILER(INFO) << "\033[34m" - << "====================" - << " After value numbering " - << "[" << GetMethodName() << "]" - << "====================" - << "\033[0m"; - circuit_->PrintAllGatesWithBytecode(); - LOG_COMPILER(INFO) << "\033[34m" << "========================= End ==========================" << "\033[0m"; - } -} GateRef ValueNumbering::VisitGate(GateRef gate) { auto opcode = acc_.GetOpCode(gate); - if (opcode != OpCode::CONVERT && opcode != OpCode::CHECK_AND_CONVERT) { + if (opcode != OpCode::CONVERT) { return Circuit::NullGate(); } size_t hash = HashCode(gate); @@ -85,7 +70,7 @@ bool ValueNumbering::CheckReplacement(GateRef lhs, GateRef rhs) } } auto opcode = acc_.GetOpCode(lhs); - if (opcode == OpCode::CONVERT || opcode == OpCode::CHECK_AND_CONVERT) { + if (opcode == OpCode::CONVERT) { if (acc_.GetSrcType(lhs) != acc_.GetSrcType(rhs)) { return false; } diff --git a/ecmascript/compiler/value_numbering.h b/ecmascript/compiler/value_numbering.h index 64d254de94ea7dc463a5d7f82e27ec79746f2061..949ab1c1271021302d0ec85c4914eb4588bda46e 100644 --- a/ecmascript/compiler/value_numbering.h +++ b/ecmascript/compiler/value_numbering.h @@ -17,34 +17,21 @@ #define ECMASCRIPT_COMPILER_VALUE_NUMBERING_H #include "ecmascript/compiler/circuit_builder.h" +#include "ecmascript/compiler/combined_pass_visitor.h" #include "ecmascript/compiler/gate_accessor.h" -#include "ecmascript/compiler/graph_visitor.h" #include "ecmascript/mem/chunk_containers.h" namespace panda::ecmascript::kungfu { -class ValueNumbering : public GraphVisitor { +class ValueNumbering : public PassVisitor { public: - ValueNumbering(Circuit *circuit, bool enableLog, const std::string& name, Chunk* chunk) - : GraphVisitor(circuit, chunk), enableLog_(enableLog), - methodName_(name), entries_(chunk) {} + ValueNumbering(Circuit *circuit, RPOVisitor *visitor, Chunk* chunk) + : PassVisitor(circuit, chunk, visitor), entries_(chunk) {} ~ValueNumbering() = default; - void Run(); - GateRef VisitGate(GateRef gate) override; bool CheckReplacement(GateRef lhs, GateRef rhs); private: - bool IsLogEnabled() const - { - return enableLog_; - } - - const std::string& GetMethodName() const - { - return methodName_; - } - size_t HashCode(GateRef gate); GateRef GetEntry(size_t hash) { @@ -59,8 +46,6 @@ private: static const uint32_t CACHE_LENGTH_BIT = 8; static const uint32_t CACHE_LENGTH = (1U << CACHE_LENGTH_BIT); - bool enableLog_ {false}; - std::string methodName_; ChunkVector entries_; }; } // panda::ecmascript::kungfu diff --git a/ecmascript/compiler/verifier.cpp b/ecmascript/compiler/verifier.cpp index 775479f7d688247248d22a9c4b5873d0af4c813b..a9641036f709f2fe69f860c3c8d3a33aed7c45c5 100644 --- a/ecmascript/compiler/verifier.cpp +++ b/ecmascript/compiler/verifier.cpp @@ -21,7 +21,7 @@ #include #include -#include "ecmascript/compiler/gate_meta_data.h" +#include "ecmascript/compiler/share_gate_meta_data.h" #include "ecmascript/compiler/scheduler.h" #include "ecmascript/compiler/gate_accessor.h" #include "ecmascript/ecma_macros.h" @@ -428,6 +428,52 @@ void Verifier::FindFixedGates(const Circuit *circuit, const std::vector } } +bool Verifier::RunFlowCyclesFind(const Circuit* circuit) +{ + GateAccessor acc(const_cast(circuit)); + circuit->AdvanceTime(); + struct DFSStack { + GateRef gate; + GateAccessor::UseIterator it; + GateAccessor::UseIterator end; + }; + std::stack st; + auto root = acc.GetCircuitRoot(); + auto rootUse = acc.Uses(root); + st.push({root, rootUse.begin(), rootUse.end()}); + acc.SetVisited(root); + while (!st.empty()) { + auto& cur = st.top(); + if (cur.it == cur.end) { + st.pop(); + acc.SetFinished(cur.gate); + continue; + } + auto succ = *cur.it; + if (acc.IsLoopBackUse(cur.it)) { + cur.it++; + continue; + } + if (acc.IsNotMarked(succ)) { + auto succUse = acc.Uses(succ); + st.push({succ, succUse.begin(), succUse.end()}); + acc.SetVisited(succ); + } else if (acc.IsVisited(succ)) { + LOG_COMPILER(ERROR) << "====================== Cycle Found ======================"; + std::string log = ""; + while (st.top().gate != succ) { + log += std::to_string(acc.GetId(st.top().gate)) + " < "; + st.pop(); + } + log += std::to_string(acc.GetId(st.top().gate)); + LOG_COMPILER(ERROR) << log; + return true; + } + cur.it++; + } + return false; +} + bool Verifier::Run(const Circuit *circuit, const std::string& methodName, bool enableLog) { if (!RunDataIntegrityCheck(circuit)) { diff --git a/ecmascript/compiler/verifier.h b/ecmascript/compiler/verifier.h index 450a1b04b438a996fa73ce3f9aee4b4209ecc265..c5e9416eb9b261aa479e635522bdbd2dcb49877e 100644 --- a/ecmascript/compiler/verifier.h +++ b/ecmascript/compiler/verifier.h @@ -61,6 +61,8 @@ public: static void FindFixedGates(const Circuit *circuit, const std::vector &bbGatesList, std::vector &fixedGatesList); + + static bool RunFlowCyclesFind(const Circuit* circuit); static bool Run(const Circuit *circuit, const std::string& methodName = "", bool enableLog = false); }; diff --git a/ecmascript/containers/containers_arraylist.cpp b/ecmascript/containers/containers_arraylist.cpp index dbf85648a46e6a5c6db326b78d18bf2c04866f9f..d1a69b14e366fe2baedfe38e132262379df44c59 100644 --- a/ecmascript/containers/containers_arraylist.cpp +++ b/ecmascript/containers/containers_arraylist.cpp @@ -94,6 +94,7 @@ JSTaggedValue ContainersArrayList::Insert(EcmaRuntimeCallInfo *argv) JSHandle index = GetCallArg(argv, 1); if (!index->IsInteger()) { JSHandle result = JSTaggedValue::ToString(thread, index); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"index\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); @@ -218,6 +219,7 @@ JSTaggedValue ContainersArrayList::IncreaseCapacityTo(EcmaRuntimeCallInfo *argv) JSHandle newCapacity = GetCallArg(argv, 0); if (!newCapacity->IsInteger()) { JSHandle result = JSTaggedValue::ToString(thread, newCapacity); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"newCapacity\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -366,6 +368,7 @@ JSTaggedValue ContainersArrayList::RemoveByIndex(EcmaRuntimeCallInfo *argv) JSHandle value = GetCallArg(argv, 0); if (!value->IsInteger()) { JSHandle result = JSTaggedValue::ToString(thread, value.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"index\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -429,6 +432,7 @@ JSTaggedValue ContainersArrayList::RemoveByRange(EcmaRuntimeCallInfo *argv) if (!startIndex->IsInteger()) { std::ostringstream oss; JSHandle result = JSTaggedValue::ToString(thread, startIndex); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"fromIndex\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -437,6 +441,7 @@ JSTaggedValue ContainersArrayList::RemoveByRange(EcmaRuntimeCallInfo *argv) if (!endIndex->IsInteger()) { std::ostringstream oss; JSHandle result = JSTaggedValue::ToString(thread, endIndex); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"toIndex\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -470,6 +475,7 @@ JSTaggedValue ContainersArrayList::ReplaceAllElements(EcmaRuntimeCallInfo *argv) JSHandle callbackFnHandle = GetCallArg(argv, 0); if (!callbackFnHandle->IsCallable()) { JSHandle result = JSTaggedValue::ToString(thread, callbackFnHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -528,6 +534,7 @@ JSTaggedValue ContainersArrayList::SubArrayList(EcmaRuntimeCallInfo *argv) if (!value1->IsInteger()) { std::ostringstream oss; JSHandle result = JSTaggedValue::ToString(thread, value1); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"fromIndex\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -536,6 +543,7 @@ JSTaggedValue ContainersArrayList::SubArrayList(EcmaRuntimeCallInfo *argv) if (!value2->IsInteger()) { std::ostringstream oss; JSHandle result = JSTaggedValue::ToString(thread, value2); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"toIndex\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -567,6 +575,7 @@ JSTaggedValue ContainersArrayList::Sort(EcmaRuntimeCallInfo *argv) JSHandle callbackFnHandle = GetCallArg(argv, 0); if (!callbackFnHandle->IsUndefined() && !callbackFnHandle->IsCallable() && !callbackFnHandle->IsNull()) { JSHandle result = JSTaggedValue::ToString(thread, callbackFnHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"comparator\" must be callable. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -677,6 +686,7 @@ JSTaggedValue ContainersArrayList::ForEach(EcmaRuntimeCallInfo *argv) JSHandle callbackFnHandle = GetCallArg(argv, 0); if (!callbackFnHandle->IsCallable()) { JSHandle result = JSTaggedValue::ToString(thread, callbackFnHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); diff --git a/ecmascript/containers/containers_deque.cpp b/ecmascript/containers/containers_deque.cpp index 40e930b46341fd498f50c7942a91df0eed140dc8..6d86f6a52a7455609e35590aea01fc3cef20e25c 100644 --- a/ecmascript/containers/containers_deque.cpp +++ b/ecmascript/containers/containers_deque.cpp @@ -237,6 +237,7 @@ JSTaggedValue ContainersDeque::ForEach(EcmaRuntimeCallInfo *argv) JSHandle callbackFnHandle = GetCallArg(argv, 0); if (!callbackFnHandle->IsCallable()) { JSHandle result = JSTaggedValue::ToString(thread, callbackFnHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); diff --git a/ecmascript/containers/containers_errors.cpp b/ecmascript/containers/containers_errors.cpp index 30aa038f357896918abed90ed2f845e300baaac2..d51e499097fa33d1893694c9d2001c50d72944ec 100644 --- a/ecmascript/containers/containers_errors.cpp +++ b/ecmascript/containers/containers_errors.cpp @@ -25,8 +25,10 @@ JSTaggedValue ContainerError::BusinessError(JSThread *thread, int32_t errorCode, JSHandle name = factory->NewFromUtf8("name"); JSHandle value = factory->NewFromUtf8("BusinessError"); JSObject::CreateDataPropertyOrThrow(thread, error, JSHandle::Cast(key), code); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSObject::CreateDataPropertyOrThrow(thread, error, JSHandle::Cast(name), JSHandle::Cast(value)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return error.GetTaggedValue(); } } // namespace panda::ecmascript::containers \ No newline at end of file diff --git a/ecmascript/containers/containers_hashmap.cpp b/ecmascript/containers/containers_hashmap.cpp index 59f2c5f6f93c287f9d894354927be72081cadc3c..c83fb67d60a424f5572e615f4f98ad12efd5251d 100644 --- a/ecmascript/containers/containers_hashmap.cpp +++ b/ecmascript/containers/containers_hashmap.cpp @@ -138,6 +138,7 @@ JSTaggedValue ContainersHashMap::ForEach(EcmaRuntimeCallInfo *argv) JSHandle callbackFnHandle = GetCallArg(argv, 0); if (!callbackFnHandle->IsCallable()) { JSHandle result = JSTaggedValue::ToString(thread, callbackFnHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -218,6 +219,7 @@ JSTaggedValue ContainersHashMap::SetAll(EcmaRuntimeCallInfo *argv) obj = JSHandle(thread, JSHandle::Cast(obj)->GetTarget()); } else { JSHandle result = JSTaggedValue::ToString(thread, obj); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"map\" must be HashMap. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); diff --git a/ecmascript/containers/containers_hashset.cpp b/ecmascript/containers/containers_hashset.cpp index c7f150b85a1182737a4f661914d33cf7509c995b..744d7c1cf459a68ebb228ed7989780aac270cf86 100644 --- a/ecmascript/containers/containers_hashset.cpp +++ b/ecmascript/containers/containers_hashset.cpp @@ -248,6 +248,7 @@ JSTaggedValue ContainersHashSet::ForEach(EcmaRuntimeCallInfo *argv) JSHandle callbackFnHandle = GetCallArg(argv, 0); if (!callbackFnHandle->IsCallable()) { JSHandle result = JSTaggedValue::ToString(thread, callbackFnHandle); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); diff --git a/ecmascript/containers/containers_lightweightmap.cpp b/ecmascript/containers/containers_lightweightmap.cpp index 02055edbc7c180ac8fdb16e491f1c78adf20f217..4d2535897ea36275d8f046b7eb9342bfff97735d 100644 --- a/ecmascript/containers/containers_lightweightmap.cpp +++ b/ecmascript/containers/containers_lightweightmap.cpp @@ -103,6 +103,7 @@ JSTaggedValue ContainersLightWeightMap::HasAll(EcmaRuntimeCallInfo *argv) lightWeightMap = JSHandle(thread, JSHandle::Cast(lightWeightMap)->GetTarget()); } else { JSHandle result = JSTaggedValue::ToString(thread, lightWeightMap.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"map\" must be LightWeightMap. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -180,6 +181,7 @@ JSTaggedValue ContainersLightWeightMap::IncreaseCapacityTo(EcmaRuntimeCallInfo * if (!index->IsInt()) { JSHandle result = JSTaggedValue::ToString(thread, index); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"minimumCapacity\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -317,6 +319,7 @@ JSTaggedValue ContainersLightWeightMap::GetKeyAt(EcmaRuntimeCallInfo *argv) if (!index->IsInt()) { JSHandle result = JSTaggedValue::ToString(thread, index); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"index\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -365,6 +368,7 @@ JSTaggedValue ContainersLightWeightMap::SetAll(EcmaRuntimeCallInfo *argv) lightWeightMap = JSHandle(thread, JSHandle::Cast(lightWeightMap)->GetTarget()); } else { JSHandle result = JSTaggedValue::ToString(thread, lightWeightMap.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"map\" must be LightWeightMap. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -445,6 +449,7 @@ JSTaggedValue ContainersLightWeightMap::RemoveAt(EcmaRuntimeCallInfo *argv) JSHandle index(GetCallArg(argv, 0)); if (!index->IsInt()) { JSHandle result = JSTaggedValue::ToString(thread, index); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"index\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -499,6 +504,7 @@ JSTaggedValue ContainersLightWeightMap::SetValueAt(EcmaRuntimeCallInfo *argv) JSHandle value(GetCallArg(argv, 1)); if (!index->IsInt()) { JSHandle result = JSTaggedValue::ToString(thread, index); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"index\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -530,6 +536,7 @@ JSTaggedValue ContainersLightWeightMap::ForEach(EcmaRuntimeCallInfo *argv) JSHandle func(GetCallArg(argv, 0)); if (!func->IsCallable()) { JSHandle result = JSTaggedValue::ToString(thread, func.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -541,9 +548,9 @@ JSTaggedValue ContainersLightWeightMap::ForEach(EcmaRuntimeCallInfo *argv) JSMutableHandle keys(thread, tmap->GetKeys()); JSMutableHandle values(thread, tmap->GetValues()); - int index = 0; - int32_t length = tmap->GetSize(); - const int32_t argsLength = 3; + uint32_t index = 0; + uint32_t length = tmap->GetSize(); + const uint32_t argsLength = 3; JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); while (index < length) { // ignore the hash value is required to determine the true index @@ -606,6 +613,7 @@ JSTaggedValue ContainersLightWeightMap::GetValueAt(EcmaRuntimeCallInfo *argv) JSHandle index(GetCallArg(argv, 0)); if (!index->IsInt()) { JSHandle result = JSTaggedValue::ToString(thread, index); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"index\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); diff --git a/ecmascript/containers/containers_lightweightset.cpp b/ecmascript/containers/containers_lightweightset.cpp index f7e2fecb9295b8b37f73b389341fb1ee00fa2730..b1a8b8bdade489965bcb50a8f313ab872e79200a 100644 --- a/ecmascript/containers/containers_lightweightset.cpp +++ b/ecmascript/containers/containers_lightweightset.cpp @@ -96,6 +96,7 @@ JSTaggedValue ContainersLightWeightSet::AddAll(EcmaRuntimeCallInfo *argv) value = JSHandle(thread, JSHandle::Cast(value)->GetTarget()); } else { JSHandle result = JSTaggedValue::ToString(thread, value.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"set\" must be LightWeightSet. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -145,6 +146,7 @@ JSTaggedValue ContainersLightWeightSet::GetValueAt(EcmaRuntimeCallInfo *argv) JSHandle value(GetCallArg(argv, 0)); if (!value->IsInteger()) { JSHandle result = JSTaggedValue::ToString(thread, value.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"index\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -177,6 +179,7 @@ JSTaggedValue ContainersLightWeightSet::HasAll(EcmaRuntimeCallInfo *argv) value = JSHandle(thread, JSHandle::Cast(value)->GetTarget()); } else { JSHandle result = JSTaggedValue::ToString(thread, value.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"set\" must be LightWeightSet. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -268,6 +271,7 @@ JSTaggedValue ContainersLightWeightSet::IncreaseCapacityTo(EcmaRuntimeCallInfo * JSHandle value(GetCallArg(argv, 0)); if (!value->IsInteger()) { JSHandle result = JSTaggedValue::ToString(thread, value.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"minimumCapacity\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -364,6 +368,7 @@ JSTaggedValue ContainersLightWeightSet::ForEach(EcmaRuntimeCallInfo *argv) if (!callbackFnHandle->IsCallable()) { JSHandle result = JSTaggedValue::ToString(thread, callbackFnHandle.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -435,6 +440,7 @@ JSTaggedValue ContainersLightWeightSet::RemoveAt(EcmaRuntimeCallInfo *argv) JSHandle value(GetCallArg(argv, 0)); if (!value->IsInteger()) { JSHandle result = JSTaggedValue::ToString(thread, value.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"index\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); diff --git a/ecmascript/containers/containers_linked_list.cpp b/ecmascript/containers/containers_linked_list.cpp index 5a50e7f49ac2c3276bc692b40c7516f256145828..eb8abdec66234c6ced0d568f46c998e23560e1af 100644 --- a/ecmascript/containers/containers_linked_list.cpp +++ b/ecmascript/containers/containers_linked_list.cpp @@ -178,6 +178,7 @@ JSTaggedValue ContainersLinkedList::Insert(EcmaRuntimeCallInfo *argv) if (!index->IsInteger()) { JSHandle result = JSTaggedValue::ToString(thread, index.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"index\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -275,6 +276,7 @@ JSTaggedValue ContainersLinkedList::Get(EcmaRuntimeCallInfo *argv) JSHandle index = GetCallArg(argv, 0); if (!index->IsInteger()) { JSHandle result = JSTaggedValue::ToString(thread, index.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"index\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -344,6 +346,7 @@ JSTaggedValue ContainersLinkedList::RemoveByIndex(EcmaRuntimeCallInfo *argv) JSHandle index = GetCallArg(argv, 0); if (!index->IsInteger()) { JSHandle result = JSTaggedValue::ToString(thread, index.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"index\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -488,6 +491,7 @@ JSTaggedValue ContainersLinkedList::Set(EcmaRuntimeCallInfo *argv) JSHandle element = GetCallArg(argv, 1); if (!index->IsInteger()) { JSHandle result = JSTaggedValue::ToString(thread, index.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"index\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -540,6 +544,7 @@ JSTaggedValue ContainersLinkedList::ForEach(EcmaRuntimeCallInfo *argv) JSHandle callbackFnHandle(GetCallArg(argv, 0)); if (!callbackFnHandle->IsCallable()) { JSHandle result = JSTaggedValue::ToString(thread, callbackFnHandle.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -549,9 +554,9 @@ JSTaggedValue ContainersLinkedList::ForEach(EcmaRuntimeCallInfo *argv) JSHandle thisArgHandle = GetCallArg(argv, 1); JSHandle linkedList = JSHandle::Cast(thisHandle); JSHandle doubleList(thread, linkedList->GetDoubleList()); - int length = linkedList->Length(); + uint32_t length = linkedList->Length(); - int index = 0; + uint32_t index = 0; const uint32_t argsLength = 3; // 3: «kValue, k, O» JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); int valueNode = TaggedDoubleList::ELEMENTS_START_INDEX; diff --git a/ecmascript/containers/containers_list.cpp b/ecmascript/containers/containers_list.cpp index 43aca99f2ef187887782f288b4fb227c5b416e9e..f87d5f5d388a5c3eb092c3045ccf16358d1d8ce1 100644 --- a/ecmascript/containers/containers_list.cpp +++ b/ecmascript/containers/containers_list.cpp @@ -94,6 +94,7 @@ JSTaggedValue ContainersList::Insert(EcmaRuntimeCallInfo *argv) JSHandle index = GetCallArg(argv, 1); if (!index->IsInteger()) { JSHandle result = JSTaggedValue::ToString(thread, index.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"index\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -206,6 +207,7 @@ JSTaggedValue ContainersList::Get(EcmaRuntimeCallInfo *argv) JSHandle index = GetCallArg(argv, 0); if (!index->IsInteger()) { JSHandle result = JSTaggedValue::ToString(thread, index.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"index\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -276,6 +278,7 @@ JSTaggedValue ContainersList::Set(EcmaRuntimeCallInfo *argv) JSHandle element = GetCallArg(argv, 1); if (!index->IsInteger()) { JSHandle result = JSTaggedValue::ToString(thread, index.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"index\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -309,6 +312,7 @@ JSTaggedValue ContainersList::ForEach(EcmaRuntimeCallInfo *argv) JSHandle callbackFnHandle(GetCallArg(argv, 0)); if (!callbackFnHandle->IsCallable()) { JSHandle result = JSTaggedValue::ToString(thread, callbackFnHandle.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -319,10 +323,10 @@ JSTaggedValue ContainersList::ForEach(EcmaRuntimeCallInfo *argv) JSHandle thisArgHandle = GetCallArg(argv, 1); JSHandle list = JSHandle::Cast(thisHandle); JSHandle singleList(thread, list->GetSingleList()); - int length = list->Length(); + uint32_t length = list->Length(); JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); - int index = 0; + uint32_t index = 0; const uint32_t argsLength = 3; // 3: «kValue, k, O» int valueNode = TaggedSingleList::ELEMENTS_START_INDEX; while (index < length) { @@ -379,6 +383,7 @@ JSTaggedValue ContainersList::RemoveByIndex(EcmaRuntimeCallInfo *argv) JSHandle index = GetCallArg(argv, 0); if (!index->IsInteger()) { JSHandle result = JSTaggedValue::ToString(thread, index.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"index\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -429,6 +434,7 @@ JSTaggedValue ContainersList::ReplaceAllElements(EcmaRuntimeCallInfo *argv) JSHandle callbackFnHandle = GetCallArg(argv, 0); if (!callbackFnHandle->IsCallable()) { JSHandle result = JSTaggedValue::ToString(thread, callbackFnHandle.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -486,6 +492,7 @@ JSTaggedValue ContainersList::Sort(EcmaRuntimeCallInfo *argv) JSHandle callbackFnHandle = GetCallArg(argv, 0); if (!callbackFnHandle->IsUndefined() && !callbackFnHandle->IsCallable()) { JSHandle result = JSTaggedValue::ToString(thread, callbackFnHandle.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"comparator\" must be callable. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -545,6 +552,7 @@ JSTaggedValue ContainersList::GetSubList(EcmaRuntimeCallInfo *argv) if (!fromIndex->IsInteger()) { JSHandle result = JSTaggedValue::ToString(thread, fromIndex.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"fromIndex\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -555,6 +563,7 @@ JSTaggedValue ContainersList::GetSubList(EcmaRuntimeCallInfo *argv) if (!toIndex->IsInteger()) { JSHandle result = JSTaggedValue::ToString(thread, toIndex.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"toIndex\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); diff --git a/ecmascript/containers/containers_plainarray.cpp b/ecmascript/containers/containers_plainarray.cpp index 922b450fc0b4fdbeae7dc79c495e02e8f15345a5..d665bf76b788ee2e92441dc2c9c8c964f79671df 100644 --- a/ecmascript/containers/containers_plainarray.cpp +++ b/ecmascript/containers/containers_plainarray.cpp @@ -70,6 +70,7 @@ JSTaggedValue ContainersPlainArray::Add(EcmaRuntimeCallInfo *argv) JSHandle value(GetCallArg(argv, 1)); if (!key->IsInteger()) { JSHandle result = JSTaggedValue::ToString(thread, key.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"key\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -140,6 +141,7 @@ JSTaggedValue ContainersPlainArray::Has(EcmaRuntimeCallInfo *argv) JSHandle value(GetCallArg(argv, 0)); if (!value->IsNumber()) { JSHandle result = JSTaggedValue::ToString(thread, value.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"key\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -170,6 +172,7 @@ JSTaggedValue ContainersPlainArray::Get(EcmaRuntimeCallInfo *argv) JSHandle key(GetCallArg(argv, 0)); if (!key->IsInteger()) { JSHandle result = JSTaggedValue::ToString(thread, key.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"key\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -220,6 +223,7 @@ JSTaggedValue ContainersPlainArray::ForEach(EcmaRuntimeCallInfo *argv) JSHandle callbackFnHandle = GetCallArg(argv, 0); if (!callbackFnHandle->IsCallable()) { JSHandle result = JSTaggedValue::ToString(thread, callbackFnHandle.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -266,6 +270,7 @@ JSTaggedValue ContainersPlainArray::GetIndexOfKey(EcmaRuntimeCallInfo *argv) JSHandle value(GetCallArg(argv, 0)); if (!value->IsNumber()) { JSHandle result = JSTaggedValue::ToString(thread, value.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"key\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -336,6 +341,7 @@ JSTaggedValue ContainersPlainArray::GetKeyAt(EcmaRuntimeCallInfo *argv) JSHandle value(GetCallArg(argv, 0)); if (!value->IsNumber()) { JSHandle result = JSTaggedValue::ToString(thread, value.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"index\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -365,6 +371,7 @@ JSTaggedValue ContainersPlainArray::Remove(EcmaRuntimeCallInfo *argv) JSHandle key(GetCallArg(argv, 0)); if (!key->IsInteger()) { JSHandle result = JSTaggedValue::ToString(thread, key.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"key\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -393,6 +400,7 @@ JSTaggedValue ContainersPlainArray::RemoveAt(EcmaRuntimeCallInfo *argv) JSHandle index(GetCallArg(argv, 0)); if (!index->IsInteger()) { JSHandle result = JSTaggedValue::ToString(thread, index.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"index\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -422,6 +430,7 @@ JSTaggedValue ContainersPlainArray::RemoveRangeFrom(EcmaRuntimeCallInfo *argv) JSHandle valueSize(GetCallArg(argv, 1)); if (!valueIndex->IsInteger()) { JSHandle result = JSTaggedValue::ToString(thread, valueIndex.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"index\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -429,6 +438,7 @@ JSTaggedValue ContainersPlainArray::RemoveRangeFrom(EcmaRuntimeCallInfo *argv) } if (!valueSize->IsInteger()) { JSHandle result = JSTaggedValue::ToString(thread, valueSize.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"size\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -461,6 +471,7 @@ JSTaggedValue ContainersPlainArray::SetValueAt(EcmaRuntimeCallInfo *argv) JSHandle value(GetCallArg(argv, 1)); if (!index->IsInteger()) { JSHandle result = JSTaggedValue::ToString(thread, index.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"index\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -490,6 +501,7 @@ JSTaggedValue ContainersPlainArray::GetValueAt(EcmaRuntimeCallInfo *argv) JSHandle idx(GetCallArg(argv, 0)); if (!idx->IsInteger()) { JSHandle result = JSTaggedValue::ToString(thread, idx.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"index\" must be number. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -516,7 +528,7 @@ JSTaggedValue ContainersPlainArray::GetSize(EcmaRuntimeCallInfo *argv) THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } } - int32_t length = JSHandle::Cast(self)->GetSize(); + uint32_t length = JSHandle::Cast(self)->GetSize(); return JSTaggedValue(length); } } // namespace panda::ecmascript::containers diff --git a/ecmascript/containers/containers_private.cpp b/ecmascript/containers/containers_private.cpp index 44faffd6909b6229a63cf3fa0491a77080db541a..26f1addc39d528fa5127d42b9a41b1b113ed61bf 100644 --- a/ecmascript/containers/containers_private.cpp +++ b/ecmascript/containers/containers_private.cpp @@ -529,6 +529,7 @@ JSHandle ContainersPrivate::InitializeTreeMap(JSThread *thread) JSHandle entries = globalConst->GetHandledEntriesString(); JSHandle entriesFunc = JSObject::GetMethod(thread, JSHandle::Cast(mapFuncPrototype), entries); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); PropertyDescriptor descriptor(thread, entriesFunc, false, false, false); JSObject::DefineOwnProperty(thread, mapFuncPrototype, iteratorSymbol, descriptor); // length @@ -601,6 +602,7 @@ JSHandle ContainersPrivate::InitializeTreeSet(JSThread *thread) JSHandle values(thread, globalConst->GetValuesString()); JSHandle valuesFunc = JSObject::GetMethod(thread, JSHandle::Cast(setFuncPrototype), values); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); PropertyDescriptor descriptor(thread, valuesFunc, false, false, false); JSObject::DefineOwnProperty(thread, setFuncPrototype, iteratorSymbol, descriptor); // length @@ -1139,6 +1141,7 @@ JSHandle ContainersPrivate::InitializeHashMap(JSThread *thread) JSHandle entries(factory->NewFromASCII("entries")); JSHandle entriesFunc = JSObject::GetMethod(thread, JSHandle::Cast(hashMapFuncPrototype), entries); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); PropertyDescriptor descriptor(thread, entriesFunc, false, false, false); JSObject::DefineOwnProperty(thread, hashMapFuncPrototype, iteratorSymbol, descriptor); @@ -1205,6 +1208,7 @@ JSHandle ContainersPrivate::InitializeHashSet(JSThread *thread) JSHandle values(thread, globalConst->GetValuesString()); JSHandle valuesFunc = JSObject::GetMethod(thread, JSHandle::Cast(hashSetFuncPrototype), values); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); PropertyDescriptor descriptor(thread, valuesFunc, false, false, false); JSObject::DefineOwnProperty(thread, hashSetFuncPrototype, iteratorSymbol, descriptor); diff --git a/ecmascript/containers/containers_queue.cpp b/ecmascript/containers/containers_queue.cpp index b79d938387e4196305534d27bfbf2db57db2ac13..70db1bfdd72ec84d0ac4165722ba0a94cfd52fb3 100644 --- a/ecmascript/containers/containers_queue.cpp +++ b/ecmascript/containers/containers_queue.cpp @@ -144,6 +144,7 @@ JSTaggedValue ContainersQueue::ForEach(EcmaRuntimeCallInfo *argv) // If IsCallable(callbackfn) is false, throw a TypeError exception. if (!callbackFnHandle->IsCallable()) { JSHandle result = JSTaggedValue::ToString(thread, callbackFnHandle.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -161,7 +162,7 @@ JSTaggedValue ContainersQueue::ForEach(EcmaRuntimeCallInfo *argv) JSHandle(thread, queue->Get(thread, index)); index = queue->GetNextPosition(index); key.Update(JSTaggedValue(k)); - const int32_t argsLength = 3; + const uint32_t argsLength = 3; EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); diff --git a/ecmascript/containers/containers_stack.cpp b/ecmascript/containers/containers_stack.cpp index 8ec40a0e7c5b2e6f89f34c064580e9fb858103bc..6b8f0f68171e6994d6026cfe948358b0c056cde4 100644 --- a/ecmascript/containers/containers_stack.cpp +++ b/ecmascript/containers/containers_stack.cpp @@ -186,12 +186,13 @@ JSTaggedValue ContainersStack::ForEach(EcmaRuntimeCallInfo *argv) } JSHandle stack = JSHandle::Cast(thisHandle); - int len = stack->GetSize(); + uint32_t len = stack->GetSize(); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle callbackFnHandle = GetCallArg(argv, 0); if (!callbackFnHandle->IsCallable()) { JSHandle result = JSTaggedValue::ToString(thread, callbackFnHandle.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -200,7 +201,7 @@ JSTaggedValue ContainersStack::ForEach(EcmaRuntimeCallInfo *argv) JSHandle thisArgHandle = GetCallArg(argv, 1); JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); - int k = 0; + uint32_t k = 0; while (k < len + 1) { JSTaggedValue kValue = stack->Get(k); EcmaRuntimeCallInfo *info = @@ -253,7 +254,7 @@ JSTaggedValue ContainersStack::GetLength(EcmaRuntimeCallInfo *argv) } } - int len = (JSHandle::Cast(self))->GetSize(); + uint32_t len = (JSHandle::Cast(self))->GetSize(); return JSTaggedValue(len + 1); } } // namespace panda::ecmascript::containers diff --git a/ecmascript/containers/containers_treemap.cpp b/ecmascript/containers/containers_treemap.cpp index 3a7159dcfa9584e8d51fba6b01390ce08d7e85ff..ea356325774c52c76c425b6ef26cbed91c26521b 100644 --- a/ecmascript/containers/containers_treemap.cpp +++ b/ecmascript/containers/containers_treemap.cpp @@ -58,6 +58,7 @@ JSTaggedValue ContainersTreeMap::TreeMapConstructor(EcmaRuntimeCallInfo *argv) } if (!compareFn->IsCallable()) { JSHandle result = JSTaggedValue::ToString(thread, compareFn.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"comparefn\" must be callable. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -252,6 +253,7 @@ JSTaggedValue ContainersTreeMap::SetAll(EcmaRuntimeCallInfo *argv) obj = JSHandle(thread, JSHandle::Cast(obj)->GetTarget()); } else { JSHandle result = JSTaggedValue::ToString(thread, obj.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"map\" must be TreeMap. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -415,6 +417,7 @@ JSTaggedValue ContainersTreeMap::ForEach(EcmaRuntimeCallInfo *argv) JSHandle func(GetCallArg(argv, 0)); if (!func->IsCallable()) { JSHandle result = JSTaggedValue::ToString(thread, func.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -424,11 +427,11 @@ JSTaggedValue ContainersTreeMap::ForEach(EcmaRuntimeCallInfo *argv) JSHandle thisArg = GetCallArg(argv, 1); JSHandle tmap = JSHandle::Cast(self); JSMutableHandle iteratedMap(thread, tmap->GetTreeMap()); - int elements = iteratedMap->NumberOfElements(); + uint32_t elements = iteratedMap->NumberOfElements(); JSHandle entries = TaggedTreeMap::GetArrayFromMap(thread, iteratedMap); - int index = 0; + uint32_t index = 0; size_t length = entries->GetLength(); - const int32_t argsLength = 3; + const uint32_t argsLength = 3; JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); JSMutableHandle key(thread, JSTaggedValue::Undefined()); JSMutableHandle value(thread, JSTaggedValue::Undefined()); diff --git a/ecmascript/containers/containers_treeset.cpp b/ecmascript/containers/containers_treeset.cpp index 6c8f4ba884a2721632a645dbcdeb7224a24391a9..28a0d34cd7e474e9696f267369264af431295f20 100644 --- a/ecmascript/containers/containers_treeset.cpp +++ b/ecmascript/containers/containers_treeset.cpp @@ -57,6 +57,7 @@ JSTaggedValue ContainersTreeSet::TreeSetConstructor(EcmaRuntimeCallInfo *argv) } if (!compareFn->IsCallable()) { JSHandle result = JSTaggedValue::ToString(thread, compareFn.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"comparefn\" must be callable. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -223,6 +224,7 @@ JSTaggedValue ContainersTreeSet::GetLowerValue(EcmaRuntimeCallInfo *argv) JSHandle key = GetCallArg(argv, 0); if (!key->IsString() && !key->IsNumber()) { JSHandle result = JSTaggedValue::ToString(thread, key.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"key\" must be not null. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -253,6 +255,7 @@ JSTaggedValue ContainersTreeSet::GetHigherValue(EcmaRuntimeCallInfo *argv) JSHandle key = GetCallArg(argv, 0); if (!key->IsString() && !key->IsNumber()) { JSHandle result = JSTaggedValue::ToString(thread, key.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"key\" must be not null. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -365,6 +368,7 @@ JSTaggedValue ContainersTreeSet::ForEach(EcmaRuntimeCallInfo *argv) JSHandle func(GetCallArg(argv, 0)); if (!func->IsCallable()) { JSHandle result = JSTaggedValue::ToString(thread, func.GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"callbackfn\" must be callable. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -374,11 +378,11 @@ JSTaggedValue ContainersTreeSet::ForEach(EcmaRuntimeCallInfo *argv) JSHandle thisArg = GetCallArg(argv, 1); JSHandle tset = JSHandle::Cast(self); JSMutableHandle iteratedSet(thread, tset->GetTreeSet()); - int elements = iteratedSet->NumberOfElements(); + uint32_t elements = iteratedSet->NumberOfElements(); JSHandle entries = TaggedTreeSet::GetArrayFromSet(thread, iteratedSet); - int index = 0; + uint32_t index = 0; size_t length = entries->GetLength(); - const int32_t argsLength = 3; + const uint32_t argsLength = 3; JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); JSMutableHandle key(thread, JSTaggedValue::Undefined()); while (index < elements) { diff --git a/ecmascript/containers/containers_vector.cpp b/ecmascript/containers/containers_vector.cpp index c781921d28488d25fe2a0868a845b67d7dd0501e..948f603d3fa6d7d46ed175aaf5fa1c2231dc601b 100644 --- a/ecmascript/containers/containers_vector.cpp +++ b/ecmascript/containers/containers_vector.cpp @@ -509,7 +509,7 @@ JSTaggedValue ContainersVector::GetSize(EcmaRuntimeCallInfo *argv) } } - int32_t length = JSHandle::Cast(self)->GetSize(); + uint32_t length = JSHandle::Cast(self)->GetSize(); return JSTaggedValue(length); } @@ -684,7 +684,7 @@ JSTaggedValue ContainersVector::CopyToArray(EcmaRuntimeCallInfo *argv) JSHandle newArrayElement = factory->NewAndCopyTaggedArray(vectorElements, vectorLength, vectorLength); array->SetElements(thread, newArrayElement); - array->SetLength(thread, JSTaggedValue(vectorLength)); + array->SetLength(vectorLength); } RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return JSTaggedValue::True(); @@ -710,7 +710,7 @@ JSTaggedValue ContainersVector::ConvertToArray(EcmaRuntimeCallInfo *argv) auto factory = thread->GetEcmaVM()->GetFactory(); JSHandle array = factory->NewJSArray(); - int32_t length = vector->GetSize(); + uint32_t length = vector->GetSize(); array->SetArrayLength(thread, length); JSHandle srcElements(thread, vector->GetElements()); diff --git a/ecmascript/date_parse.cpp b/ecmascript/date_parse.cpp index 61178d2221497995a3fd5dc7b65fc96ec541c978..7f37c5af436636eec61011f88cf32cb0e982acd2 100644 --- a/ecmascript/date_parse.cpp +++ b/ecmascript/date_parse.cpp @@ -236,7 +236,7 @@ bool DateParse::ParseLegacyDates(DateProxy *proxy, DayValue *dayValue, TimeValue return false; } int timeNum = timeNumUnit.GetValue(); - int numLength = timeNumUnit.GetLength(); + uint32_t numLength = timeNumUnit.GetLength(); proxy->NextDate(); // parse +hh:mm if (proxy->GetDate().IsSymbol(':')) { diff --git a/ecmascript/date_parse.h b/ecmascript/date_parse.h index 266e8121ffc44ebd37d196a8f270bffa03f00f97..86b9fcba559e1aa56f8fdb9f4b5fb637ad696baf 100644 --- a/ecmascript/date_parse.h +++ b/ecmascript/date_parse.h @@ -202,7 +202,7 @@ private: { return type_ == DATE_INVALID_WORD; } - + bool IsMonth() const { return type_ == DATE_MONTH; @@ -301,7 +301,7 @@ private: return type_; } - int GetLength() const + uint32_t GetLength() const { return len_; } @@ -309,9 +309,9 @@ private: explicit DateUnit(DateValueType type, int value, int len) : type_(type), value_(value), len_(len) {} DateValueType type_; int value_; - int len_; + uint32_t len_; }; - + class DateProxy { public: explicit DateProxy(StringReader *str) : str_(str), date_(Read()) {} @@ -422,7 +422,7 @@ private: static int NormMilliSecond(DateUnit sec) { - int len = sec.GetLength(); + uint32_t len = sec.GetLength(); int value = sec.GetValue(); // 3: "sss" norm length if (len == 3) { diff --git a/ecmascript/debugger/debugger_api.cpp b/ecmascript/debugger/debugger_api.cpp index 65e2e8a22548eb7acf51d631b850fc2fd01b370d..185d2ab0e75a23b2f35fd58ba533c1e445d0f5ad 100644 --- a/ecmascript/debugger/debugger_api.cpp +++ b/ecmascript/debugger/debugger_api.cpp @@ -31,12 +31,44 @@ #include "ecmascript/napi/jsnapi_helper.h" #include "ecmascript/tagged_array.h" #include "ecmascript/tagged_dictionary.h" +#include "ecmascript/tagged_hash_array.h" +#include "ecmascript/tagged_tree.h" +#include "ecmascript/tagged_queue.h" +#include "ecmascript/js_api/js_api_hashmap.h" +#include "ecmascript/js_api/js_api_hashset.h" +#include "ecmascript/js_api/js_api_tree_map.h" +#include "ecmascript/js_api/js_api_tree_set.h" +#include "ecmascript/js_api/js_api_lightweightmap.h" +#include "ecmascript/js_api/js_api_lightweightset.h" +#include "ecmascript/jobs/micro_job_queue.h" +#include "ecmascript/frames.h" namespace panda::ecmascript::tooling { using panda::ecmascript::base::ALLOW_BINARY; using panda::ecmascript::base::ALLOW_HEX; using panda::ecmascript::base::ALLOW_OCTAL; using panda::ecmascript::base::NumberHelper; +using ecmascript::JSAPIArrayList; +using ecmascript::JSAPIDeque; +using ecmascript::JSAPIHashMap; +using ecmascript::JSAPIHashSet; +using ecmascript::JSAPILightWeightMap; +using ecmascript::JSAPILightWeightSet; +using ecmascript::JSAPIList; +using ecmascript::JSAPILinkedList; +using ecmascript::JSAPIPlainArray; +using ecmascript::JSAPIStack; +using ecmascript::JSAPIQueue; +using ecmascript::JSAPITreeSet; +using ecmascript::JSAPITreeMap; +using ecmascript::JSAPIVector; +using ecmascript::LinkedNode; +using ecmascript::TaggedHashArray; +using ecmascript::TaggedNode; +using ecmascript::RBTreeNode; +using ecmascript::TaggedTreeSet; +using ecmascript::TaggedTreeMap; +using ecmascript::TaggedQueue; // FrameHandler uint32_t DebuggerApi::GetStackDepth(const EcmaVM *ecmaVm) @@ -76,6 +108,22 @@ bool DebuggerApi::StackWalker(const EcmaVM *ecmaVm, std::functionGetJSThread()); + for (; frameHandler.HasFrame(); frameHandler.PrevJSFrame()) { + if (frameHandler.IsEntryFrame()) { + continue; + } + if (frameHandler.IsBuiltinFrame()) { + break; + } + ++count; + } + return count; +} + uint32_t DebuggerApi::GetBytecodeOffset(const EcmaVM *ecmaVm) { return FrameHandler(ecmaVm->GetJSThread()).GetBytecodeOffset(); @@ -149,11 +197,12 @@ int32_t DebuggerApi::GetVregIndex(const FrameHandler *frameHandler, std::string_ return -1; } auto table = extractor->GetLocalVariableTable(method->GetMethodId()); - auto iter = table.find(name.data()); - if (iter == table.end()) { - return -1; + for (auto iter = table.begin(); iter != table.end(); iter++) { + if (iter->name == name.data()) { + return iter->regNumber; + } } - return iter->second; + return -1; } Local DebuggerApi::GetVRegValue(const EcmaVM *ecmaVm, @@ -332,8 +381,7 @@ JSTaggedValue DebuggerApi::GetCurrentModule(const EcmaVM *ecmaVm) if (method->IsNativeWithCallField()) { continue; } - JSTaggedValue func = frameHandler.GetFunction(); - JSTaggedValue module = JSFunction::Cast(func.GetTaggedObject())->GetModule(); + JSTaggedValue module = method->GetModule(); if (module.IsUndefined()) { continue; } @@ -345,18 +393,22 @@ JSTaggedValue DebuggerApi::GetCurrentModule(const EcmaVM *ecmaVm) JSHandle DebuggerApi::GetImportModule(const EcmaVM *ecmaVm, const JSHandle ¤tModule, std::string &name) { + JSThread *thread = ecmaVm->GetJSThread(); + JSMutableHandle importModule(thread, thread->GlobalConstants()->GetUndefined()); + if (!currentModule->IsSourceTextModule()) { + return importModule; + } + JSTaggedValue importEntries = SourceTextModule::Cast(currentModule->GetTaggedObject())->GetImportEntries(); if (importEntries.IsUndefined()) { - return currentModule; + return importModule; } - JSThread *thread = ecmaVm->GetJSThread(); JSHandle importArray(thread, TaggedArray::Cast(importEntries.GetTaggedObject())); size_t importEntriesLen = importArray->GetLength(); JSHandle starString = thread->GlobalConstants()->GetHandledStarString(); JSMutableHandle ee(thread, thread->GlobalConstants()->GetUndefined()); JSMutableHandle environment(thread, thread->GlobalConstants()->GetUndefined()); - JSMutableHandle importModule(thread, thread->GlobalConstants()->GetUndefined()); for (size_t idx = 0; idx < importEntriesLen; idx++) { ee.Update(importArray->Get(idx)); JSTaggedValue localName = ee->GetLocalName(); @@ -377,12 +429,15 @@ JSHandle DebuggerApi::GetImportModule(const EcmaVM *ecmaVm, return importModule; } } - return currentModule; + return importModule; } int32_t DebuggerApi::GetModuleVariableIndex(const EcmaVM *ecmaVm, const JSHandle ¤tModule, std::string &name) { + if (!currentModule->IsSourceTextModule()) { + return -1; + } JSTaggedValue dictionary = SourceTextModule::Cast(currentModule->GetTaggedObject())->GetNameDictionary(); if (dictionary.IsUndefined()) { return -1; @@ -415,34 +470,37 @@ int32_t DebuggerApi::GetModuleVariableIndex(const EcmaVM *ecmaVm, const JSHandle int32_t DebuggerApi::GetRequestModuleIndex(const EcmaVM *ecmaVm, JSTaggedValue moduleRequest, const JSHandle ¤tModule) { + if (!currentModule->IsSourceTextModule()) { + return -1; + } JSThread *thread = ecmaVm->GetJSThread(); JSHandle module(thread, SourceTextModule::Cast(currentModule->GetTaggedObject())); JSHandle required(thread, moduleRequest); - JSHandle requiredModule = JSHandle::Cast( - SourceTextModule::HostResolveImportedModuleWithMerge(thread, module, required)); - JSTaggedValue requireModule = requiredModule->GetEcmaModuleRecordName(); JSHandle requestedModules(thread, module->GetRequestedModules()); - int32_t requestedModulesLen = static_cast(requestedModules->GetLength()); - for (int32_t idx = 0; idx < requestedModulesLen; idx++) { + uint32_t requestedModulesLen = requestedModules->GetLength(); + for (uint32_t idx = 0; idx < requestedModulesLen; idx++) { JSTaggedValue requestModule = requestedModules->Get(idx); - if (JSTaggedValue::SameValue(requireModule, requestModule)) { + if (JSTaggedValue::SameValue(required.GetTaggedValue(), requestModule)) { return idx; } } + LOG_ECMA(FATAL) << "this branch is unreachable"; return -1; } -Local DebuggerApi::GetModuleValue(const EcmaVM *ecmaVm, const JSHandle ¤tModule, - std::string &name) +Local DebuggerApi::GetExportVariableValue(const EcmaVM *ecmaVm, + const JSHandle ¤tModule, std::string &name) { Local result; - JSHandle module = GetImportModule(ecmaVm, currentModule, name); - int32_t index = GetModuleVariableIndex(ecmaVm, module, name); + if (!currentModule->IsSourceTextModule()) { + return result; + } + int32_t index = GetModuleVariableIndex(ecmaVm, currentModule, name); if (index == -1) { return result; } - JSTaggedValue dictionary = SourceTextModule::Cast(module->GetTaggedObject())->GetNameDictionary(); + JSTaggedValue dictionary = SourceTextModule::Cast(currentModule->GetTaggedObject())->GetNameDictionary(); if (dictionary.IsUndefined()) { return result; } @@ -457,16 +515,18 @@ Local DebuggerApi::GetModuleValue(const EcmaVM *ecmaVm, const JSHand return result; } -bool DebuggerApi::SetModuleValue(const EcmaVM *ecmaVm, const JSHandle ¤tModule, - std::string &name, Local value) +bool DebuggerApi::SetExportVariableValue(const EcmaVM *ecmaVm, const JSHandle ¤tModule, + std::string &name, Local value) { - JSHandle module = GetImportModule(ecmaVm, currentModule, name); - int32_t index = GetModuleVariableIndex(ecmaVm, module, name); + if (!currentModule->IsSourceTextModule()) { + return false; + } + int32_t index = GetModuleVariableIndex(ecmaVm, currentModule, name); if (index == -1) { return false; } - JSTaggedValue dictionary = SourceTextModule::Cast(module->GetTaggedObject())->GetNameDictionary(); + JSTaggedValue dictionary = SourceTextModule::Cast(currentModule->GetTaggedObject())->GetNameDictionary(); if (dictionary.IsUndefined()) { return false; } @@ -476,13 +536,56 @@ bool DebuggerApi::SetModuleValue(const EcmaVM *ecmaVm, const JSHandleSet(thread, index, curValue); + return true; } - return true; + return false; +} + +Local DebuggerApi::GetModuleValue(const EcmaVM *ecmaVm, const JSHandle ¤tModule, + std::string &name) +{ + Local result; + if (!currentModule->IsSourceTextModule()) { + return result; + } + // Get variable from local export + result = GetExportVariableValue(ecmaVm, currentModule, name); + if (!result.IsEmpty()) { + return result; + } + // Get variable from import module + JSHandle importModule = GetImportModule(ecmaVm, currentModule, name); + result = GetExportVariableValue(ecmaVm, importModule, name); + return result; +} + +bool DebuggerApi::SetModuleValue(const EcmaVM *ecmaVm, const JSHandle ¤tModule, + std::string &name, Local value) +{ + bool result; + if (!currentModule->IsSourceTextModule()) { + return false; + } + // Set local export variable + result = SetExportVariableValue(ecmaVm, currentModule, name, value); + if (result == true) { + return result; + } + // Set import module variable + JSHandle importModule = GetImportModule(ecmaVm, currentModule, name); + result = SetExportVariableValue(ecmaVm, importModule, name, value); + if (result == true) { + return result; + } + return false; } void DebuggerApi::InitializeExportVariables(const EcmaVM *ecmaVm, Local &moduleObj, const JSHandle ¤tModule) { + if (!currentModule->IsSourceTextModule()) { + return; + } JSTaggedValue localExportEntries = SourceTextModule::Cast( currentModule->GetTaggedObject())->GetLocalExportEntries(); if (localExportEntries.IsUndefined()) { @@ -513,6 +616,9 @@ void DebuggerApi::InitializeExportVariables(const EcmaVM *ecmaVm, Local &moduleObj, const JSHandle ¤tModule, bool isImportStar) { + if (!currentModule->IsSourceTextModule()) { + return; + } JSTaggedValue dictionary = SourceTextModule::Cast(currentModule->GetTaggedObject())->GetNameDictionary(); if (dictionary.IsUndefined()) { InitializeExportVariables(ecmaVm, moduleObj, currentModule); @@ -562,6 +668,9 @@ void DebuggerApi::GetLocalExportVariables(const EcmaVM *ecmaVm, Local void DebuggerApi::GetIndirectExportVariables(const EcmaVM *ecmaVm, Local &moduleObj, const JSHandle ¤tModule) { + if (!currentModule->IsSourceTextModule()) { + return; + } JSTaggedValue indirectExportEntries = SourceTextModule::Cast( currentModule->GetTaggedObject())->GetIndirectExportEntries(); if (indirectExportEntries.IsUndefined()) { @@ -579,12 +688,15 @@ void DebuggerApi::GetIndirectExportVariables(const EcmaVM *ecmaVm, Local variableName = JSNApiHelper::ToLocal(name); - JSTaggedValue moduleRequest = ee->GetModuleRequest(); - int32_t index = GetRequestModuleIndex(ecmaVm, moduleRequest, currentModule); - JSTaggedValue importNamespace = - thread->GetCurrentEcmaContext()->GetModuleManager()->GetModuleNamespace(index); - JSHandle importModule(thread, - ModuleNamespace::Cast(importNamespace.GetTaggedObject())->GetModule()); + JSHandle moduleRequest(thread, ee->GetModuleRequest()); + JSHandle importModule; + JSHandle module = JSHandle::Cast(currentModule); + JSTaggedValue moduleRecordName = module->GetEcmaModuleRecordName(); + if (moduleRecordName.IsUndefined()) { + importModule = SourceTextModule::HostResolveImportedModule(thread, module, moduleRequest); + } else { + importModule = SourceTextModule::HostResolveImportedModuleWithMerge(thread, module, moduleRequest); + } std::string importName = EcmaStringAccessor(ee->GetImportName()).ToStdString(); Local value = GetModuleValue(ecmaVm, importModule, importName); PropertyAttribute descriptor(value, true, true, true); @@ -596,38 +708,35 @@ void DebuggerApi::GetIndirectExportVariables(const EcmaVM *ecmaVm, Local &moduleObj, const JSHandle ¤tModule) { - JSTaggedValue importEntries = SourceTextModule::Cast(currentModule->GetTaggedObject())->GetImportEntries(); - if (importEntries.IsUndefined()) { + if (!currentModule->IsSourceTextModule()) { return; } - - JSTaggedValue moduleEnvironment = SourceTextModule::Cast(currentModule->GetTaggedObject())->GetEnvironment(); - if (moduleEnvironment.IsUndefined()) { + JSTaggedValue importEntries = SourceTextModule::Cast(currentModule->GetTaggedObject())->GetImportEntries(); + if (importEntries.IsUndefined()) { return; } JSThread *thread = ecmaVm->GetJSThread(); JSHandle importArray(thread, TaggedArray::Cast(importEntries.GetTaggedObject())); - int32_t importEntriesLen = static_cast(importArray->GetLength()); - JSHandle environment(thread, TaggedArray::Cast(moduleEnvironment.GetTaggedObject())); + uint32_t importEntriesLen = importArray->GetLength(); JSHandle starString = thread->GlobalConstants()->GetHandledStarString(); JSMutableHandle ee(thread, thread->GlobalConstants()->GetUndefined()); JSMutableHandle name(thread, thread->GlobalConstants()->GetUndefined()); - for (int32_t idx = 0; idx < importEntriesLen; idx++) { + for (uint32_t idx = 0; idx < importEntriesLen; idx++) { ee.Update(importArray->Get(idx)); JSTaggedValue key = ee->GetImportName(); JSTaggedValue localName = ee->GetLocalName(); name.Update(localName); - if (!key.IsString()) { - continue; - } if (JSTaggedValue::SameValue(key, starString.GetTaggedValue())) { - JSTaggedValue moduleRequest = ee->GetModuleRequest(); - int32_t index = GetRequestModuleIndex(ecmaVm, moduleRequest, currentModule); - JSTaggedValue importNamespace = - thread->GetCurrentEcmaContext()->GetModuleManager()->GetModuleNamespace(index); - JSHandle importModule(thread, - ModuleNamespace::Cast(importNamespace.GetTaggedObject())->GetModule()); + JSHandle moduleRequest(thread, ee->GetModuleRequest()); + JSHandle importModule; + JSHandle module = JSHandle::Cast(currentModule); + JSTaggedValue moduleRecordName = module->GetEcmaModuleRecordName(); + if (moduleRecordName.IsUndefined()) { + importModule = SourceTextModule::HostResolveImportedModule(thread, module, moduleRequest); + } else { + importModule = SourceTextModule::HostResolveImportedModuleWithMerge(thread, module, moduleRequest); + } Local importModuleObj = ObjectRef::New(ecmaVm); GetLocalExportVariables(ecmaVm, importModuleObj, importModule, true); Local variableName = JSNApiHelper::ToLocal(name); @@ -635,14 +744,9 @@ void DebuggerApi::GetImportVariables(const EcmaVM *ecmaVm, Local &mod moduleObj->DefineProperty(ecmaVm, variableName, descriptor); continue; } - JSTaggedValue resolvedBinding = environment->Get(idx); - if (resolvedBinding.IsHole()) { - continue; - } - ResolvedIndexBinding *binding = ResolvedIndexBinding::Cast(resolvedBinding.GetTaggedObject()); - JSHandle importModule(thread, binding->GetModule()); - std::string importName = EcmaStringAccessor(key).ToStdString(); - Local value = GetModuleValue(ecmaVm, importModule, importName); + JSTaggedValue moduleValue = + thread->GetCurrentEcmaContext()->GetModuleManager()->GetModuleValueOutter(idx, currentModule); + Local value = JSNApiHelper::ToLocal(JSHandle(thread, moduleValue)); Local variableName = JSNApiHelper::ToLocal(name); PropertyAttribute descriptor(value, true, true, true); moduleObj->DefineProperty(ecmaVm, variableName, descriptor); @@ -656,6 +760,7 @@ void DebuggerApi::HandleUncaughtException(const EcmaVM *ecmaVm, std::string &mes const GlobalEnvConstants *globalConst = thread->GlobalConstants(); JSHandle exHandle(thread, thread->GetException()); + thread->ClearException(); if (exHandle->IsJSError()) { JSHandle nameKey = globalConst->GetHandledNameString(); JSHandle name(JSObject::GetProperty(thread, exHandle, nameKey).GetValue()); @@ -666,7 +771,6 @@ void DebuggerApi::HandleUncaughtException(const EcmaVM *ecmaVm, std::string &mes JSHandle ecmaStr = JSTaggedValue::ToString(thread, exHandle); message = ConvertToString(*ecmaStr); } - thread->ClearException(); } Local DebuggerApi::GenerateFuncFromBuffer(const EcmaVM *ecmaVm, const void *buffer, @@ -730,4 +834,398 @@ const JSPandaFile *DebuggerApi::GetBaseJSPandaFile(const EcmaVM *ecmaVm, const J return hotReloadManager->GetBaseJSPandaFile(jsPandaFile); } +std::vector DebuggerApi::GetNativePointer(const EcmaVM *ecmaVm) +{ + void *native = nullptr; + std::vector nativePointer; + JSThread *thread = ecmaVm->GetJSThread(); + JSTaggedType *current = const_cast(thread->GetCurrentFrame()); + FrameIterator it(current, thread); + for (; !it.Done(); it.Advance()) { + if (!it.IsJSFrame()) { + continue; + } + auto method = it.CheckAndGetMethod(); + if (method == nullptr) { + continue; + } + + if (method->IsNativeWithCallField()) { + JSTaggedValue function = it.GetFunction(); + JSHandle extraInfoValue( + thread, JSFunction::Cast(function.GetTaggedObject())->GetFunctionExtraInfo()); + auto cb = ecmaVm->GetNativePtrGetter(); + if (extraInfoValue->IsJSNativePointer() && cb != nullptr) { + JSHandle extraInfo(extraInfoValue); + native = cb(reinterpret_cast(extraInfo->GetData())); + nativePointer.push_back(native); + } + } else { + nativePointer.push_back(nullptr); // to tell IDE this frame don't hava nativePointer + } + } + return nativePointer; +} + +uint32_t DebuggerApi::GetContainerLength(const EcmaVM *ecmaVm, Local value) +{ + uint32_t length = Local(value)->Length(ecmaVm); + return length; +} + +void DebuggerApi::AddInternalProperties(const EcmaVM *ecmaVm, Local object, + ArkInternalValueType type, Global internalObjects) +{ + if (internalObjects.IsEmpty()) { + return; + } + internalObjects->Set(ecmaVm, object, NumberRef::New(ecmaVm, static_cast(type))); +} + +Local DebuggerApi::GetArrayListValue(const EcmaVM *ecmaVm, Local value, + Global internalObjects) +{ + JSHandle arrayList(JSNApiHelper::ToJSHandle(value)); + uint32_t size = static_cast(arrayList->GetSize()); + Local jsValueRef = ArrayRef::New(ecmaVm, size); + JSThread *thread = ecmaVm->GetJSThread(); + JSMutableHandle currentValue(thread, JSTaggedValue::Undefined()); + uint32_t index = 0; + while (index < size) { + currentValue.Update(arrayList->Get(thread, index)); + ArrayRef::SetValueAt(ecmaVm, jsValueRef, index++, JSNApiHelper::ToLocal(currentValue)); + } + AddInternalProperties(ecmaVm, jsValueRef, ArkInternalValueType::Entry, internalObjects); + return jsValueRef; +} + +Local DebuggerApi::GetDequeValue(const EcmaVM *ecmaVm, Local value, + Global internalObjects) +{ + JSHandle deque(JSNApiHelper::ToJSHandle(value)); + uint32_t size = static_cast(deque->GetSize()); + Local jsValueRef = ArrayRef::New(ecmaVm, size); + JSThread *thread = ecmaVm->GetJSThread(); + JSMutableHandle currentValue(thread, JSTaggedValue::Undefined()); + uint32_t index = 0; + while (index < size) { + currentValue.Update(deque->Get(index)); + ArrayRef::SetValueAt(ecmaVm, jsValueRef, index++, JSNApiHelper::ToLocal(currentValue)); + } + AddInternalProperties(ecmaVm, jsValueRef, ArkInternalValueType::Entry, internalObjects); + return jsValueRef; +} + +Local DebuggerApi::GetHashMapValue(const EcmaVM *ecmaVm, Local value, + Global internalObjects) +{ + JSHandle hashMap(JSNApiHelper::ToJSHandle(value)); + JSThread *thread = ecmaVm->GetJSThread(); + JSHandle table(thread, hashMap->GetTable()); + uint32_t length = table->GetLength(); + uint32_t size = static_cast(hashMap->GetSize()); + Local jsValueRef = ArrayRef::New(ecmaVm, size); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle queue(thread, factory->NewTaggedQueue(0)); + JSMutableHandle node(thread, JSTaggedValue::Undefined()); + JSMutableHandle currentKey(thread, JSTaggedValue::Undefined()); + JSMutableHandle currentValue(thread, JSTaggedValue::Undefined()); + Local jsKey = StringRef::NewFromUtf8(ecmaVm, "key"); + Local jsValue = StringRef::NewFromUtf8(ecmaVm, "value"); + uint32_t pos = 0; + uint32_t index = 0; + while (index < length) { + node.Update(TaggedHashArray::GetCurrentNode(thread, queue, table, index)); + if (!node.GetTaggedValue().IsHole()) { + currentKey.Update(node->GetKey()); + currentValue.Update(node->GetValue()); + Local objRef = ObjectRef::New(ecmaVm); + objRef->Set(ecmaVm, jsKey, JSNApiHelper::ToLocal(currentKey)); + objRef->Set(ecmaVm, jsValue, JSNApiHelper::ToLocal(currentValue)); + AddInternalProperties(ecmaVm, objRef, ArkInternalValueType::Entry, internalObjects); + ArrayRef::SetValueAt(ecmaVm, jsValueRef, pos++, objRef); + } + } + AddInternalProperties(ecmaVm, jsValueRef, ArkInternalValueType::Entry, internalObjects); + return jsValueRef; +} + +Local DebuggerApi::GetHashSetValue(const EcmaVM *ecmaVm, Local value, + Global internalObjects) +{ + JSHandle hashSet(JSNApiHelper::ToJSHandle(value)); + JSThread *thread = ecmaVm->GetJSThread(); + JSHandle table(thread, hashSet->GetTable()); + uint32_t length = table->GetLength(); + uint32_t size = static_cast(hashSet->GetSize()); + Local jsValueRef = ArrayRef::New(ecmaVm, size); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSMutableHandle queue(thread, factory->NewTaggedQueue(0)); + JSMutableHandle node(thread, JSTaggedValue::Undefined()); + JSMutableHandle currentKey(thread, JSTaggedValue::Undefined()); + Local jsValue = StringRef::NewFromUtf8(ecmaVm, "value"); + uint32_t pos = 0; + uint32_t index = 0; + while (index < length) { + node.Update(TaggedHashArray::GetCurrentNode(thread, queue, table, index)); + if (!node.GetTaggedValue().IsHole()) { + currentKey.Update(node->GetKey()); + if (currentKey->IsECMAObject()) { + Local objRef = ObjectRef::New(ecmaVm); + objRef->Set(ecmaVm, jsValue, JSNApiHelper::ToLocal(currentKey)); + AddInternalProperties(ecmaVm, objRef, ArkInternalValueType::Entry, internalObjects); + ArrayRef::SetValueAt(ecmaVm, jsValueRef, pos++, objRef); + } else { + ArrayRef::SetValueAt(ecmaVm, jsValueRef, pos++, JSNApiHelper::ToLocal(currentKey)); + } + } + } + AddInternalProperties(ecmaVm, jsValueRef, ArkInternalValueType::Entry, internalObjects); + return jsValueRef; +} + +Local DebuggerApi::GetLightWeightMapValue(const EcmaVM *ecmaVm, Local value, + Global internalObjects) +{ + JSHandle lightweightMap(JSNApiHelper::ToJSHandle(value)); + uint32_t size = static_cast(lightweightMap->GetSize()); + Local jsValueRef = ArrayRef::New(ecmaVm, size); + JSThread *thread = ecmaVm->GetJSThread(); + JSMutableHandle keys(thread, lightweightMap->GetKeys()); + JSMutableHandle values(thread, lightweightMap->GetValues()); + JSMutableHandle currentKey(thread, JSTaggedValue::Undefined()); + JSMutableHandle currentValue(thread, JSTaggedValue::Undefined()); + Local jsKey = StringRef::NewFromUtf8(ecmaVm, "key"); + Local jsValue = StringRef::NewFromUtf8(ecmaVm, "value"); + uint32_t index = 0; + while (index < size) { + currentKey.Update(keys->Get(index)); + currentValue.Update(values->Get(index)); + Local objRef = ObjectRef::New(ecmaVm); + objRef->Set(ecmaVm, jsKey, JSNApiHelper::ToLocal(currentKey)); + objRef->Set(ecmaVm, jsValue, JSNApiHelper::ToLocal(currentValue)); + AddInternalProperties(ecmaVm, objRef, ArkInternalValueType::Entry, internalObjects); + ArrayRef::SetValueAt(ecmaVm, jsValueRef, index++, objRef); + } + AddInternalProperties(ecmaVm, jsValueRef, ArkInternalValueType::Entry, internalObjects); + return jsValueRef; +} + +Local DebuggerApi::GetLightWeightSetValue(const EcmaVM *ecmaVm, Local value, + Global internalObjects) +{ + JSHandle lightWeightSet(JSNApiHelper::ToJSHandle(value)); + uint32_t size = static_cast(lightWeightSet->GetSize()); + Local jsValueRef = ArrayRef::New(ecmaVm, size); + JSThread *thread = ecmaVm->GetJSThread(); + JSMutableHandle currentValue(thread, JSTaggedValue::Undefined()); + Local jsValue = StringRef::NewFromUtf8(ecmaVm, "value"); + uint32_t index = 0; + while (index < size) { + currentValue.Update(lightWeightSet->GetValueAt(index)); + if (currentValue->IsECMAObject()) { + Local objRef = ObjectRef::New(ecmaVm); + objRef->Set(ecmaVm, jsValue, JSNApiHelper::ToLocal(currentValue)); + AddInternalProperties(ecmaVm, objRef, ArkInternalValueType::Entry, internalObjects); + ArrayRef::SetValueAt(ecmaVm, jsValueRef, index++, objRef); + } else { + ArrayRef::SetValueAt(ecmaVm, jsValueRef, index++, JSNApiHelper::ToLocal(currentValue)); + } + } + AddInternalProperties(ecmaVm, jsValueRef, ArkInternalValueType::Entry, internalObjects); + return jsValueRef; +} + +Local DebuggerApi::GetLinkedListValue(const EcmaVM *ecmaVm, Local value, + Global internalObjects) +{ + JSHandle linkedList(JSNApiHelper::ToJSHandle(value)); + JSThread *thread = ecmaVm->GetJSThread(); + JSHandle doubleList(thread, linkedList->GetDoubleList()); + uint32_t size = static_cast(linkedList->Length()); + Local jsValueRef = ArrayRef::New(ecmaVm, size); + JSMutableHandle currentValue(thread, JSTaggedValue::Undefined()); + int valueNode = TaggedDoubleList::ELEMENTS_START_INDEX; + uint32_t index = 0; + while (index < size) { + valueNode = doubleList->GetNextDataIndex(valueNode); + currentValue.Update(doubleList->GetElement(valueNode)); + ArrayRef::SetValueAt(ecmaVm, jsValueRef, index++, JSNApiHelper::ToLocal(currentValue)); + } + AddInternalProperties(ecmaVm, jsValueRef, ArkInternalValueType::Entry, internalObjects); + return jsValueRef; +} + +Local DebuggerApi::GetListValue(const EcmaVM *ecmaVm, Local value, + Global internalObjects) +{ + JSHandle list(JSNApiHelper::ToJSHandle(value)); + JSThread *thread = ecmaVm->GetJSThread(); + JSHandle singleList(thread, list->GetSingleList()); + uint32_t size = static_cast(list->Length()); + Local jsValueRef = ArrayRef::New(ecmaVm, size); + JSMutableHandle currentValue(thread, JSTaggedValue::Undefined()); + int valueNode = TaggedDoubleList::ELEMENTS_START_INDEX; + uint32_t index = 0; + while (index < size) { + valueNode = singleList->GetNextDataIndex(valueNode); + currentValue.Update(singleList->GetElement(valueNode)); + ArrayRef::SetValueAt(ecmaVm, jsValueRef, index++, JSNApiHelper::ToLocal(currentValue)); + } + AddInternalProperties(ecmaVm, jsValueRef, ArkInternalValueType::Entry, internalObjects); + return jsValueRef; +} + +Local DebuggerApi::GetPlainArrayValue(const EcmaVM *ecmaVm, Local value, + Global internalObjects) +{ + JSHandle plainarray(JSNApiHelper::ToJSHandle(value)); + uint32_t size = static_cast(plainarray->GetSize()); + Local jsValueRef = ArrayRef::New(ecmaVm, size); + JSThread *thread = ecmaVm->GetJSThread(); + JSHandle keyArray(thread, plainarray->GetKeys()); + JSHandle valueArray(thread, plainarray->GetValues()); + JSMutableHandle currentKey(thread, JSTaggedValue::Undefined()); + JSMutableHandle currentValue(thread, JSTaggedValue::Undefined()); + Local jsKey = StringRef::NewFromUtf8(ecmaVm, "key"); + Local jsValue = StringRef::NewFromUtf8(ecmaVm, "value"); + uint32_t index = 0; + while (index < size) { + currentKey.Update(keyArray->Get(index)); + currentValue.Update(valueArray->Get(index)); + Local objRef = ObjectRef::New(ecmaVm); + objRef->Set(ecmaVm, jsKey, JSNApiHelper::ToLocal(currentKey)); + objRef->Set(ecmaVm, jsValue, JSNApiHelper::ToLocal(currentValue)); + AddInternalProperties(ecmaVm, objRef, ArkInternalValueType::Entry, internalObjects); + ArrayRef::SetValueAt(ecmaVm, jsValueRef, index++, objRef); + } + AddInternalProperties(ecmaVm, jsValueRef, ArkInternalValueType::Entry, internalObjects); + return jsValueRef; +} + +Local DebuggerApi::GetQueueValue(const EcmaVM *ecmaVm, Local value, + Global internalObjects) +{ + JSHandle queue(JSNApiHelper::ToJSHandle(value)); + uint32_t size = static_cast(queue->GetSize()); + Local jsValueRef = ArrayRef::New(ecmaVm, size); + JSThread *thread = ecmaVm->GetJSThread(); + JSMutableHandle currentValue(thread, JSTaggedValue::Undefined()); + uint32_t pos = 0; + uint32_t index = 0; + while (index < size) { + currentValue.Update(queue->Get(thread, pos)); + pos = queue->GetNextPosition(pos); + ArrayRef::SetValueAt(ecmaVm, jsValueRef, index++, JSNApiHelper::ToLocal(currentValue)); + } + AddInternalProperties(ecmaVm, jsValueRef, ArkInternalValueType::Entry, internalObjects); + return jsValueRef; +} + +Local DebuggerApi::GetStackValue(const EcmaVM *ecmaVm, Local value, + Global internalObjects) +{ + JSHandle stack(JSNApiHelper::ToJSHandle(value)); + uint32_t size = static_cast(stack->GetSize()); + Local jsValueRef = ArrayRef::New(ecmaVm, size); + JSThread *thread = ecmaVm->GetJSThread(); + JSMutableHandle currentValue(thread, JSTaggedValue::Undefined()); + uint32_t index = 0; + while (index < size + 1) { + currentValue.Update(stack->Get(index)); + ArrayRef::SetValueAt(ecmaVm, jsValueRef, index++, JSNApiHelper::ToLocal(currentValue)); + } + AddInternalProperties(ecmaVm, jsValueRef, ArkInternalValueType::Entry, internalObjects); + return jsValueRef; +} + +Local DebuggerApi::GetTreeMapValue(const EcmaVM *ecmaVm, Local value, + Global internalObjects) +{ + JSHandle treeMap(JSNApiHelper::ToJSHandle(value)); + uint32_t size = static_cast(treeMap->GetSize()); + Local jsValueRef = ArrayRef::New(ecmaVm, size); + JSThread *thread = ecmaVm->GetJSThread(); + JSMutableHandle iteratedMap(thread, treeMap->GetTreeMap()); + uint32_t elements = static_cast(iteratedMap->NumberOfElements()); + JSHandle entries = TaggedTreeMap::GetArrayFromMap(thread, iteratedMap); + JSMutableHandle currentKey(thread, JSTaggedValue::Undefined()); + JSMutableHandle currentValue(thread, JSTaggedValue::Undefined()); + Local jsKey = StringRef::NewFromUtf8(ecmaVm, "key"); + Local jsValue = StringRef::NewFromUtf8(ecmaVm, "value"); + uint32_t index = 0; + while (index < elements) { + int entriesIndex = entries->Get(index).GetInt(); + currentKey.Update(iteratedMap->GetKey(entriesIndex)); + currentValue.Update(iteratedMap->GetValue(entriesIndex)); + Local objRef = ObjectRef::New(ecmaVm); + objRef->Set(ecmaVm, jsKey, JSNApiHelper::ToLocal(currentKey)); + objRef->Set(ecmaVm, jsValue, JSNApiHelper::ToLocal(currentValue)); + AddInternalProperties(ecmaVm, objRef, ArkInternalValueType::Entry, internalObjects); + ArrayRef::SetValueAt(ecmaVm, jsValueRef, index++, objRef); + } + AddInternalProperties(ecmaVm, jsValueRef, ArkInternalValueType::Entry, internalObjects); + return jsValueRef; +} + +Local DebuggerApi::GetTreeSetValue(const EcmaVM *ecmaVm, Local value, + Global internalObjects) +{ + JSHandle treeSet(JSNApiHelper::ToJSHandle(value)); + JSThread *thread = ecmaVm->GetJSThread(); + JSMutableHandle iteratedSet(thread, treeSet->GetTreeSet()); + uint32_t elements = static_cast(iteratedSet->NumberOfElements()); + Local jsValueRef = ArrayRef::New(ecmaVm, elements); + JSHandle entries = TaggedTreeSet::GetArrayFromSet(thread, iteratedSet); + JSMutableHandle currentValue(thread, JSTaggedValue::Undefined()); + Local jsValue = StringRef::NewFromUtf8(ecmaVm, "value"); + uint32_t index = 0; + while (index < elements) { + int entriesIndex = entries->Get(index).GetInt(); + currentValue.Update(iteratedSet->GetKey(entriesIndex)); + if (currentValue->IsECMAObject()) { + Local objRef = ObjectRef::New(ecmaVm); + objRef->Set(ecmaVm, jsValue, JSNApiHelper::ToLocal(currentValue)); + AddInternalProperties(ecmaVm, objRef, ArkInternalValueType::Entry, internalObjects); + ArrayRef::SetValueAt(ecmaVm, jsValueRef, index++, objRef); + } else { + ArrayRef::SetValueAt(ecmaVm, jsValueRef, index++, JSNApiHelper::ToLocal(currentValue)); + } + } + AddInternalProperties(ecmaVm, jsValueRef, ArkInternalValueType::Entry, internalObjects); + return jsValueRef; +} + +Local DebuggerApi::GetVectorValue(const EcmaVM *ecmaVm, Local value, + Global internalObjects) +{ + JSHandle vector(JSNApiHelper::ToJSHandle(value)); + uint32_t size = static_cast(vector->GetSize()); + Local jsValueRef = ArrayRef::New(ecmaVm, size); + JSThread *thread = ecmaVm->GetJSThread(); + JSMutableHandle currentValue(thread, JSTaggedValue::Undefined()); + uint32_t index = 0; + while (index < size) { + currentValue.Update(vector->Get(thread, vector, index)); + ArrayRef::SetValueAt(ecmaVm, jsValueRef, index++, JSNApiHelper::ToLocal(currentValue)); + } + AddInternalProperties(ecmaVm, jsValueRef, ArkInternalValueType::Entry, internalObjects); + return jsValueRef; +} + +bool DebuggerApi::CheckPromiseQueueSize(const EcmaVM *ecmaVm) +{ + auto *debuggerMgr = ecmaVm->GetJsDebuggerManager(); + uint32_t queueSizeEntry = debuggerMgr->GetPromiseQueueSizeRecordOfTopFrame(); + JSThread *thread = ecmaVm->GetJSThread(); + EcmaContext *context = thread->GetCurrentEcmaContext(); + uint32_t queueSizeCurrent = job::MicroJobQueue::GetPromiseQueueSize(thread, context->GetMicroJobQueue()); + return queueSizeEntry == queueSizeCurrent; +} + +void DebuggerApi::DropLastFrame(const EcmaVM *ecmaVm) +{ + auto *debuggerMgr = ecmaVm->GetJsDebuggerManager(); + debuggerMgr->DropLastFrame(); +} } // namespace panda::ecmascript::tooling diff --git a/ecmascript/debugger/debugger_api.h b/ecmascript/debugger/debugger_api.h index ac7212a2382e085686658517ed60e697921d1f72..f2e7807e33816e645494113af6237e17a79910c8 100644 --- a/ecmascript/debugger/debugger_api.h +++ b/ecmascript/debugger/debugger_api.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_TOOLING_BACKEND_DEBUGGER_API_H -#define ECMASCRIPT_TOOLING_BACKEND_DEBUGGER_API_H +#ifndef ECMASCRIPT_DEBUGGER_DEBUGGER_API_H +#define ECMASCRIPT_DEBUGGER_DEBUGGER_API_H #include @@ -48,12 +48,14 @@ enum StackState { SUCCESS, }; +enum class ArkInternalValueType {None, Entry, Scope, ScopeList}; class PUBLIC_API DebuggerApi { public: // FrameHandler static uint32_t GetStackDepth(const EcmaVM *ecmaVm); static std::shared_ptr NewFrameHandler(const EcmaVM *ecmaVm); static bool StackWalker(const EcmaVM *ecmaVm, std::function func); + static uint32_t GetStackDepthOverBuiltin(const EcmaVM *ecmaVm); static uint32_t GetBytecodeOffset(const EcmaVM *ecmaVm); static uint32_t GetBytecodeOffset(const FrameHandler *frameHandler); @@ -87,6 +89,10 @@ public: std::string &name); static int32_t GetRequestModuleIndex(const EcmaVM *ecmaVm, const JSTaggedValue moduleRequest, const JSHandle ¤tModule); + static Local GetExportVariableValue(const EcmaVM *ecmaVm, const JSHandle ¤tModule, + std::string &name); + static bool SetExportVariableValue(const EcmaVM *ecmaVm, const JSHandle ¤tModule, + std::string &name, Local value); static Local GetModuleValue(const EcmaVM *ecmaVm, const JSHandle ¤tModule, std::string &name); static bool SetModuleValue(const EcmaVM *ecmaVm, const JSHandle ¤tModule, @@ -111,19 +117,56 @@ public: static void DestroyJSDebugger(JSDebugger *debugger); static void RegisterHooks(JSDebugger *debugger, PtHooks *hooks); static bool SetBreakpoint(JSDebugger *debugger, const JSPtLocation &location, - Local condFuncRef); + Local condFuncRef); static bool RemoveBreakpoint(JSDebugger *debugger, const JSPtLocation &location); static void RemoveAllBreakpoints(JSDebugger *debugger); static void HandleUncaughtException(const EcmaVM *ecmaVm, std::string &message); static Local EvaluateViaFuncCall(EcmaVM *ecmaVm, Local funcRef, - std::shared_ptr &frameHandler); + std::shared_ptr &frameHandler); static Local GenerateFuncFromBuffer(const EcmaVM *ecmaVm, const void *buffer, size_t size, - std::string_view entryPoint); + std::string_view entryPoint); // HotReload static DebugInfoExtractor *GetPatchExtractor(const EcmaVM *ecmaVm, const std::string &url); static const JSPandaFile *GetBaseJSPandaFile(const EcmaVM *ecmaVm, const JSPandaFile *jsPandaFile); + static std::vector GetNativePointer(const EcmaVM *ecmaVm); + + // Container + static uint32_t GetContainerLength(const EcmaVM *ecmaVm, Local value); + static void AddInternalProperties(const EcmaVM *ecmaVm, Local object, + ArkInternalValueType type, Global internalObjects); + static Local GetArrayListValue(const EcmaVM *ecmaVm, Local value, + Global internalObjects); + static Local GetDequeValue(const EcmaVM *ecmaVm, Local value, + Global internalObjects); + static Local GetHashMapValue(const EcmaVM *ecmaVm, Local value, + Global internalObjects); + static Local GetHashSetValue(const EcmaVM *ecmaVm, Local value, + Global internalObjects); + static Local GetLightWeightMapValue(const EcmaVM *ecmaVm, Local value, + Global internalObjects); + static Local GetLightWeightSetValue(const EcmaVM *ecmaVm, Local value, + Global internalObjects); + static Local GetLinkedListValue(const EcmaVM *ecmaVm, Local value, + Global internalObjects); + static Local GetListValue(const EcmaVM *ecmaVm, Local value, + Global internalObjects); + static Local GetPlainArrayValue(const EcmaVM *ecmaVm, Local value, + Global internalObjects); + static Local GetQueueValue(const EcmaVM *ecmaVm, Local value, + Global internalObjects); + static Local GetStackValue(const EcmaVM *ecmaVm, Local value, + Global internalObjects); + static Local GetTreeMapValue(const EcmaVM *ecmaVm, Local value, + Global internalObjects); + static Local GetTreeSetValue(const EcmaVM *ecmaVm, Local value, + Global internalObjects); + static Local GetVectorValue(const EcmaVM *ecmaVm, Local value, + Global internalObjects); + + static bool CheckPromiseQueueSize(const EcmaVM *ecmaVm); + static void DropLastFrame(const EcmaVM *ecmaVm); }; } // namespace panda::ecmascript::tooling -#endif // ECMASCRIPT_TOOLING_BACKEND_DEBUGGER_API_H +#endif // ECMASCRIPT_DEBUGGER_DEBUGGER_API_H diff --git a/ecmascript/debugger/dropframe_manager.cpp b/ecmascript/debugger/dropframe_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0fa51a18d8a4a41e6bbde03549dbc0f6f07f6c5b --- /dev/null +++ b/ecmascript/debugger/dropframe_manager.cpp @@ -0,0 +1,267 @@ +/* + * 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/debugger/dropframe_manager.h" + +#include "ecmascript/frames.h" +#include "ecmascript/global_env.h" +#include "ecmascript/global_handle_collection.h" +#include "ecmascript/interpreter/frame_handler.h" +#include "ecmascript/interpreter/interpreter-inl.h" +#include "ecmascript/jobs/micro_job_queue.h" + +namespace panda::ecmascript::tooling { +bool DropframeManager::IsNewlexenvOpcode(BytecodeInstruction::Opcode op) +{ + switch (op) { + case BytecodeInstruction::Opcode::NEWLEXENV_IMM8: + case BytecodeInstruction::Opcode::NEWLEXENVWITHNAME_IMM8_ID16: + case BytecodeInstruction::Opcode::WIDE_NEWLEXENV_PREF_IMM16: + case BytecodeInstruction::Opcode::WIDE_NEWLEXENVWITHNAME_PREF_IMM16_ID16: + return true; + default: + break; + } + return false; +} + +bool DropframeManager::IsStlexvarOpcode(BytecodeInstruction::Opcode op) +{ + switch (op) { + case BytecodeInstruction::Opcode::STLEXVAR_IMM4_IMM4: + case BytecodeInstruction::Opcode::STLEXVAR_IMM8_IMM8: + case BytecodeInstruction::Opcode::WIDE_STLEXVAR_PREF_IMM16_IMM16: + return true; + default: + break; + } + return false; +} + +std::pair DropframeManager::ReadStlexvarParams(const uint8_t *pc, BytecodeInstruction::Opcode op) +{ + uint16_t level = 0; + uint16_t slot = 0; + switch (op) { + case BytecodeInstruction::Opcode::STLEXVAR_IMM4_IMM4: + level = READ_INST_4_0(); + slot = READ_INST_4_1(); + break; + case BytecodeInstruction::Opcode::STLEXVAR_IMM8_IMM8: + level = READ_INST_8_0(); + slot = READ_INST_8_1(); + break; + case BytecodeInstruction::Opcode::WIDE_STLEXVAR_PREF_IMM16_IMM16: + level = READ_INST_16_1(); + slot = READ_INST_16_3(); + break; + default: + break; + } + return std::make_pair(level, slot); +} + +void DropframeManager::MethodEntry(JSThread *thread, JSHandle method, JSHandle envHandle) +{ + std::set> modifiedLexVarPos; + NewLexModifyRecordLevel(); + if (envHandle.GetTaggedValue().IsUndefinedOrNull()) { + return; + } + uint32_t codeSize = method->GetCodeSize(); + uint16_t newEnvCount = 0; + auto bcIns = BytecodeInstruction(method->GetBytecodeArray()); + auto bcInsLast = bcIns.JumpTo(codeSize); + while (bcIns.GetAddress() != bcInsLast.GetAddress()) { + BytecodeInstruction::Opcode op = bcIns.GetOpcode(); + if (IsNewlexenvOpcode(op)) { + newEnvCount++; + } else if (IsStlexvarOpcode(op)) { + std::pair lexVarPos = ReadStlexvarParams(bcIns.GetAddress(), op); + uint16_t level; + uint16_t slot; + std::tie(level, slot) = lexVarPos; + JSTaggedValue env = envHandle.GetTaggedValue(); + for (uint16_t i = 0; ; i++) { + if ((level < newEnvCount || i >= level - newEnvCount) && + slot < LexicalEnv::Cast(env.GetTaggedObject())->GetLength() - LexicalEnv::RESERVED_ENV_LENGTH && + !modifiedLexVarPos.count({i, slot})) { + JSTaggedValue value = LexicalEnv::Cast(env.GetTaggedObject())->GetProperties(slot); + EmplaceLexModifyRecord(thread, env, slot, value); + modifiedLexVarPos.insert({i, slot}); + } + if (i >= level) { + break; + } + JSTaggedValue taggedParentEnv = LexicalEnv::Cast(env.GetTaggedObject())->GetParentEnv(); + if (taggedParentEnv.IsUndefined()) { + break; + } + env = taggedParentEnv; + } + } + bcIns = bcIns.GetNext(); + } + PushPromiseQueueSizeRecord(thread); +} + +void DropframeManager::MethodExit(JSThread *thread, [[maybe_unused]] JSHandle method) +{ + MergeLexModifyRecordOfTopFrame(thread); + PopPromiseQueueSizeRecord(); +} + +void DropframeManager::DropLastFrame(JSThread *thread) +{ + std::vector, uint16_t, JSHandle>> lexModifyRecord; + lexModifyRecord = GetLexModifyRecordOfTopFrame(); + for (const auto &item : lexModifyRecord) { + JSHandle envHandle; + uint16_t slot; + JSHandle valueHandle; + std::tie(envHandle, slot, valueHandle) = item; + JSTaggedValue env = envHandle.GetTaggedValue(); + ASSERT(slot < LexicalEnv::Cast(env.GetTaggedObject())->GetLength() - LexicalEnv::RESERVED_ENV_LENGTH); + LexicalEnv::Cast(env.GetTaggedObject())->SetProperties(thread, slot, valueHandle.GetTaggedValue()); + } + RemoveLexModifyRecordOfTopFrame(thread); + PopPromiseQueueSizeRecord(); + + FrameHandler frameHandler(thread); + bool isEntryFrameDropped = false; + while (frameHandler.HasFrame()) { + frameHandler.PrevJSFrame(); + if (frameHandler.IsEntryFrame()) { + isEntryFrameDropped = true; + continue; + } + if (frameHandler.IsBuiltinFrame()) { + continue; + } + if (!thread->IsAsmInterpreter()) { + JSTaggedType *prevSp = frameHandler.GetSp(); + thread->SetCurrentFrame(prevSp); + } + if (isEntryFrameDropped) { + thread->SetEntryFrameDroppedState(); + } + thread->SetFrameDroppedState(); + break; + } +} + +void DropframeManager::NewLexModifyRecordLevel() +{ + modifiedLexVar_.push(std::vector, uint16_t, JSHandle>>()); +} + +void DropframeManager::EmplaceLexModifyRecord(JSThread *thread, JSTaggedValue env, uint16_t slot, JSTaggedValue value) +{ + GlobalHandleCollection globalHandleCollection(thread); + for (const auto &item : modifiedLexVar_.top()) { + JSHandle envHandleRecord = std::get<0>(item); + uint16_t slotRecord = std::get<1>(item); + if (envHandleRecord.GetTaggedType() == env.GetRawData() && slotRecord == slot) { + return; + } + } + JSHandle envHandle = globalHandleCollection.NewHandle(env.GetRawData()); + JSHandle valueHandle = globalHandleCollection.NewHandle(value.GetRawData()); + modifiedLexVar_.top().emplace_back(envHandle, slot, valueHandle); +} + +uint32_t DropframeManager::GetLexModifyRecordLevel() +{ + return modifiedLexVar_.size(); +} + +std::vector, uint16_t, JSHandle>> + DropframeManager::GetLexModifyRecordOfTopFrame() +{ + if (modifiedLexVar_.empty()) { + return std::vector, uint16_t, JSHandle>>(); + } + return modifiedLexVar_.top(); +} + +void DropframeManager::RemoveLexModifyRecordOfTopFrame(JSThread *thread) +{ + if (modifiedLexVar_.empty()) { + return; + } + GlobalHandleCollection globalHandleCollection(thread); + for (const auto &item : modifiedLexVar_.top()) { + JSHandle envHandle = std::get<0>(item); + JSHandle valueHandle = std::get<2>(item); // 2:get the third item of the tuple + globalHandleCollection.Dispose(envHandle); + globalHandleCollection.Dispose(valueHandle); + } + modifiedLexVar_.pop(); +} + +void DropframeManager::MergeLexModifyRecordOfTopFrame(JSThread *thread) +{ + if (modifiedLexVar_.empty()) { + return; + } + GlobalHandleCollection globalHandleCollection(thread); + std::vector, uint16_t, JSHandle>> lexModifyRecord; + lexModifyRecord = modifiedLexVar_.top(); + modifiedLexVar_.pop(); + for (const auto &item : lexModifyRecord) { + JSHandle envHandle; + uint16_t slot; + JSHandle valueHandle; + std::tie(envHandle, slot, valueHandle) = item; + bool existRecord = false; + if (!modifiedLexVar_.empty()) { + for (const auto &itemLast : modifiedLexVar_.top()) { + JSHandle envHandleRecord = std::get<0>(itemLast); + uint16_t slotRecord = std::get<1>(itemLast); + if (envHandleRecord.GetTaggedType() == envHandle.GetTaggedType() && slotRecord == slot) { + existRecord = true; + break; + } + } + } + if (modifiedLexVar_.empty() || existRecord) { + globalHandleCollection.Dispose(envHandle); + globalHandleCollection.Dispose(valueHandle); + } else { + modifiedLexVar_.top().emplace_back(envHandle, slot, valueHandle); + } + } +} + +void DropframeManager::PushPromiseQueueSizeRecord(JSThread *thread) +{ + EcmaContext *context = thread->GetCurrentEcmaContext(); + uint32_t queueSize = job::MicroJobQueue::GetPromiseQueueSize(thread, context->GetMicroJobQueue()); + promiseQueueSizeRecord_.push(queueSize); +} + +uint32_t DropframeManager::GetPromiseQueueSizeRecordOfTopFrame() +{ + ASSERT(!promiseQueueSizeRecord_.empty()); + return promiseQueueSizeRecord_.top(); +} + +void DropframeManager::PopPromiseQueueSizeRecord() +{ + if (!promiseQueueSizeRecord_.empty()) { + promiseQueueSizeRecord_.pop(); + } +} +} \ No newline at end of file diff --git a/ecmascript/debugger/dropframe_manager.h b/ecmascript/debugger/dropframe_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..7da78936df4e5d809a66d7eb7acec8a053a53dc1 --- /dev/null +++ b/ecmascript/debugger/dropframe_manager.h @@ -0,0 +1,55 @@ +/* + * 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_DEBUGGER_DROPFRAME_MANAGER_H +#define ECMASCRIPT_DEBUGGER_DROPFRAME_MANAGER_H + +#include "ecmascript/js_handle.h" +#include "ecmascript/js_thread.h" +#include "ecmascript/method.h" + +namespace panda::ecmascript::tooling { +class DropframeManager { +public: + DropframeManager() = default; + ~DropframeManager() = default; + NO_COPY_SEMANTIC(DropframeManager); + NO_MOVE_SEMANTIC(DropframeManager); + + void MethodEntry(JSThread *thread, JSHandle method, JSHandle env); + void MethodExit(JSThread *thread, JSHandle method); + void DropLastFrame(JSThread *thread); + uint32_t GetPromiseQueueSizeRecordOfTopFrame(); +private: + bool IsNewlexenvOpcode(BytecodeInstruction::Opcode op); + bool IsStlexvarOpcode(BytecodeInstruction::Opcode op); + std::pair ReadStlexvarParams(const uint8_t *pc, BytecodeInstruction::Opcode op); + + void NewLexModifyRecordLevel(); + void EmplaceLexModifyRecord(JSThread *thread, JSTaggedValue env, uint16_t slot, JSTaggedValue value); + uint32_t GetLexModifyRecordLevel(); + std::vector, uint16_t, JSHandle>> GetLexModifyRecordOfTopFrame(); + void RemoveLexModifyRecordOfTopFrame(JSThread *thread); + void MergeLexModifyRecordOfTopFrame(JSThread *thread); + + void PushPromiseQueueSizeRecord(JSThread *thread); + void PopPromiseQueueSizeRecord(); + + std::stack, uint16_t, JSHandle>>> modifiedLexVar_; + std::stack promiseQueueSizeRecord_; +}; +} + +#endif // ECMASCRIPT_DEBUGGER_DROPFRAME_MANAGER_H \ No newline at end of file diff --git a/ecmascript/debugger/hot_reload_manager.h b/ecmascript/debugger/hot_reload_manager.h index 0c55d0e0afca31cd1faee1e62bd77a13e0c23bad..146f39b707ca32d1add0ab19aa6568838222d856 100644 --- a/ecmascript/debugger/hot_reload_manager.h +++ b/ecmascript/debugger/hot_reload_manager.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_TOOLING_INTERFACE_HOT_RELOAD_MANAGER_H -#define ECMASCRIPT_TOOLING_INTERFACE_HOT_RELOAD_MANAGER_H +#ifndef ECMASCRIPT_DEBUGGER_HOT_RELOAD_MANAGER_H +#define ECMASCRIPT_DEBUGGER_HOT_RELOAD_MANAGER_H #include "ecmascript/jspandafile/js_pandafile.h" #include "ecmascript/jspandafile/debug_info_extractor.h" @@ -42,4 +42,4 @@ private: CUnorderedMap patchExtractors_ {}; }; } // namespace panda::ecmascript::tooling -#endif // ECMASCRIPT_TOOLING_INTERFACE_HOT_RELOAD_MANAGER_H +#endif // ECMASCRIPT_DEBUGGER_HOT_RELOAD_MANAGER_H diff --git a/ecmascript/debugger/js_debugger.cpp b/ecmascript/debugger/js_debugger.cpp index cebff7f5f05523af4818d8d5afd09e1a4222c61a..9945836f648ea09b0312616f7b876f12d6b46f81 100644 --- a/ecmascript/debugger/js_debugger.cpp +++ b/ecmascript/debugger/js_debugger.cpp @@ -21,6 +21,7 @@ #include "ecmascript/interpreter/fast_runtime_stub-inl.h" #include "ecmascript/interpreter/frame_handler.h" #include "ecmascript/jspandafile/js_pandafile_manager.h" +#include "ecmascript/interpreter/interpreter-inl.h" namespace panda::ecmascript::tooling { using panda::ecmascript::base::BuiltinsBase; @@ -205,4 +206,30 @@ void JSDebugger::DumpBreakpoints() LOG_DEBUGGER(DEBUG) << bp.ToString(); } } + +void JSDebugger::MethodEntry(JSHandle method, JSHandle envHandle) +{ + if (hooks_ == nullptr || !ecmaVm_->GetJsDebuggerManager()->IsDebugMode()) { + return; + } + FrameHandler frameHandler(ecmaVm_->GetJSThread()); + if (frameHandler.IsEntryFrame() || frameHandler.IsBuiltinFrame()) { + return; + } + auto *debuggerMgr = ecmaVm_->GetJsDebuggerManager(); + debuggerMgr->MethodEntry(method, envHandle); +} + +void JSDebugger::MethodExit([[maybe_unused]] JSHandle method) +{ + if (hooks_ == nullptr || !ecmaVm_->GetJsDebuggerManager()->IsDebugMode()) { + return; + } + FrameHandler frameHandler(ecmaVm_->GetJSThread()); + if (frameHandler.IsEntryFrame() || frameHandler.IsBuiltinFrame()) { + return; + } + auto *debuggerMgr = ecmaVm_->GetJsDebuggerManager(); + debuggerMgr->MethodExit(method); +} } // namespace panda::tooling::ecmascript diff --git a/ecmascript/debugger/js_debugger.h b/ecmascript/debugger/js_debugger.h index fb1772870051d2f717b1471684fb53b90bdddae6..b0ed921e46338a24f51e65cbe3eecb7bb70791bd 100644 --- a/ecmascript/debugger/js_debugger.h +++ b/ecmascript/debugger/js_debugger.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_TOOLING_BACKEND_JS_DEBUGGER_H -#define ECMASCRIPT_TOOLING_BACKEND_JS_DEBUGGER_H +#ifndef ECMASCRIPT_DEBUGGER_JS_DEBUGGER_H +#define ECMASCRIPT_DEBUGGER_JS_DEBUGGER_H #include "ecmascript/debugger/debugger_api.h" #include "ecmascript/debugger/js_debugger_manager.h" @@ -115,7 +115,7 @@ public: notificationMgr_->VmDeathEvent(); hooks_ = nullptr; } - bool HandleDebuggerStmt(JSHandle method, uint32_t bc_offset) override; + bool HandleDebuggerStmt(JSHandle method, uint32_t bcOffset) override; bool SetBreakpoint(const JSPtLocation &location, Local condFuncRef) override; bool RemoveBreakpoint(const JSPtLocation &location) override; void RemoveAllBreakpoints() override; @@ -141,13 +141,6 @@ public: } hooks_->VmDeath(); } - void PendingJobEntry() override - { - if (hooks_ == nullptr) { - return; - } - hooks_->PendingJobEntry(); - } void NativeCalling(const void *nativeAddress) override { if (hooks_ == nullptr) { @@ -155,6 +148,8 @@ public: } hooks_->NativeCalling(nativeAddress); } + void MethodEntry(JSHandle method, JSHandle envHandle) override; + void MethodExit(JSHandle method) override; private: std::unique_ptr FindMethod(const JSPtLocation &location) const; std::optional FindBreakpoint(JSHandle method, uint32_t bcOffset) const; @@ -172,4 +167,4 @@ private: }; } // namespace panda::ecmascript::tooling -#endif // ECMASCRIPT_TOOLING_BACKEND_JS_DEBUGGER_H +#endif // ECMASCRIPT_DEBUGGER_JS_DEBUGGER_H diff --git a/ecmascript/debugger/js_debugger_interface.h b/ecmascript/debugger/js_debugger_interface.h index e84483a33b2521b6f76aebf16dd2219e24cca458..386124d5f042ba3a725ee6954eb2cd012536abff 100644 --- a/ecmascript/debugger/js_debugger_interface.h +++ b/ecmascript/debugger/js_debugger_interface.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_TOOLING_INTERFACE_JS_DEBUG_INTERFACE_H -#define ECMASCRIPT_TOOLING_INTERFACE_JS_DEBUG_INTERFACE_H +#ifndef ECMASCRIPT_DEBUGGER_JS_DEBUG_INTERFACE_H +#define ECMASCRIPT_DEBUGGER_JS_DEBUG_INTERFACE_H #include @@ -70,11 +70,6 @@ public: */ virtual void LoadModule(std::string_view pandaFileName, std::string_view entryPoint) = 0; - /** - * \brief called before executing pending job - */ - virtual void PendingJobEntry() = 0; - /** * \brief called by the ecmavm when virtual machine start initialization */ @@ -103,9 +98,10 @@ public: /** * \brief handle debugger statement event - * @param location debugger statement location + * @param method Current method + * @param bcOffset Current bytecode offset */ - virtual bool HandleDebuggerStmt(JSHandle method, uint32_t bc_offset) = 0; + virtual bool HandleDebuggerStmt(JSHandle method, uint32_t bcOffset) = 0; /** * \brief Register debug hooks in the ecmavm @@ -134,8 +130,7 @@ public: virtual bool RemoveBreakpoint(const JSPtLocation &location) = 0; /** - * \brief Remove all breakpoints from \param location - * @param location Breakpoint location + * \brief Remove all breakpoints */ virtual void RemoveAllBreakpoints() = 0; @@ -146,4 +141,4 @@ public: }; } // namespace panda::ecmascript::tooling -#endif // ECMASCRIPT_TOOLING_INTERFACE_JS_DEBUG_INTERFACE_H +#endif // ECMASCRIPT_DEBUGGER_JS_DEBUG_INTERFACE_H diff --git a/ecmascript/debugger/js_debugger_manager.h b/ecmascript/debugger/js_debugger_manager.h index 12dec88bf8fdfa0408db6379b3f860fa11918160..7dc2b2704449be433e7e6a26e7f132fbf1389d2f 100644 --- a/ecmascript/debugger/js_debugger_manager.h +++ b/ecmascript/debugger/js_debugger_manager.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -13,15 +13,17 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_TOOLING_INTERFACE_JS_DEBUGGER_MANAGER_H -#define ECMASCRIPT_TOOLING_INTERFACE_JS_DEBUGGER_MANAGER_H +#ifndef ECMASCRIPT_DEBUGGER_JS_DEBUGGER_MANAGER_H +#define ECMASCRIPT_DEBUGGER_JS_DEBUGGER_MANAGER_H #include "ecmascript/debugger/hot_reload_manager.h" #include "ecmascript/debugger/notification_manager.h" +#include "ecmascript/debugger/dropframe_manager.h" #include "ecmascript/ecma_vm.h" #include "ecmascript/interpreter/frame_handler.h" #include "ecmascript/js_thread.h" #include "ecmascript/napi/include/jsnapi.h" +#include "ecmascript/global_handle_collection.h" #include "libpandabase/os/library_loader.h" @@ -60,6 +62,12 @@ public: } isDebugMode_ = isDebugMode; + + if (isDebugMode) { + jsThread_->SetDebugModeState(); + } else { + jsThread_->ResetDebugModeState(); + } } bool IsDebugMode() const @@ -131,6 +139,26 @@ public: } } + void MethodEntry(JSHandle method, JSHandle envHandle) + { + dropframeManager_.MethodEntry(jsThread_, method, envHandle); + } + + void MethodExit(JSHandle method) + { + dropframeManager_.MethodExit(jsThread_, method); + } + + void DropLastFrame() + { + dropframeManager_.DropLastFrame(jsThread_); + } + + uint32_t GetPromiseQueueSizeRecordOfTopFrame() + { + return dropframeManager_.GetPromiseQueueSizeRecordOfTopFrame(); + } + private: bool isDebugMode_ {false}; bool isMixedDebugEnabled_ { false }; @@ -140,10 +168,11 @@ private: SingleStepperFunc *stepperFunc_ {nullptr}; JSThread *jsThread_ {nullptr}; std::shared_ptr frameHandler_; + DropframeManager dropframeManager_ { }; NotificationManager notificationManager_; HotReloadManager hotReloadManager_; }; } // panda::ecmascript::tooling -#endif // ECMASCRIPT_TOOLING_INTERFACE_JS_DEBUGGER_MANAGER_H \ No newline at end of file +#endif // ECMASCRIPT_DEBUGGER_JS_DEBUGGER_MANAGER_H \ No newline at end of file diff --git a/ecmascript/debugger/js_pt_location.h b/ecmascript/debugger/js_pt_location.h index a5105fdab8dea87bb3db144ef14086e5009a213f..762efe87d0a9d9d874e28cc63299f01ac33df894 100644 --- a/ecmascript/debugger/js_pt_location.h +++ b/ecmascript/debugger/js_pt_location.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_TOOLING_BACKEND_JS_PT_LOCATION_H -#define ECMASCRIPT_TOOLING_BACKEND_JS_PT_LOCATION_H +#ifndef ECMASCRIPT_DEBUGGER_JS_PT_LOCATION_H +#define ECMASCRIPT_DEBUGGER_JS_PT_LOCATION_H #include @@ -84,4 +84,4 @@ private: }; } // namespace panda::ecmascript::tooling -#endif // ECMASCRIPT_TOOLING_BACKEND_JS_PT_LOCATION_H +#endif // ECMASCRIPT_DEBUGGER_JS_PT_LOCATION_H diff --git a/ecmascript/debugger/js_pt_method.h b/ecmascript/debugger/js_pt_method.h index 5c8bffa2f352cb60f7c7691e48f69c3bdcbe3d4e..0441db8ceaf6498ac1c7e517e1ec914e646fd380 100644 --- a/ecmascript/debugger/js_pt_method.h +++ b/ecmascript/debugger/js_pt_method.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_TOOLING_BASE_PT_METHOD_H -#define ECMASCRIPT_TOOLING_BASE_PT_METHOD_H +#ifndef ECMASCRIPT_DEBUGGER_JS_PT_METHOD_H +#define ECMASCRIPT_DEBUGGER_JS_PT_METHOD_H #include "ecmascript/jspandafile/js_pandafile.h" @@ -71,4 +71,4 @@ private: bool isNative_ {false}; }; } // namespace panda::ecmascript::tooling -#endif // ECMASCRIPT_TOOLING_BASE_PT_METHOD_H +#endif // ECMASCRIPT_DEBUGGER_JS_PT_METHOD_H diff --git a/ecmascript/debugger/notification_manager.h b/ecmascript/debugger/notification_manager.h index 422f8de7c3afb743e4a59aa703e5c22a1095e5ab..8adb610406af999baf08330952ab46f2ec83c182 100644 --- a/ecmascript/debugger/notification_manager.h +++ b/ecmascript/debugger/notification_manager.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_TOOLING_INTERFACE_NOTIFICATION_MANAGER_H -#define ECMASCRIPT_TOOLING_INTERFACE_NOTIFICATION_MANAGER_H +#ifndef ECMASCRIPT_DEBUGGER_NOTIFICATION_MANAGER_H +#define ECMASCRIPT_DEBUGGER_NOTIFICATION_MANAGER_H #include @@ -32,13 +32,14 @@ public: virtual void LoadModule(std::string_view name, std::string_view) = 0; virtual void BytecodePcChanged(JSThread *thread, JSHandle method, - uint32_t bc_offset) = 0; + uint32_t bcOffset) = 0; - virtual bool HandleDebuggerStmt(JSHandle method, uint32_t bc_offset) = 0; + virtual bool HandleDebuggerStmt(JSHandle method, uint32_t bcOffset) = 0; virtual void VmStart() = 0; virtual void VmDeath() = 0; - virtual void PendingJobEntry() = 0; virtual void NativeCalling(const void *nativeAddress) = 0; + virtual void MethodEntry(JSHandle method, JSHandle envHandle) = 0; + virtual void MethodExit(JSHandle method) = 0; }; class NotificationManager { @@ -88,13 +89,6 @@ public: } } - void PendingJobEntryEvent() const - { - if (UNLIKELY(listener_ != nullptr)) { - listener_->PendingJobEntry(); - } - } - void VmStartEvent() const { if (UNLIKELY(listener_ != nullptr)) { @@ -108,9 +102,24 @@ public: } } + void MethodEntryEvent(JSThread *thread, Method *method, JSTaggedValue env) const + { + if (UNLIKELY(listener_ != nullptr)) { + JSHandle methodHandle(thread, method); + JSHandle envHandle(thread, env); + listener_->MethodEntry(methodHandle, envHandle); + } + } + void MethodExitEvent(JSThread *thread, Method *method) const + { + if (UNLIKELY(listener_ != nullptr)) { + JSHandle methodHandle(thread, method); + listener_->MethodExit(methodHandle); + } + } private: RuntimeListener *listener_ {nullptr}; }; } // panda::ecmascript::tooling -#endif // ECMASCRIPT_TOOLING_INTERFACE_NOTIFICATION_MANAGER_H \ No newline at end of file +#endif // ECMASCRIPT_DEBUGGER_NOTIFICATION_MANAGER_H \ No newline at end of file diff --git a/ecmascript/deoptimizer/deoptimizer.cpp b/ecmascript/deoptimizer/deoptimizer.cpp index 33cdb672603d3a359cbffec5b1a106382eadb374..5d5b3aa8396eb895c9117bb0aa8d8bcb73777461 100644 --- a/ecmascript/deoptimizer/deoptimizer.cpp +++ b/ecmascript/deoptimizer/deoptimizer.cpp @@ -17,7 +17,7 @@ #include #include "ecmascript/compiler/assembler/assembler.h" -#include "ecmascript/compiler/gate_meta_data.h" +#include "ecmascript/compiler/share_gate_meta_data.h" #include "ecmascript/dfx/stackinfo/js_stackinfo.h" #include "ecmascript/frames.h" #include "ecmascript/interpreter/interpreter.h" @@ -75,6 +75,12 @@ public: firstFrame_ = top_; } + void ReviseValueByIndex(JSTaggedType value, size_t index) + { + ASSERT(index < static_cast(start_ - top_)); + *(top_ + index) = value; + } + private: JSThread *thread_ {nullptr}; JSTaggedType *start_ {nullptr}; @@ -379,6 +385,29 @@ bool Deoptimizier::CollectVirtualRegisters(Method* method, FrameWriter *frameWri frameWriter->PushValue(value.GetRawData()); virtualIndex--; } + // revise correct a0 - aN virtual regs , for example: ldobjbyname key; sta a2; update value to a2 + // +--------------------------+ ^ + // | aN | | + // +--------------------------+ | + // | ... | | + // +--------------------------+ | + // | a2(this) | | + // +--------------------------+ revise correct vreg + // | a1(newtarget) | | + // +--------------------------+ | + // | a0(func) | | + // |--------------------------| v + // | v0 - vN | + // sp --> |--------------------------| + int32_t vregsAndArgsNum = declaredNumArgs + callFieldNumVregs + + static_cast(method->GetNumRevervedArgs()); + for (int32_t i = callFieldNumVregs; i < vregsAndArgsNum; i++) { + JSTaggedValue value = JSTaggedValue::Undefined(); + if (HasDeoptValue(curDepth, i)) { + value = GetDeoptValue(curDepth, i); + frameWriter->ReviseValueByIndex(value.GetRawData(), i); + } + } return true; } @@ -505,7 +534,7 @@ void Deoptimizier::UpdateAndDumpDeoptInfo(kungfu::DeoptType type) JSHandle oldHclass(thread_, jsFunc->GetClass()); // instead of hclass by non_optimized hclass when method ClearAOTFlags JSHandle newHClass = factory->GetNonOptimizedHclass(oldHclass, kind); - jsFunc->SetClass(newHClass); + jsFunc->SynchronizedSetClass(*newHClass); } } } diff --git a/ecmascript/deoptimizer/deoptimizer.h b/ecmascript/deoptimizer/deoptimizer.h index 1951a689421b36d91b3f4577f4e1f8af191ea5d2..2d10142d83f2b2b67475eeb31e27bf10a5f38925 100644 --- a/ecmascript/deoptimizer/deoptimizer.h +++ b/ecmascript/deoptimizer/deoptimizer.h @@ -33,8 +33,16 @@ enum class SpecVregIndex: int { NEWTARGET_INDEX = -5, THIS_OBJECT_INDEX = -6, ACTUAL_ARGC_INDEX = -7, + FIRST_METHOD_OFFSET_INDEX = -8, + PADDING1 = -9, + PADDING2 = -10, + PADDING3 = -11, + MAX_METHOD_OFFSET_INDEX = -12, }; +static constexpr uint32_t MAX_METHOD_OFFSET_NUM = static_cast(SpecVregIndex::FIRST_METHOD_OFFSET_INDEX) - + static_cast(SpecVregIndex::MAX_METHOD_OFFSET_INDEX) + 1; + struct Context { uintptr_t callsiteSp; uintptr_t callsiteFp; diff --git a/ecmascript/dfx/cpu_profiler/cpu_profiler.cpp b/ecmascript/dfx/cpu_profiler/cpu_profiler.cpp index b8bc5e896022abdfbc8d19eeaffe66c47722f7e9..1e49931553a607238871766ea39a04e57353eaaf 100644 --- a/ecmascript/dfx/cpu_profiler/cpu_profiler.cpp +++ b/ecmascript/dfx/cpu_profiler/cpu_profiler.cpp @@ -28,13 +28,14 @@ #include "ecmascript/taskpool/taskpool.h" namespace panda::ecmascript { -os::memory::Mutex CpuProfiler::synchronizationMutex_; +Mutex CpuProfiler::synchronizationMutex_; CMap CpuProfiler::profilerMap_ = CMap(); CpuProfiler::CpuProfiler(const EcmaVM *vm, const int interval) : vm_(vm), interval_(interval) { enableVMTag_ = const_cast(vm)->GetJSOptions().EnableCpuProfilerVMTag(); generator_ = new SamplesRecord(); generator_->SetEnableVMTag(enableVMTag_); + generator_->SetSourceMapTranslateCallback(vm->GetSourceMapTranslateCallback()); generator_->NodeInit(); if (generator_->SemInit(0, 0, 0) != 0) { LOG_ECMA(ERROR) << "sem_[0] init failed"; @@ -70,21 +71,24 @@ void CpuProfiler::StartCpuProfilerForInfo() } tid_ = static_cast(syscall(SYS_gettid)); { - os::memory::LockHolder lock(synchronizationMutex_); + LockHolder lock(synchronizationMutex_); profilerMap_[tid_] = vm_; } vm_->GetJSThread()->SetIsProfiling(true); JSPandaFileManager *pandaFileManager = JSPandaFileManager::GetInstance(); pandaFileManager->EnumerateJSPandaFiles([&](const JSPandaFile *file) -> bool { - pandaFileManager->GetJSPtExtractorAndExtract(file); + pandaFileManager->CpuProfilerGetJSPtExtractor(file); return true; }); generator_->SetTimeDeltaThreshold(interval_ * THRESHOLD_GROWTH_FACTORY + THRESHOLD_FIXED_INCREMENT); generator_->SetIsStart(true); - Taskpool::GetCurrentTaskpool()->PostTask( - std::make_unique(vm_->GetJSThread()->GetThreadId(), generator_, interval_)); + RunParams *params = new RunParams(generator_, static_cast(interval_), pthread_self()); + if (pthread_create(&tid_, nullptr, SamplingProcessor::Run, params) != 0) { + LOG_ECMA(ERROR) << "pthread_create fail!"; + return; + } } void CpuProfiler::StartCpuProfilerForFile(const std::string &fileName) @@ -129,7 +133,7 @@ void CpuProfiler::StartCpuProfilerForFile(const std::string &fileName) } tid_ = static_cast(syscall(SYS_gettid)); { - os::memory::LockHolder lock(synchronizationMutex_); + LockHolder lock(synchronizationMutex_); profilerMap_[tid_] = vm_; } outToFile_ = true; @@ -137,14 +141,17 @@ void CpuProfiler::StartCpuProfilerForFile(const std::string &fileName) vm_->GetJSThread()->SetIsProfiling(true); JSPandaFileManager *pandaFileManager = JSPandaFileManager::GetInstance(); pandaFileManager->EnumerateJSPandaFiles([&](const JSPandaFile *file) -> bool { - pandaFileManager->GetJSPtExtractorAndExtract(file); + pandaFileManager->CpuProfilerGetJSPtExtractor(file); return true; }); generator_->SetTimeDeltaThreshold(interval_ * THRESHOLD_GROWTH_FACTORY + THRESHOLD_FIXED_INCREMENT); generator_->SetIsStart(true); - Taskpool::GetCurrentTaskpool()->PostTask( - std::make_unique(vm_->GetJSThread()->GetThreadId(), generator_, interval_)); + RunParams *params = new RunParams(generator_, static_cast(interval_), pthread_self()); + if (pthread_create(&tid_, nullptr, SamplingProcessor::Run, params) != 0) { + LOG_ECMA(ERROR) << "pthread_create fail!"; + return; + } } std::unique_ptr CpuProfiler::StopCpuProfilerForInfo() @@ -269,17 +276,17 @@ void CpuProfiler::GetStack(FrameIterator &it) generator_->ResetFrameLength(); for (; !it.Done(); it.Advance<>()) { auto method = it.CheckAndGetMethod(); - if (method == nullptr) { + if (method == nullptr || !JSTaggedValue(method).IsMethod()) { continue; } - const JSPandaFile *jsPandaFile = method->GetJSPandaFile(); + bool isNative = method->IsNativeWithCallField(); struct MethodKey methodKey; methodKey.deoptType = method->GetDeoptType(); if (topFrame) { - methodKey.state = JsStackGetter::GetRunningState(it, vm_, jsPandaFile, true, enableVMTag_); + methodKey.state = JsStackGetter::GetRunningState(it, vm_, isNative, true, enableVMTag_); topFrame = false; } else { - methodKey.state = JsStackGetter::GetRunningState(it, vm_, jsPandaFile, false, enableVMTag_); + methodKey.state = JsStackGetter::GetRunningState(it, vm_, isNative, false, enableVMTag_); } void *methodIdentifier = JsStackGetter::GetMethodIdentifier(method, it); if (methodIdentifier == nullptr) { @@ -302,20 +309,27 @@ void CpuProfiler::GetStack(FrameIterator &it) generator_->PostFrame(); } -void CpuProfiler::GetStackCallNapi(JSThread *thread, bool beforeCallNapi) +bool CpuProfiler::GetStackBeforeCallNapi(JSThread *thread) { uint64_t tempTimeStamp = SamplingProcessor::GetMicrosecondsTimeStamp(); - if (beforeCallNapi) { - if (tempTimeStamp - beforeCallNapiTimeStamp_ < INTERVAL_OF_ACTIVE_SAMPLING) { - beforeCallNapiTimeStamp_ = tempTimeStamp; - return; - } + if (tempTimeStamp - beforeCallNapiTimeStamp_ < interval_) { + return false; + } + + if (GetStackCallNapi(thread, true)) { beforeCallNapiTimeStamp_ = tempTimeStamp; - } else { - if (tempTimeStamp - beforeCallNapiTimeStamp_ < CPUPROFILER_DEFAULT_INTERVAL) { - return; - } + return true; } + return false; +} + +void CpuProfiler::GetStackAfterCallNapi(JSThread *thread) +{ + GetStackCallNapi(thread, false); +} + +bool CpuProfiler::GetStackCallNapi(JSThread *thread, bool beforeCallNapi) +{ [[maybe_unused]] CallNapiScope scope(this); const CMap &stackInfo = generator_->GetStackInfo(); generator_->ClearNapiStack(); @@ -327,21 +341,22 @@ void CpuProfiler::GetStackCallNapi(JSThread *thread, bool beforeCallNapi) } for (; !it.Done(); it.Advance()) { auto method = it.CheckAndGetMethod(); - if (method == nullptr) { + if (method == nullptr || !JSTaggedValue(method).IsMethod()) { continue; } + bool isNative = method->IsNativeWithCallField(); struct MethodKey methodKey; methodKey.deoptType = method->GetDeoptType(); if (topFrame) { if (beforeCallNapi) { methodKey.state = RunningState::NAPI; } else { - methodKey.state = JsStackGetter::GetRunningState(it, vm_, method->GetJSPandaFile(), true, enableVMTag_); + methodKey.state = JsStackGetter::GetRunningState(it, vm_, isNative, true, enableVMTag_); } topFrame = false; } else { - methodKey.state = JsStackGetter::GetRunningState(it, vm_, method->GetJSPandaFile(), false, enableVMTag_); + methodKey.state = JsStackGetter::GetRunningState(it, vm_, isNative, false, enableVMTag_); } void *methodIdentifier = JsStackGetter::GetMethodIdentifier(method, it); if (methodIdentifier == nullptr) { @@ -354,14 +369,15 @@ void CpuProfiler::GetStackCallNapi(JSThread *thread, bool beforeCallNapi) continue; } if (UNLIKELY(!generator_->PushNapiStackInfo(codeEntry))) { - return; + return false; } } if (UNLIKELY(!generator_->PushNapiFrameStack(methodKey))) { - return; + return false; } } generator_->PostNapiFrame(); + return true; } void CpuProfiler::GetStackSignalHandler(int signal, [[maybe_unused]] siginfo_t *siginfo, void *context) @@ -372,7 +388,7 @@ void CpuProfiler::GetStackSignalHandler(int signal, [[maybe_unused]] siginfo_t * CpuProfiler *profiler = nullptr; JSThread *thread = nullptr; { - os::memory::LockHolder lock(synchronizationMutex_); + LockHolder lock(synchronizationMutex_); pthread_t tid = static_cast(syscall(SYS_gettid)); const EcmaVM *vm = profilerMap_[tid]; if (vm == nullptr) { @@ -387,7 +403,7 @@ void CpuProfiler::GetStackSignalHandler(int signal, [[maybe_unused]] siginfo_t * } } - if (profiler->GetBuildNapiStack()) { + if (profiler->GetBuildNapiStack() || thread->GetGcState()) { if (profiler->generator_->SemPost(0) != 0) { LOG_ECMA(ERROR) << "sem_[0] post failed"; } diff --git a/ecmascript/dfx/cpu_profiler/cpu_profiler.h b/ecmascript/dfx/cpu_profiler/cpu_profiler.h index 28c5b5780694ee0d136d6bcc5d11de9bd884c711..4095c14d4aa37debd149e827a3a7145a8a16074b 100644 --- a/ecmascript/dfx/cpu_profiler/cpu_profiler.h +++ b/ecmascript/dfx/cpu_profiler/cpu_profiler.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_CPU_PROFILER_H -#define ECMASCRIPT_CPU_PROFILER_H +#ifndef ECMASCRIPT_DFX_CPU_PROFILER_CPU_PROFILER_H +#define ECMASCRIPT_DFX_CPU_PROFILER_CPU_PROFILER_H #include @@ -80,7 +80,9 @@ public: bool InHeaderOrTail(uint64_t pc, uint64_t entryBegin, uint64_t entryDuration, uint64_t headerSize, uint64_t tailSize) const; bool IsEntryFrameHeaderOrTail(JSThread *thread, uint64_t pc) const; - void GetStackCallNapi(JSThread *thread, bool beforeCallNapi); + bool GetStackBeforeCallNapi(JSThread *thread); + void GetStackAfterCallNapi(JSThread *thread); + bool GetStackCallNapi(JSThread *thread, bool beforeCallNapi); static void GetStackSignalHandler(int signal, siginfo_t *siginfo, void *context); void StartCpuProfilerForInfo(); @@ -96,7 +98,7 @@ public: static CMap profilerMap_; private: - static os::memory::Mutex synchronizationMutex_; + static Mutex synchronizationMutex_; void GetStack(FrameIterator &it); static uint64_t GetPcFromContext(void *context); @@ -132,4 +134,4 @@ private: CpuProfiler *profiler_ {nullptr}; }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_CPU_PROFILE_H \ No newline at end of file +#endif // ECMASCRIPT_DFX_CPU_PROFILER_CPU_PROFILER_H \ No newline at end of file diff --git a/ecmascript/dfx/cpu_profiler/samples_record.cpp b/ecmascript/dfx/cpu_profiler/samples_record.cpp index 3041c6356bf57399c155712d313d344c8171ad4d..d319db1af91283180ab78c7b8b5d4e147801f796 100644 --- a/ecmascript/dfx/cpu_profiler/samples_record.cpp +++ b/ecmascript/dfx/cpu_profiler/samples_record.cpp @@ -175,14 +175,22 @@ void SamplesRecord::StringifyNodes() { sampleData_ += "\"nodes\":["; size_t nodeCount = static_cast(profileInfo_->nodeCount); + bool translateCallback = false; + if (sourceMapTranslateCallback_ != nullptr) { + translateCallback = true; + } for (size_t i = 0; i < nodeCount; i++) { struct CpuProfileNode node = profileInfo_->nodes[i]; struct FrameInfo codeEntry = node.codeEntry; + if (translateCallback) { + TranslateUrlPositionBySourceMap(codeEntry); + } std::string url = codeEntry.url; replace(url.begin(), url.end(), '\\', '/'); sampleData_ += "{\"id\":" + std::to_string(node.id) + ",\"callFrame\":{\"functionName\":\"" - + codeEntry.functionName + "\",\"scriptId\":\"" + + codeEntry.functionName + "\",\"moduleName\":\"" + + codeEntry.moduleName + "\",\"scriptId\":\"" + std::to_string(codeEntry.scriptId) + "\",\"url\":\"" + url + "\",\"lineNumber\":" + std::to_string(codeEntry.lineNumber) + ",\"columnNumber\":" @@ -658,6 +666,22 @@ bool SamplesRecord::PushNapiStackInfo(const FrameInfoTemp &frameInfoTemp) return true; } +std::string SamplesRecord::GetModuleName(char *recordName) +{ + std::string recordNameStr = recordName; + std::string::size_type atPos = recordNameStr.find("@"); + if (atPos == std::string::npos) { + return ""; + } + + std::string::size_type slashPos = recordNameStr.rfind("/", atPos); + if (slashPos == std::string::npos) { + return ""; + } + + return recordNameStr.substr(slashPos + 1, atPos - slashPos - 1); +} + void SamplesRecord::FrameInfoTempToMap(FrameInfoTemp *frameInfoTemps, int frameInfoTempLength) { if (frameInfoTempLength == 0) { @@ -676,6 +700,9 @@ void SamplesRecord::FrameInfoTempToMap(FrameInfoTemp *frameInfoTemps, int frameI frameInfo.functionName = AddRunningState(frameInfoTemps[i].functionName, frameInfoTemps[i].methodKey.state, frameInfoTemps[i].methodKey.deoptType); + if (strlen(frameInfoTemps[i].recordName) != 0) { + frameInfo.moduleName = GetModuleName(frameInfoTemps[i].recordName); + } frameInfo.columnNumber = frameInfoTemps[i].columnNumber; frameInfo.lineNumber = frameInfoTemps[i].lineNumber; stackInfoMap_.emplace(frameInfoTemps[i].methodKey, frameInfo); @@ -702,6 +729,9 @@ void SamplesRecord::NapiFrameInfoTempToMap() frameInfo.functionName = AddRunningState(napiFrameInfoTemps_[i].functionName, napiFrameInfoTemps_[i].methodKey.state, napiFrameInfoTemps_[i].methodKey.deoptType); + if (strlen(napiFrameInfoTemps_[i].recordName) != 0) { + frameInfo.moduleName = GetModuleName(napiFrameInfoTemps_[i].recordName); + } frameInfo.columnNumber = napiFrameInfoTemps_[i].columnNumber; frameInfo.lineNumber = napiFrameInfoTemps_[i].lineNumber; stackInfoMap_.emplace(napiFrameInfoTemps_[i].methodKey, frameInfo); @@ -749,16 +779,36 @@ void SamplesRecord::SetCallTimeStamp(uint64_t timeStamp) callTimeStamp_ = timeStamp; } +void SamplesRecord::TranslateUrlPositionBySourceMap(struct FrameInfo &codeEntry) +{ + if (codeEntry.url.empty()) { + return; + } + if (!sourceMapTranslateCallback_(codeEntry.url, codeEntry.lineNumber, codeEntry.columnNumber)) { + size_t find = codeEntry.url.rfind("_.js"); + if (find == std::string::npos) { + size_t start = codeEntry.url.find("entry/"); + size_t end = codeEntry.url.rfind(".ets"); + if (start != std::string::npos && end != std::string::npos) { + std::string key = codeEntry.url.substr(start + SUB_LEN, end - start - SUB_LEN); + codeEntry.url = JS_PATH + key + ".js"; + } + } + } +} + // SamplesQueue void SamplesQueue::PostFrame(FrameInfoTemp *frameInfoTemps, MethodKey *frameStack, int frameInfoTempsLength, int frameStackLength) { - os::memory::LockHolder holder(mtx_); + LockHolder holder(mtx_); if (!IsFull()) { // frameInfoTemps for (int i = 0; i < frameInfoTempsLength; i++) { CheckAndCopy(frames_[rear_].frameInfoTemps[i].functionName, sizeof(frames_[rear_].frameInfoTemps[i].functionName), frameInfoTemps[i].functionName); + CheckAndCopy(frames_[rear_].frameInfoTemps[i].recordName, + sizeof(frames_[rear_].frameInfoTemps[i].recordName), frameInfoTemps[i].recordName); frames_[rear_].frameInfoTemps[i].columnNumber = frameInfoTemps[i].columnNumber; frames_[rear_].frameInfoTemps[i].lineNumber = frameInfoTemps[i].lineNumber; frames_[rear_].frameInfoTemps[i].scriptId = frameInfoTemps[i].scriptId; @@ -786,14 +836,16 @@ void SamplesQueue::PostFrame(FrameInfoTemp *frameInfoTemps, MethodKey *frameStac void SamplesQueue::PostNapiFrame(CVector &napiFrameInfoTemps, CVector &napiFrameStack) { - os::memory::LockHolder holder(mtx_); + LockHolder holder(mtx_); if (!IsFull()) { - int frameInfoTempsLength = static_cast(napiFrameInfoTemps.size()); - int frameStackLength = static_cast(napiFrameStack.size()); + size_t frameInfoTempsLength = napiFrameInfoTemps.size(); + size_t frameStackLength = napiFrameStack.size(); // napiFrameInfoTemps - for (int i = 0; i < frameInfoTempsLength; i++) { + for (size_t i = 0; i < frameInfoTempsLength; i++) { CheckAndCopy(frames_[rear_].frameInfoTemps[i].functionName, sizeof(frames_[rear_].frameInfoTemps[i].functionName), napiFrameInfoTemps[i].functionName); + CheckAndCopy(frames_[rear_].frameInfoTemps[i].recordName, + sizeof(frames_[rear_].frameInfoTemps[i].recordName), napiFrameInfoTemps[i].recordName); frames_[rear_].frameInfoTemps[i].columnNumber = napiFrameInfoTemps[i].columnNumber; frames_[rear_].frameInfoTemps[i].lineNumber = napiFrameInfoTemps[i].lineNumber; frames_[rear_].frameInfoTemps[i].scriptId = napiFrameInfoTemps[i].scriptId; @@ -804,7 +856,7 @@ void SamplesQueue::PostNapiFrame(CVector &napiFrameInfoTemps, frames_[rear_].frameInfoTemps[i].methodKey.state = napiFrameInfoTemps[i].methodKey.state; } // napiFrameStack - for (int i = 0; i < frameStackLength; i++) { + for (size_t i = 0; i < frameStackLength; i++) { frames_[rear_].frameStack[i].methodIdentifier = napiFrameStack[i].methodIdentifier; frames_[rear_].frameStack[i].state = napiFrameStack[i].state; } @@ -821,7 +873,7 @@ void SamplesQueue::PostNapiFrame(CVector &napiFrameInfoTemps, FrameStackAndInfo *SamplesQueue::PopFrame() { - os::memory::LockHolder holder(mtx_); + LockHolder holder(mtx_); if (!IsEmpty()) { FrameStackAndInfo *frame = &frames_[front_]; front_ = (front_ + 1) % QUEUE_CAPACITY; diff --git a/ecmascript/dfx/cpu_profiler/samples_record.h b/ecmascript/dfx/cpu_profiler/samples_record.h index 4369c0d434d4734512fe794a4435a350fa490e8e..911246a7284438c9fbe04401ad8f5410ecadaab3 100644 --- a/ecmascript/dfx/cpu_profiler/samples_record.h +++ b/ecmascript/dfx/cpu_profiler/samples_record.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_SAMPLES_RECORD_H -#define ECMASCRIPT_SAMPLES_RECORD_H +#ifndef ECMASCRIPT_DFX_CPU_PROFILER_SAMPLES_RECORD_H +#define ECMASCRIPT_DFX_CPU_PROFILER_SAMPLES_RECORD_H #include #include @@ -26,7 +26,7 @@ #include "ecmascript/js_thread.h" #include "ecmascript/jspandafile/method_literal.h" #include "ecmascript/mem/c_containers.h" -#include "libpandabase/os/mutex.h" +#include "ecmascript/platform/mutex.h" namespace panda::ecmascript { const int MAX_STACK_SIZE = 128; // 128:the maximum size of the js stack @@ -36,13 +36,15 @@ const int PROGRAM_NODE_ID = 2; // 2: the (program) node id const int QUEUE_CAPACITY = 51; // the capacity of the circular queue is QUEUE_CAPACITY - 1 const size_t NAPI_CALL_SETP = 2; // 2: step size of the variable napiCallIdx in while loop const size_t PRE_IDX_RANGE = 5; // 5: length of variable preIdx looping backward +const size_t SUB_LEN = 6; // 6: Truncate the path length +const std::string JS_PATH = "entry/build/default/cache/default/default@CompileArkTS/esmodule/debug/"; struct FrameInfo { - std::string codeType = ""; - std::string functionName = ""; - int columnNumber = -1; - int lineNumber = -1; int scriptId = 0; + int lineNumber = -1; + int columnNumber = -1; + std::string functionName = ""; + std::string moduleName = ""; std::string url = ""; }; @@ -106,7 +108,7 @@ private: FrameStackAndInfo frames_[QUEUE_CAPACITY] = {}; int front_ = 0; int rear_ = 0; - os::memory::Mutex mtx_; + Mutex mtx_; }; class SamplesRecord { @@ -121,6 +123,7 @@ public: int GetMethodNodeCount() const; int GetframeStackLength() const; std::string GetSampleData() const; + std::string GetModuleName(char *recordName); void SetThreadStartTime(uint64_t threadStartTime); void SetThreadStopTime(); void SetStartsampleData(std::string sampleData); @@ -172,6 +175,11 @@ public: enableVMTag_ = flag; } + void SetSourceMapTranslateCallback(SourceMapTranslateCallback cb) + { + sourceMapTranslateCallback_ = cb; + } + void SetTimeDeltaThreshold(uint32_t timeDeltaThreshold) { timeDeltaThreshold_ = timeDeltaThreshold; @@ -186,6 +194,7 @@ private: void FrameInfoTempToMap(FrameInfoTemp *frameInfoTemps, int frameInfoTempLength); void NapiFrameInfoTempToMap(); void StatisticStateTime(int timeDelta, RunningState state); + void TranslateUrlPositionBySourceMap(struct FrameInfo &codeEntry); int previousId_ = 0; RunningState previousState_ = RunningState::OTHER; @@ -215,6 +224,7 @@ private: bool enableVMTag_ {false}; uint64_t callTimeStamp_ = 0; uint32_t timeDeltaThreshold_ = 0; + SourceMapTranslateCallback sourceMapTranslateCallback_ {nullptr}; }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_SAMPLES_RECORD_H \ No newline at end of file +#endif // ECMASCRIPT_DFX_CPU_PROFILER_SAMPLES_RECORD_H \ No newline at end of file diff --git a/ecmascript/dfx/cpu_profiler/sampling_processor.cpp b/ecmascript/dfx/cpu_profiler/sampling_processor.cpp index 9056a3e771d7bb2f776709072f1a1580af0d61c6..9d427b0b79ce85b09c543d3aa5d21b6a50dcdad3 100644 --- a/ecmascript/dfx/cpu_profiler/sampling_processor.cpp +++ b/ecmascript/dfx/cpu_profiler/sampling_processor.cpp @@ -16,7 +16,6 @@ #include "ecmascript/dfx/cpu_profiler/sampling_processor.h" #include -#include #include #include @@ -29,57 +28,55 @@ namespace panda::ecmascript { const int USEC_PER_SEC = 1000 * 1000; const int NSEC_PER_USEC = 1000; -SamplingProcessor::SamplingProcessor(int32_t id, SamplesRecord *generator, int interval) : Task(id) -{ - generator_ = generator; - interval_ = static_cast(interval); - pid_ = pthread_self(); -} SamplingProcessor::~SamplingProcessor() {} -bool SamplingProcessor::Run([[maybe_unused]] uint32_t threadIndex) +void *SamplingProcessor::Run(void *arg) { + RunParams params = *reinterpret_cast(arg); + SamplesRecord *generator = params.generatorParam; + uint32_t interval = params.intervalParam; + pthread_t pid = params.pidParam; pthread_t tid = pthread_self(); pthread_setname_np(tid, "SamplingThread"); uint64_t startTime = GetMicrosecondsTimeStamp(); uint64_t endTime = startTime; - generator_->SetThreadStartTime(startTime); - while (generator_->GetIsStart()) { - if (pthread_kill(pid_, SIGPROF) != 0) { + generator->SetThreadStartTime(startTime); + while (generator->GetIsStart()) { + if (pthread_kill(pid, SIGPROF) != 0) { LOG(ERROR, RUNTIME) << "pthread_kill signal failed"; - return false; + return nullptr; } - if (generator_->SemWait(0) != 0) { + if (generator->SemWait(0) != 0) { LOG_ECMA(ERROR) << "sem_[0] wait failed"; - return false; + return nullptr; } startTime = GetMicrosecondsTimeStamp(); - int64_t ts = static_cast(interval_) - static_cast(startTime - endTime); + int64_t ts = static_cast(interval) - static_cast(startTime - endTime); endTime = startTime; if (ts > 0) { usleep(ts); endTime = GetMicrosecondsTimeStamp(); } - if (generator_->GetMethodNodeCount() + generator_->GetframeStackLength() >= MAX_NODE_COUNT) { + if (generator->GetMethodNodeCount() + generator->GetframeStackLength() >= MAX_NODE_COUNT) { break; } - if (generator_->samplesQueue_->IsEmpty()) { + if (generator->samplesQueue_->IsEmpty()) { uint64_t sampleTimeStamp = SamplingProcessor::GetMicrosecondsTimeStamp(); - generator_->AddEmptyStackSample(sampleTimeStamp); + generator->AddEmptyStackSample(sampleTimeStamp); } else { - while (!generator_->samplesQueue_->IsEmpty()) { - FrameStackAndInfo *frame = generator_->samplesQueue_->PopFrame(); - generator_->AddSample(frame); + while (!generator->samplesQueue_->IsEmpty()) { + FrameStackAndInfo *frame = generator->samplesQueue_->PopFrame(); + generator->AddSample(frame); } } } - generator_->SetThreadStopTime(); + generator->SetThreadStopTime(); pthread_setname_np(tid, "GC_WorkerThread"); - if (generator_->SemPost(1) != 0) { + if (generator->SemPost(1) != 0) { LOG_ECMA(ERROR) << "sem_[1] post failed"; - return false; + return nullptr; } - return true; + return nullptr; } uint64_t SamplingProcessor::GetMicrosecondsTimeStamp() diff --git a/ecmascript/dfx/cpu_profiler/sampling_processor.h b/ecmascript/dfx/cpu_profiler/sampling_processor.h index bfda1205c0b04eb70985c385f6f8f3a11cd95140..c41f198d8f9733297901e8f303731bb6c54346a1 100644 --- a/ecmascript/dfx/cpu_profiler/sampling_processor.h +++ b/ecmascript/dfx/cpu_profiler/sampling_processor.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -13,13 +13,12 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_SAMPLING_PROCESSOR_H -#define ECMASCRIPT_SAMPLING_PROCESSOR_H +#ifndef ECMASCRIPT_DFX_CPU_PROFILER_SAMPLING_PROCESSOR_H +#define ECMASCRIPT_DFX_CPU_PROFILER_SAMPLING_PROCESSOR_H #include #include - -#include "ecmascript/taskpool/task.h" +#include #include "libpandabase/macros.h" @@ -27,21 +26,25 @@ namespace panda::ecmascript { class SamplesRecord; class JSThread; class EcmaVM; -class SamplingProcessor : public Task { +class SamplingProcessor { public: static uint64_t GetMicrosecondsTimeStamp(); - SamplingProcessor(int32_t id, SamplesRecord *generator, int interval); virtual ~SamplingProcessor(); - bool Run(uint32_t threadIndex) override; + static void *Run(void *arg); NO_COPY_SEMANTIC(SamplingProcessor); NO_MOVE_SEMANTIC(SamplingProcessor); -private: - SamplesRecord *generator_ = nullptr; - uint32_t interval_ = 0; - pthread_t pid_ = 0; +}; + +struct RunParams { + SamplesRecord *generatorParam; + uint32_t intervalParam; + pthread_t pidParam; + + RunParams(SamplesRecord *generatorParam, uint32_t intervalParam, pthread_t pidParam) + :generatorParam(generatorParam), intervalParam(intervalParam), pidParam(pidParam) {}; }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_SAMPLING_PROCESSOR_H +#endif // ECMASCRIPT_DFX_CPU_PROFILER_SAMPLING_PROCESSOR_H diff --git a/ecmascript/dfx/hprof/file_stream.cpp b/ecmascript/dfx/hprof/file_stream.cpp index 4199977c1e899aa23dda8215894324a8e4057144..5352806500a04f852f89e25a72ad2f14e2468fca 100644 --- a/ecmascript/dfx/hprof/file_stream.cpp +++ b/ecmascript/dfx/hprof/file_stream.cpp @@ -32,9 +32,14 @@ FileStream::FileStream(const std::string &fileName) Initialize(fileName); } +FileStream::~FileStream() +{ + EndOfStream(); +} + void FileStream::EndOfStream() { - if (Good()) { + if (fileStream_.is_open()) { fileStream_.close(); } } @@ -50,7 +55,6 @@ void FileStream::Initialize(const std::string &fileName) std::pair realPath = FilePathValid(fileName); if (!realPath.first) { LOG_ECMA(ERROR) << "FileStream: check file path failed"; - fileStream_.close(); return; } diff --git a/ecmascript/dfx/hprof/file_stream.h b/ecmascript/dfx/hprof/file_stream.h index 413b8b50825e083b5473153c987d07a9c94241a2..f1f712cb040f4ab310d149d9bf1ea046b4af7042 100644 --- a/ecmascript/dfx/hprof/file_stream.h +++ b/ecmascript/dfx/hprof/file_stream.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_TOOLING_INTERFACE_FILE_STREAM_H -#define ECMASCRIPT_TOOLING_INTERFACE_FILE_STREAM_H +#ifndef ECMASCRIPT_DFX_HPROF_FILE_STREAM_H +#define ECMASCRIPT_DFX_HPROF_FILE_STREAM_H #include #include @@ -28,7 +28,7 @@ namespace panda::ecmascript { class FileStream : public Stream { public: explicit FileStream(const std::string &fileName); - ~FileStream() override = default; + ~FileStream() override; void EndOfStream() override; @@ -85,4 +85,4 @@ private: }; } // namespace panda::ecmascript::tooling -#endif // ECMASCRIPT_TOOLING_INTERFACE_FILE_STREAM_H +#endif // ECMASCRIPT_DFX_HPROF_FILE_STREAM_H diff --git a/ecmascript/dfx/hprof/heap_profiler.cpp b/ecmascript/dfx/hprof/heap_profiler.cpp index 47e90fe91cf38a437b779d74e24e776bd28c818e..098c6b7017662755f78c4921f84dea08b9041235 100644 --- a/ecmascript/dfx/hprof/heap_profiler.cpp +++ b/ecmascript/dfx/hprof/heap_profiler.cpp @@ -24,19 +24,99 @@ #include "ecmascript/mem/heap-inl.h" namespace panda::ecmascript { -HeapProfiler::HeapProfiler(const EcmaVM *vm) : vm_(vm), chunk_(vm->GetNativeAreaAllocator()) +std::pair EntryIdMap::FindId(Address addr) { - jsonSerializer_ = GetChunk()->New(); - if (UNLIKELY(jsonSerializer_ == nullptr)) { - LOG_FULL(FATAL) << "alloc snapshot json serializer failed"; - UNREACHABLE(); + auto it = idMap_.find(addr); + if (it == idMap_.end()) { + return std::make_pair(false, GetNextId()); // return nextId if entry not exits + } else { + return std::make_pair(true, it->second); + } +} + +bool EntryIdMap::InsertId(Address addr, uint32_t id) +{ + auto it = idMap_.find(addr); + if (it == idMap_.end()) { + idMap_.emplace(addr, id); + return true; + } + idMap_[addr] = id; + return false; +} + +bool EntryIdMap::EraseId(Address addr) +{ + auto it = idMap_.find(addr); + if (it == idMap_.end()) { + return false; + } + idMap_.erase(it); + return true; +} + +bool EntryIdMap::Move(Address oldAddr, Address forwardAddr) +{ + if (oldAddr == forwardAddr) { + return true; + } + auto it = idMap_.find(oldAddr); + if (it != idMap_.end()) { + uint32_t id = it->second; + idMap_.erase(it); + idMap_[forwardAddr] = id; + return true; + } + return false; +} + +void EntryIdMap::RemoveDeadEntryId(HeapSnapshot *snapshot) +{ + auto nodes = snapshot->GetNodes(); + CUnorderedMap newIdMap; + for (auto node : *nodes) { + auto addr = node->GetAddress(); + auto it = idMap_.find(addr); + if (it != idMap_.end()) { + newIdMap.emplace(addr, it->second); + } } + idMap_.clear(); + idMap_ = newIdMap; } + +HeapProfiler::HeapProfiler(const EcmaVM *vm) : vm_(vm), chunk_(vm->GetNativeAreaAllocator()) +{ + isProfiling_ = false; + entryIdMap_ = GetChunk()->New(); +} + HeapProfiler::~HeapProfiler() { ClearSnapshot(); - GetChunk()->Delete(jsonSerializer_); - jsonSerializer_ = nullptr; + GetChunk()->Delete(entryIdMap_); +} + +void HeapProfiler::AllocationEvent(TaggedObject *address, size_t size) +{ + DISALLOW_GARBAGE_COLLECTION; + if (isProfiling_) { + // Id will be allocated later while add new node + if (heapTracker_ != nullptr) { + heapTracker_->AllocationEvent(address, size); + } + } +} + +void HeapProfiler::MoveEvent(uintptr_t address, TaggedObject *forwardAddress, size_t size) +{ + LockHolder lock(mutex_); + if (isProfiling_) { + entryIdMap_->Move(address, reinterpret_cast
(forwardAddress)); + if (heapTracker_ != nullptr) { + heapTracker_->MoveEvent(address, forwardAddress, size); + } + } } void HeapProfiler::UpdateHeapObjects(HeapSnapshot *snapshot) @@ -47,41 +127,46 @@ void HeapProfiler::UpdateHeapObjects(HeapSnapshot *snapshot) } bool HeapProfiler::DumpHeapSnapshot(DumpFormat dumpFormat, Stream *stream, Progress *progress, - bool isVmMode, bool isPrivate) + bool isVmMode, bool isPrivate, bool captureNumericValue) { [[maybe_unused]] bool heapClean = ForceFullGC(vm_); ASSERT(heapClean); LOG_ECMA(INFO) << "HeapProfiler DumpSnapshot start"; size_t heapSize = vm_->GetHeap()->GetHeapObjectSize(); - LOG_ECMA(ERROR) << "HeapProfiler DumpSnapshot heap size " << heapSize; - int32_t heapCount = vm_->GetHeap()->GetHeapObjectCount(); + LOG_ECMA(INFO) << "HeapProfiler DumpSnapshot heap size " << heapSize; + int32_t heapCount = static_cast(vm_->GetHeap()->GetHeapObjectCount()); if (progress != nullptr) { progress->ReportProgress(0, heapCount); } - HeapSnapshot *snapshot = MakeHeapSnapshot(SampleType::ONE_SHOT, isVmMode, isPrivate); + HeapSnapshot *snapshot = MakeHeapSnapshot(SampleType::ONE_SHOT, isVmMode, isPrivate, captureNumericValue); + ASSERT(snapshot != nullptr); + entryIdMap_->RemoveDeadEntryId(snapshot); + isProfiling_ = true; if (progress != nullptr) { progress->ReportProgress(heapCount, heapCount); } - ASSERT(snapshot != nullptr); if (!stream->Good()) { FileStream newStream(GenDumpFileName(dumpFormat)); - return jsonSerializer_->Serialize(snapshot, &newStream); + auto serializerResult = HeapSnapshotJSONSerializer::Serialize(snapshot, &newStream); + GetChunk()->Delete(snapshot); + return serializerResult; } - return jsonSerializer_->Serialize(snapshot, stream); + auto serializerResult = HeapSnapshotJSONSerializer::Serialize(snapshot, stream); + GetChunk()->Delete(snapshot); + return serializerResult; } bool HeapProfiler::StartHeapTracking(double timeInterval, bool isVmMode, Stream *stream, bool traceAllocation, bool newThread) { - HeapSnapshot *snapshot = MakeHeapSnapshot(SampleType::REAL_TIME, isVmMode, false, traceAllocation); + HeapSnapshot *snapshot = MakeHeapSnapshot(SampleType::REAL_TIME, isVmMode, false, false, traceAllocation); if (snapshot == nullptr) { return false; } - + isProfiling_ = true; UpdateHeapObjects(snapshot); heapTracker_ = std::make_unique(snapshot, timeInterval, stream); - const_cast(vm_)->StartHeapTracking(heapTracker_.get()); - + const_cast(vm_)->StartHeapTracking(); if (newThread) { heapTracker_->StartTracing(); } @@ -91,11 +176,13 @@ bool HeapProfiler::StartHeapTracking(double timeInterval, bool isVmMode, Stream bool HeapProfiler::UpdateHeapTracking(Stream *stream) { - HeapSnapshot *snapshot = hprofs_.at(0); + if (heapTracker_ == nullptr) { + return false; + } + HeapSnapshot *snapshot = heapTracker_->GetHeapSnapshot(); if (snapshot == nullptr) { return false; } - snapshot->RecordSampleTime(); UpdateHeapObjects(snapshot); @@ -111,14 +198,14 @@ bool HeapProfiler::StopHeapTracking(Stream *stream, Progress *progress, bool new if (heapTracker_ == nullptr) { return false; } - int32_t heapCount = vm_->GetHeap()->GetHeapObjectCount(); + int32_t heapCount = static_cast(vm_->GetHeap()->GetHeapObjectCount()); const_cast(vm_)->StopHeapTracking(); if (newThread) { heapTracker_->StopTracing(); } - HeapSnapshot *snapshot = hprofs_.at(0); + HeapSnapshot *snapshot = heapTracker_->GetHeapSnapshot(); if (snapshot == nullptr) { return false; } @@ -127,10 +214,11 @@ bool HeapProfiler::StopHeapTracking(Stream *stream, Progress *progress, bool new progress->ReportProgress(0, heapCount); } snapshot->FinishSnapshot(); + isProfiling_ = false; if (progress != nullptr) { progress->ReportProgress(heapCount, heapCount); } - return jsonSerializer_->Serialize(snapshot, stream); + return HeapSnapshotJSONSerializer::Serialize(snapshot, stream); } std::string HeapProfiler::GenDumpFileName(DumpFormat dumpFormat) @@ -189,24 +277,26 @@ bool HeapProfiler::ForceFullGC(const EcmaVM *vm) return false; } -HeapSnapshot *HeapProfiler::MakeHeapSnapshot(SampleType sampleType, bool isVmMode, bool isPrivate, bool traceAllocation) +HeapSnapshot *HeapProfiler::MakeHeapSnapshot(SampleType sampleType, bool isVmMode, bool isPrivate, + bool captureNumericValue, bool traceAllocation) { LOG_ECMA(INFO) << "HeapProfiler::MakeHeapSnapshot"; DISALLOW_GARBAGE_COLLECTION; const_cast(vm_->GetHeap())->Prepare(); switch (sampleType) { case SampleType::ONE_SHOT: { - auto *snapshot = GetChunk()->New(vm_, isVmMode, isPrivate, traceAllocation, GetChunk()); + auto *snapshot = GetChunk()->New(vm_, isVmMode, isPrivate, captureNumericValue, + traceAllocation, entryIdMap_, GetChunk()); if (snapshot == nullptr) { LOG_FULL(FATAL) << "alloc snapshot failed"; UNREACHABLE(); } snapshot->BuildUp(); - AddSnapshot(snapshot); return snapshot; } case SampleType::REAL_TIME: { - auto *snapshot = GetChunk()->New(vm_, isVmMode, isPrivate, traceAllocation, GetChunk()); + auto *snapshot = GetChunk()->New(vm_, isVmMode, isPrivate, captureNumericValue, + traceAllocation, entryIdMap_, GetChunk()); if (snapshot == nullptr) { LOG_FULL(FATAL) << "alloc snapshot failed"; UNREACHABLE(); diff --git a/ecmascript/dfx/hprof/heap_profiler.h b/ecmascript/dfx/hprof/heap_profiler.h index d5d1dcae0384acbee3044c7218815ee1dbd6bfec..924671c5c94c4eae67c347a31f52e21fb1903be2 100644 --- a/ecmascript/dfx/hprof/heap_profiler.h +++ b/ecmascript/dfx/hprof/heap_profiler.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_HPROF_HEAP_PROFILER_H -#define ECMASCRIPT_HPROF_HEAP_PROFILER_H +#ifndef ECMASCRIPT_DFX_HPROF_HEAP_PROFILER_H +#define ECMASCRIPT_DFX_HPROF_HEAP_PROFILER_H #include "ecmascript/dfx/hprof/heap_profiler_interface.h" #include "ecmascript/dfx/hprof/heap_snapshot_json_serializer.h" @@ -29,6 +29,38 @@ namespace panda::ecmascript { class HeapSnapshot; class EcmaVM; +class EntryIdMap { +public: + EntryIdMap() = default; + ~EntryIdMap() = default; + NO_COPY_SEMANTIC(EntryIdMap); + NO_MOVE_SEMANTIC(EntryIdMap); + + static constexpr uint32_t SEQ_STEP = 2; + std::pair FindId(Address addr); + bool InsertId(Address addr, uint32_t id); + bool EraseId(Address addr); + bool Move(Address oldAddr, Address forwardAddr); + void RemoveDeadEntryId(HeapSnapshot *snapshot); + uint32_t GetNextId() + { + nextId_ += SEQ_STEP; + return nextId_ - SEQ_STEP; + } + uint32_t GetLastId() + { + return nextId_ - SEQ_STEP; + } + size_t GetIdCount() + { + return idMap_.size(); + } + +private: + uint32_t nextId_ {3U}; // 1 Reversed for SyntheticRoot + CUnorderedMap idMap_ {}; +}; + class HeapProfiler : public HeapProfilerInterface { public: NO_MOVE_SEMANTIC(HeapProfiler); @@ -37,11 +69,14 @@ public: ~HeapProfiler() override; enum class SampleType { ONE_SHOT, REAL_TIME }; + + void AllocationEvent(TaggedObject *address, size_t size) override; + void MoveEvent(uintptr_t address, TaggedObject *forwardAddress, size_t size) override; /** * dump the specific snapshot in target format */ bool DumpHeapSnapshot(DumpFormat dumpFormat, Stream *stream, Progress *progress = nullptr, - bool isVmMode = true, bool isPrivate = false) override; + bool isVmMode = true, bool isPrivate = false, bool captureNumericValue = false) override; void AddSnapshot(HeapSnapshot *snapshot); @@ -52,6 +87,14 @@ public: bool StartHeapSampling(uint64_t samplingInterval, int stackDepth = 128) override; void StopHeapSampling() override; const struct SamplingInfo *GetAllocationProfile() override; + size_t GetIdCount() override + { + return entryIdMap_->GetIdCount(); + } + EntryIdMap *GetEntryIdMap() const + { + return const_cast(entryIdMap_); + } Chunk *GetChunk() const { return const_cast(&chunk_); @@ -66,7 +109,8 @@ private: * make a new heap snapshot and put it into a container eg, vector */ HeapSnapshot *MakeHeapSnapshot(SampleType sampleType, bool isVmMode = true, - bool isPrivate = false, bool traceAllocation = false); + bool isPrivate = false, bool captureNumericValue = false, + bool traceAllocation = false); std::string GenDumpFileName(DumpFormat dumpFormat); CString GetTimeStamp(); void UpdateHeapObjects(HeapSnapshot *snapshot); @@ -75,10 +119,12 @@ private: const size_t MAX_NUM_HPROF = 5; // ~10MB const EcmaVM *vm_; CVector hprofs_; - HeapSnapshotJSONSerializer *jsonSerializer_ {nullptr}; + bool isProfiling_ {false}; + EntryIdMap* entryIdMap_; std::unique_ptr heapTracker_; Chunk chunk_; std::unique_ptr heapSampling_ {nullptr}; + Mutex mutex_; }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_HPROF_HEAP_PROFILER_H +#endif // ECMASCRIPT_DFX_HPROF_HEAP_PROFILER_H diff --git a/ecmascript/dfx/hprof/heap_profiler_interface.h b/ecmascript/dfx/hprof/heap_profiler_interface.h index 2650615553a4567606c606a5f665a2670698cdc1..385161cc78a54e287e1685188583ee095e1733a0 100644 --- a/ecmascript/dfx/hprof/heap_profiler_interface.h +++ b/ecmascript/dfx/hprof/heap_profiler_interface.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_HPROF_HEAP_PROFILER_INTERFACE_H -#define ECMASCRIPT_HPROF_HEAP_PROFILER_INTERFACE_H +#ifndef ECMASCRIPT_DFX_HPROF_HEAP_PROFILER_INTERFACE_H +#define ECMASCRIPT_DFX_HPROF_HEAP_PROFILER_INTERFACE_H #include @@ -22,6 +22,7 @@ namespace panda::ecmascript { class EcmaVM; +class TaggedObject; class Progress; class Stream; struct SamplingInfo; @@ -36,8 +37,11 @@ public: HeapProfilerInterface() = default; virtual ~HeapProfilerInterface() = default; + virtual size_t GetIdCount() = 0; + virtual void AllocationEvent(TaggedObject *address, size_t size) = 0; + virtual void MoveEvent(uintptr_t address, TaggedObject *forwardAddress, size_t size)= 0; virtual bool DumpHeapSnapshot(DumpFormat dumpFormat, Stream *stream, Progress *progress = nullptr, - bool isVmMode = true, bool isPrivate = false) = 0; + bool isVmMode = true, bool isPrivate = false, bool captureNumericValue = false) = 0; virtual bool StartHeapTracking(double timeInterval, bool isVmMode = true, Stream *stream = nullptr, bool traceAllocation = false, bool newThread = true) = 0; @@ -51,4 +55,4 @@ public: NO_COPY_SEMANTIC(HeapProfilerInterface); }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_HPROF_HEAP_PROFILER_INTERFACE_H +#endif // ECMASCRIPT_DFX_HPROF_HEAP_PROFILER_INTERFACE_H diff --git a/ecmascript/dfx/hprof/heap_root_visitor.h b/ecmascript/dfx/hprof/heap_root_visitor.h index e929db65b3eaf2b5b21c41c42b74d038917719ce..a2b4d6f1d2232c36b63f857b6099820ee3319748 100644 --- a/ecmascript/dfx/hprof/heap_root_visitor.h +++ b/ecmascript/dfx/hprof/heap_root_visitor.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_HPROF_HEAP_ROOT_VISITOR_H -#define ECMASCRIPT_HPROF_HEAP_ROOT_VISITOR_H +#ifndef ECMASCRIPT_DFX_HPROF_HEAP_ROOT_VISITOR_H +#define ECMASCRIPT_DFX_HPROF_HEAP_ROOT_VISITOR_H #include "ecmascript/ecma_vm.h" #include "ecmascript/mem/visitor.h" @@ -35,4 +35,4 @@ private: EcmaVM *GetVMInstance(JSThread *thread) const; }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_HPROF_HEAP_ROOT_VISITOR_H +#endif // ECMASCRIPT_DFX_HPROF_HEAP_ROOT_VISITOR_H diff --git a/ecmascript/dfx/hprof/heap_sampling.cpp b/ecmascript/dfx/hprof/heap_sampling.cpp index d1dfee57d2fea6eef522e1fcba072a2ce4566ab5..cbe178a6852d3ca2a84d0583b22e50fde9172755 100644 --- a/ecmascript/dfx/hprof/heap_sampling.cpp +++ b/ecmascript/dfx/hprof/heap_sampling.cpp @@ -94,13 +94,13 @@ void HeapSampling::GetStack() if (method == nullptr) { continue; } - const JSPandaFile *jsPandaFile = method->GetJSPandaFile(); + bool isNative = method->IsNativeWithCallField(); struct MethodKey methodKey; if (topFrame) { - methodKey.state = JsStackGetter::GetRunningState(it, vm_, jsPandaFile, true); + methodKey.state = JsStackGetter::GetRunningState(it, vm_, isNative, true); topFrame = false; } else { - methodKey.state = JsStackGetter::GetRunningState(it, vm_, jsPandaFile, false); + methodKey.state = JsStackGetter::GetRunningState(it, vm_, isNative, false); } void *methodIdentifier = JsStackGetter::GetMethodIdentifier(method, it); if (methodIdentifier == nullptr) { diff --git a/ecmascript/dfx/hprof/heap_sampling.h b/ecmascript/dfx/hprof/heap_sampling.h index d5b7c99c17949ae660c93d20ee541c1e8a404d79..372ef0dd72002832a09cc843861d5b234b9cecc0 100644 --- a/ecmascript/dfx/hprof/heap_sampling.h +++ b/ecmascript/dfx/hprof/heap_sampling.h @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_HPROF_HEAP_SAMPLING_H -#define ECMASCRIPT_HPROF_HEAP_SAMPLING_H +#ifndef ECMASCRIPT_DFX_HPROF_HEAP_SAMPLING_H +#define ECMASCRIPT_DFX_HPROF_HEAP_SAMPLING_H #include "ecmascript/dfx/stackinfo/js_stackgetter.h" #include "ecmascript/mem/heap.h" @@ -23,6 +23,7 @@ namespace panda::ecmascript { struct CallFrameInfo { std::string codeType_ = ""; std::string functionName_ = ""; + std::string moduleName_ = ""; int columnNumber_ = -1; int lineNumber_ = -1; int scriptId_ = 0; @@ -91,4 +92,4 @@ private: CVector frameInfoTemps_; }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_HPROF_HEAP_SAMPLING_H \ No newline at end of file +#endif // ECMASCRIPT_DFX_HPROF_HEAP_SAMPLING_H \ No newline at end of file diff --git a/ecmascript/dfx/hprof/heap_snapshot.cpp b/ecmascript/dfx/hprof/heap_snapshot.cpp index 57f552d167e79a05f66016c95e15f27eeb0857a2..6f8f2b643e7209e3f0fba93f78dd58361f45a90e 100644 --- a/ecmascript/dfx/hprof/heap_snapshot.cpp +++ b/ecmascript/dfx/hprof/heap_snapshot.cpp @@ -61,7 +61,7 @@ Node *Node::NewNode(Chunk *chunk, size_t id, size_t index, const CString *name, return node; } -Edge *Edge::NewEdge(Chunk *chunk, uint64_t id, EdgeType type, Node *from, Node *to, CString *name) +Edge *Edge::NewEdge(Chunk *chunk, uint32_t id, EdgeType type, Node *from, Node *to, CString *name) { auto edge = chunk->New(id, type, from, to, name); if (UNLIKELY(edge == nullptr)) { @@ -71,6 +71,16 @@ Edge *Edge::NewEdge(Chunk *chunk, uint64_t id, EdgeType type, Node *from, Node * return edge; } +Edge *Edge::NewEdge(Chunk *chunk, uint32_t id, EdgeType type, Node *from, Node *to, uint32_t index) +{ + auto edge = chunk->New(id, type, from, to, index); + if (UNLIKELY(edge == nullptr)) { + LOG_FULL(FATAL) << "internal allocator failed"; + UNREACHABLE(); + } + return edge; +} + HeapSnapshot::~HeapSnapshot() { for (Node *node : nodes_) { @@ -86,6 +96,7 @@ HeapSnapshot::~HeapSnapshot() scriptIdMap_.clear(); methodToTraceNodeId_.clear(); traceNodeIndex_.clear(); + entryIdMap_ = nullptr; chunk_ = nullptr; } @@ -121,6 +132,7 @@ void HeapSnapshot::UpdateNodes(bool isInFinish) if (!(*iter)->IsLive()) { Node *node = entryMap_.FindAndEraseNode((*iter)->GetAddress()); ASSERT(*iter == node); + entryIdMap_->EraseId((*iter)->GetAddress()); if (node != nullptr) { DecreaseNodeSize(node->GetSelfSize()); chunk_->Delete(node); @@ -143,7 +155,7 @@ bool HeapSnapshot::FinishSnapshot() void HeapSnapshot::RecordSampleTime() { - timeStamps_.emplace_back(sequenceId_); + timeStamps_.emplace_back(entryIdMap_->GetLastId()); } void HeapSnapshot::PushHeapStat(Stream* stream) @@ -161,8 +173,8 @@ void HeapSnapshot::PushHeapStat(Stream* stream) TimeStamp& timeStamp = timeStamps_[timeIndex]; sequenceId = timeStamp.GetLastSequenceId(); timeStampUs = timeStamp.GetTimeStamp(); - int32_t nodesSize = 0; - int32_t nodesCount = 0; + uint32_t nodesSize = 0; + uint32_t nodesCount = 0; while (iter != nodes_.end() && (*iter)->GetId() <= static_cast(sequenceId)) { nodesCount++; nodesSize += (*iter)->GetSelfSize(); @@ -198,7 +210,7 @@ void HeapSnapshot::MoveNode(uintptr_t address, TaggedObject *forwardAddress, siz Node *node = entryMap_.FindAndEraseNode(address); if (node != nullptr) { - ASSERT(node->GetId() <= static_cast(INT_MAX)); + ASSERT(node->GetId() <= UINT_MAX); Node *oldNode = entryMap_.FindAndEraseNode(Node::NewAddress(forwardAddress)); if (oldNode != nullptr) { @@ -215,7 +227,6 @@ void HeapSnapshot::MoveNode(uintptr_t address, TaggedObject *forwardAddress, siz node->SetSelfSize(size); } node->SetAddress(Node::NewAddress(forwardAddress)); - entryMap_.InsertEntry(node); } else { LOG_DEBUGGER(WARN) << "Untracked object moves from " << address << " to " << forwardAddress; @@ -236,6 +247,8 @@ CString *HeapSnapshot::GenerateNodeName(TaggedObject *entry) return GetArrayString(TaggedArray::Cast(entry), "LexicalEnv["); case JSType::CONSTANT_POOL: return GetArrayString(TaggedArray::Cast(entry), "ConstantPool["); + case JSType::PROFILE_TYPE_INFO: + return GetArrayString(TaggedArray::Cast(entry), "ProfileTypeInfo["); case JSType::TAGGED_DICTIONARY: return GetArrayString(TaggedArray::Cast(entry), "TaggedDict["); case JSType::AOT_LITERAL_INFO: @@ -249,6 +262,7 @@ CString *HeapSnapshot::GenerateNodeName(TaggedObject *entry) case JSType::LINE_STRING: case JSType::CONSTANT_STRING: case JSType::TREE_STRING: + case JSType::SLICED_STRING: return GetString("BaseString"); case JSType::JS_OBJECT: { CString objName = CString("JSOBJECT(Ctor="); // Ctor-name @@ -299,7 +313,7 @@ CString *HeapSnapshot::GenerateNodeName(TaggedObject *entry) case JSType::JS_ARRAY: { JSArray *jsArray = JSArray::Cast(entry); CString jsArrayName("JSArray["); - jsArrayName.append(ToCString(jsArray->GetLength().GetInt())); + jsArrayName.append(ToCString(jsArray->GetLength())); jsArrayName.append("]"); return GetString(jsArrayName); } @@ -375,6 +389,8 @@ CString *HeapSnapshot::GenerateNodeName(TaggedObject *entry) return GetString("PromiseReactionsFunction"); case JSType::JS_PROMISE_EXECUTOR_FUNCTION: return GetString("PromiseExecutorFunction"); + case JSType::JS_ASYNC_FROM_SYNC_ITER_UNWARP_FUNCTION: + return GetString("AsyncFromSyncIterUnwarpFunction"); case JSType::JS_PROMISE_ALL_RESOLVE_ELEMENT_FUNCTION: return GetString("PromiseAllResolveElementFunction"); case JSType::JS_PROMISE_ANY_REJECT_ELEMENT_FUNCTION: @@ -534,6 +550,8 @@ CString *HeapSnapshot::GenerateNodeName(TaggedObject *entry) return GetString("StoreTSHandler"); case JSType::PROTO_CHANGE_MARKER: return GetString("ProtoChangeMarker"); + case JSType::MARKER_CELL: + return GetString("MarkerCell"); case JSType::PROTOTYPE_INFO: return GetString("ProtoChangeDetails"); case JSType::TEMPLATE_MAP: @@ -587,26 +605,23 @@ void HeapSnapshot::FillNodes(bool isInFinish) auto heap = vm_->GetHeap(); if (heap != nullptr) { heap->IterateOverObjects([this, isInFinish](TaggedObject *obj) { - GenerateNode(JSTaggedValue(obj), 0, -1, isInFinish); + GenerateNode(JSTaggedValue(obj), 0, isInFinish); }); } } -Node *HeapSnapshot::GenerateNode(JSTaggedValue entry, size_t size, int sequenceId, bool isInFinish) +Node *HeapSnapshot::GenerateNode(JSTaggedValue entry, size_t size, bool isInFinish) { Node *node = nullptr; - if (sequenceId == -1) { - sequenceId = sequenceId_ + SEQ_STEP; - } if (entry.IsHeapObject()) { if (entry.IsWeak()) { entry.RemoveWeakTag(); } if (entry.IsString()) { if (isPrivate_) { - node = GeneratePrivateStringNode(size, sequenceId); + node = GeneratePrivateStringNode(size); } else { - node = GenerateStringNode(entry, size, sequenceId, isInFinish); + node = GenerateStringNode(entry, size, isInFinish); } if (node == nullptr) { LOG_ECMA(DEBUG) << "string node nullptr"; @@ -618,14 +633,15 @@ Node *HeapSnapshot::GenerateNode(JSTaggedValue entry, size_t size, int sequenceI if (baseClass != nullptr) { Address addr = reinterpret_cast
(obj); Node *existNode = entryMap_.FindEntry(addr); // Fast Index + auto [idExist, sequenceId] = entryIdMap_->FindId(addr); if (existNode == nullptr) { size_t selfSize = (size != 0) ? size : obj->GetClass()->SizeFromJSHClass(obj); node = Node::NewNode(chunk_, sequenceId, nodeCount_, GenerateNodeName(obj), GenerateNodeType(obj), selfSize, obj); - if (sequenceId == sequenceId_ + SEQ_STEP) { - sequenceId_ = sequenceId; // Odd Digit - } entryMap_.InsertEntry(node); + if (!idExist) { + entryIdMap_->InsertId(addr, sequenceId); + } InsertNodeUnique(node); ASSERT(entryMap_.FindEntry(node->GetAddress())->GetAddress() == node->GetAddress()); } else { @@ -636,11 +652,17 @@ Node *HeapSnapshot::GenerateNode(JSTaggedValue entry, size_t size, int sequenceI } else { CString primitiveName; if (entry.IsInt()) { + if (!captureNumericValue_) { + return nullptr; + } primitiveName.append("Int:"); if (!isPrivate_) { primitiveName.append(ToCString(entry.GetInt())); } } else if (entry.IsDouble()) { + if (!captureNumericValue_) { + return nullptr; + } primitiveName.append("Double:"); if (!isPrivate_) { primitiveName.append(FloatToCString(entry.GetDouble())); @@ -668,12 +690,13 @@ Node *HeapSnapshot::GenerateNode(JSTaggedValue entry, size_t size, int sequenceI existNode->SetLive(true); return existNode; } - + Address addr = reinterpret_cast
(obj); + auto [idExist, sequenceId] = entryIdMap_->FindId(addr); node = Node::NewNode(chunk_, sequenceId, nodeCount_, GetString(primitiveName), NodeType::JS_PRIMITIVE_REF, 0, obj); entryMap_.InsertEntry(node); // Fast Index - if (sequenceId == sequenceId_ + SEQ_STEP) { - sequenceId_ = sequenceId; // Odd Digit + if (!idExist) { + entryIdMap_->InsertId(addr, sequenceId); } InsertNodeUnique(node); } @@ -758,10 +781,14 @@ int HeapSnapshot::AddTraceNode(int sequenceId, int size) if (method == nullptr || method->IsNativeWithCallField()) { continue; } - MethodLiteral *methodLiteral = method->GetMethodLiteral(); + if (methodLiteral == nullptr) { + continue; + } if (stackInfo_.count(methodLiteral) == 0) { - AddMethodInfo(methodLiteral, it, method->GetJSPandaFile(), sequenceId); + if (!AddMethodInfo(methodLiteral, method->GetJSPandaFile(), sequenceId)) { + continue; + } } AddTraceNodeId(methodLiteral); } @@ -779,8 +806,7 @@ int HeapSnapshot::AddTraceNode(int sequenceId, int size) return topNode->GetId(); } -void HeapSnapshot::AddMethodInfo(MethodLiteral *methodLiteral, - const FrameIterator &it, +bool HeapSnapshot::AddMethodInfo(MethodLiteral *methodLiteral, const JSPandaFile *jsPandaFile, int sequenceId) { @@ -814,32 +840,19 @@ void HeapSnapshot::AddMethodInfo(MethodLiteral *methodLiteral, GetString(codeEntry.scriptName.c_str()); // line number - int32_t lineNumber = 0; - int32_t columnNumber = 0; - auto callbackLineFunc = [&](int32_t line) -> bool { - lineNumber = line + 1; - return true; - }; - auto callbackColumnFunc = [&](int32_t column) -> bool { - columnNumber = column + 1; - return true; - }; - uint32_t offset = it.GetBytecodeOffset(); - if (!debugExtractor->MatchLineWithOffset(callbackLineFunc, methodId, offset) || - !debugExtractor->MatchColumnWithOffset(callbackColumnFunc, methodId, offset)) { - codeEntry.lineNumber = 0; - codeEntry.columnNumber = 0; - } else { - codeEntry.lineNumber = lineNumber; - codeEntry.columnNumber = columnNumber; + codeEntry.lineNumber = debugExtractor->GetFristLine(methodId); + // lineNumber is 0 means that lineTable error or empty function body, so jump this frame. + if (UNLIKELY(codeEntry.lineNumber == 0)) { + return false; } + codeEntry.columnNumber = debugExtractor->GetFristColumn(methodId); traceInfoStack_.emplace_back(codeEntry); stackInfo_.emplace(methodLiteral, codeEntry); - return; + return true; } -Node *HeapSnapshot::GenerateStringNode(JSTaggedValue entry, size_t size, int sequenceId, bool isInFinish) +Node *HeapSnapshot::GenerateStringNode(JSTaggedValue entry, size_t size, bool isInFinish) { static const CString EMPTY_STRING; @@ -859,17 +872,19 @@ Node *HeapSnapshot::GenerateStringNode(JSTaggedValue entry, size_t size, int seq if (isInFinish) { nodeName = GetString(EntryVisitor::ConvertKey(entry)); } + Address addr = reinterpret_cast
(entry.GetTaggedObject()); + auto [idExist, sequenceId] = entryIdMap_->FindId(addr); Node *node = Node::NewNode(chunk_, sequenceId, nodeCount_, nodeName, NodeType::PRIM_STRING, selfsize, entry.GetTaggedObject()); - if (sequenceId == sequenceId_ + SEQ_STEP) { - sequenceId_ = sequenceId; // Odd Digit + if (!idExist) { + entryIdMap_->InsertId(addr, sequenceId); } entryMap_.InsertEntry(node); InsertNodeUnique(node); return node; } -Node *HeapSnapshot::GeneratePrivateStringNode(size_t size, int sequenceId) +Node *HeapSnapshot::GeneratePrivateStringNode(size_t size) { if (privateStringNode_ != nullptr) { return privateStringNode_; @@ -880,13 +895,16 @@ Node *HeapSnapshot::GeneratePrivateStringNode(size_t size, int sequenceId) size_t selfsize = (size != 0) ? size : EcmaStringAccessor(originStr).GetFlatStringSize(); CString strContent; strContent.append(EntryVisitor::ConvertKey(stringValue)); + Address addr = reinterpret_cast
(stringValue.GetTaggedObject()); + auto [idExist, sequenceId] = entryIdMap_->FindId(addr); node = Node::NewNode(chunk_, sequenceId, nodeCount_, GetString(strContent), NodeType::PRIM_STRING, selfsize, stringValue.GetTaggedObject()); Node *existNode = entryMap_.FindOrInsertNode(node); // Fast Index if (existNode == node) { - if (sequenceId == sequenceId_ + SEQ_STEP) { - sequenceId_ = sequenceId; // Odd Digit + if (!idExist) { + entryIdMap_->InsertId(addr, sequenceId); } + entryMap_.InsertEntry(node); InsertNodeUnique(node); } else { existNode->SetLive(true); @@ -910,12 +928,16 @@ void HeapSnapshot::FillEdges() size_t count = 0; while (++count < length) { ASSERT(*iter != nullptr); - auto *objFrom = reinterpret_cast((*iter)->GetAddress()); - std::vector> nameResources; - JSTaggedValue(objFrom).DumpForSnapshot(nameResources, isVmMode_); + auto entryFrom = *iter; + auto *objFrom = reinterpret_cast(entryFrom->GetAddress()); + std::vector referenceResources; JSTaggedValue objValue(objFrom); - for (auto const &it : nameResources) { - JSTaggedValue toValue = it.second; + objValue.DumpForSnapshot(referenceResources, isVmMode_); + for (auto const &it : referenceResources) { + JSTaggedValue toValue = it.value_; + if (toValue.IsNumber() && !captureNumericValue_) { + continue; + } Node *entryTo = nullptr; if (toValue.IsWeak()) { toValue.RemoveWeakTag(); @@ -925,35 +947,37 @@ void HeapSnapshot::FillEdges() entryTo = entryMap_.FindEntry(Node::NewAddress(to)); } if (entryTo == nullptr) { - entryTo = GenerateNode(toValue, 0, -1, true); + entryTo = GenerateNode(toValue, 0, true); } if (entryTo != nullptr) { - Edge *edge = Edge::NewEdge(chunk_, edgeCount_, EdgeType::DEFAULT, *iter, entryTo, GetString(it.first)); + Edge *edge = (it.type_ == Reference::ReferenceType::ELEMENT) ? + Edge::NewEdge(chunk_, edgeCount_, (EdgeType)it.type_, entryFrom, entryTo, it.index_) : + Edge::NewEdge(chunk_, edgeCount_, (EdgeType)it.type_, entryFrom, entryTo, GetString(it.name_)); + RenameFunction(it.name_, entryFrom, entryTo); InsertEdgeUnique(edge); (*iter)->IncEdgeCount(); // Update Node's edgeCount_ here } } iter++; } - // Fill Primitive Edge - size_t lengthExtend = nodes_.size(); - while (++count < lengthExtend) { - ASSERT(*iter != nullptr); - if ((*iter)->GetType() == NodeType::JS_PRIMITIVE_REF) { - JSTaggedValue jsFrom(reinterpret_cast((*iter)->GetAddress())); - CString valueName; - if (jsFrom.IsInt()) { - valueName.append(ToCString(jsFrom.GetInt())); - } else if (jsFrom.IsDouble()) { - valueName.append(FloatToCString(jsFrom.GetDouble())); - } else { - valueName.append("NaN"); - } - Edge *edge = Edge::NewEdge(chunk_, edgeCount_, EdgeType::DEFAULT, (*iter), (*iter), GetString(valueName)); - InsertEdgeUnique(edge); - (*iter)->IncEdgeCount(); // Update Node's edgeCount_ here - } - iter++; +} + +void HeapSnapshot::RenameFunction(const CString &edgeName, Node *entryFrom, Node *entryTo) +{ + if (edgeName != "name") { + return; + } + auto fromName = *entryFrom->GetName(); + if (*entryTo->GetName() != "" && (fromName == "JSFunctionBase" || + fromName == "JSFunction" || fromName == "PromiseValueThunkOrThrowerFunction" || + fromName == "JSGeneratorFunction" || fromName == "PromiseAllSettledElementFunction" || + fromName == "Bound Function" || fromName == "PromiseAnyRejectElementFunction" || + fromName == "PromiseReactionsFunction" || fromName == "PromiseExecutorFunction" || + fromName == "PromiseAllResolveElementFunction" || fromName == "AsyncFunction" || + fromName == "ProxyRevocFunction" || fromName == "AsyncFromSyncIterUnwarpFunction" || + fromName == "PromiseFinallyFunction" || fromName == "JSIntlBoundFunction" || + fromName == "JSAsyncGeneratorFunction" || fromName == "AsyncAwaitStatusFunction")) { + entryFrom->SetName(GetString(*entryTo->GetName())); } } @@ -1131,11 +1155,11 @@ FrontType NodeTypeConverter::Convert(NodeType type) } else if (type == NodeType::JS_OBJECT) { fType = FrontType::OBJECT; } else if (type >= NodeType::JS_FUNCTION_FIRST && type <= NodeType::JS_FUNCTION_LAST) { - fType = FrontType::CLOSURE; + fType = FrontType::DEFAULT; } else if (type == NodeType::JS_BOUND_FUNCTION) { - fType = FrontType::CLOSURE; + fType = FrontType::DEFAULT; } else if (type == NodeType::JS_FUNCTION_BASE) { - fType = FrontType::CLOSURE; + fType = FrontType::DEFAULT; } else if (type == NodeType::JS_REG_EXP) { fType = FrontType::REGEXP; } else if (type == NodeType::SYMBOL) { diff --git a/ecmascript/dfx/hprof/heap_snapshot.h b/ecmascript/dfx/hprof/heap_snapshot.h index 4317b874fbc37bbd63124dbdc5b4c6b022153492..464253e3ce201fa0de5ac8e376003f6637e243b0 100644 --- a/ecmascript/dfx/hprof/heap_snapshot.h +++ b/ecmascript/dfx/hprof/heap_snapshot.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_HPROF_HEAP_SNAPSHOT_H -#define ECMASCRIPT_HPROF_HEAP_SNAPSHOT_H +#ifndef ECMASCRIPT_DFX_HPROF_HEAP_SNAPSHOT_H +#define ECMASCRIPT_DFX_HPROF_HEAP_SNAPSHOT_H #include #include @@ -33,6 +33,7 @@ #include "ecmascript/interpreter/frame_handler.h" namespace panda::ecmascript { +class EntryIdMap; // Define the Object Graphic using Address = uintptr_t; @@ -47,7 +48,7 @@ enum class EdgeType { CONTEXT, ELEMENT, PROPERTY, INTERNAL, HIDDEN, SHORTCUT, WE class Node { public: - Node(uint64_t id, uint64_t index, const CString *name, NodeType type, size_t size, uint64_t traceId, + Node(uint32_t id, uint32_t index, const CString *name, NodeType type, size_t size, uint32_t traceId, Address address, bool isLive = true) : id_(id), index_(index), @@ -59,15 +60,15 @@ public: isLive_(isLive) { } - uint64_t GetId() const + uint32_t GetId() const { return id_; } - void SetIndex(uint64_t index) + void SetIndex(uint32_t index) { index_ = index; } - uint64_t GetIndex() const + uint32_t GetIndex() const { return index_; } @@ -102,7 +103,7 @@ public: { edgeCount_++; } - uint64_t GetStackTraceId() const + uint32_t GetStackTraceId() const { return traceId_; } @@ -122,7 +123,7 @@ public: { isLive_ = isLive; } - void SetTraceId(uint64_t traceId) + void SetTraceId(uint32_t traceId) { traceId_ = traceId; } @@ -137,24 +138,24 @@ public: ~Node() = default; private: - uint64_t id_ {0}; // Range from 1 - uint64_t index_ {0}; + uint32_t id_ {0}; // Range from 1 + uint32_t index_ {0}; const CString *name_ {nullptr}; NodeType type_ {NodeType::INVALID}; size_t size_ {0}; size_t edgeCount_ {0}; - uint64_t traceId_ {0}; + uint32_t traceId_ {0}; Address address_ {0x0}; bool isLive_ {true}; }; class Edge { public: - Edge(uint64_t id, EdgeType type, Node *from, Node *to, CString *name) - : id_(id), edgeType_(type), from_(from), to_(to), name_(name) - { - } - uint64_t GetId() const + Edge(uint32_t id, EdgeType type, Node *from, Node *to, CString *name) + : id_(id), edgeType_(type), from_(from), to_(to), name_(name) {} + Edge(uint32_t id, EdgeType type, Node *from, Node *to, uint32_t index) + : id_(id), edgeType_(type), from_(from), to_(to), index_(index) {} + uint32_t GetId() const { return id_; } @@ -172,10 +173,17 @@ public: } const CString *GetName() const { + ASSERT(GetType() != EdgeType::ELEMENT); return name_; } + uint32_t GetIndex() const + { + ASSERT(GetType() == EdgeType::ELEMENT); + return index_; + } void SetName(CString *name) { + ASSERT(GetType() != EdgeType::ELEMENT); name_ = name; } void UpdateFrom(Node *node) @@ -186,16 +194,20 @@ public: { to_ = node; } - static Edge *NewEdge(Chunk *chunk, uint64_t id, EdgeType type, Node *from, Node *to, CString *name); + static Edge *NewEdge(Chunk *chunk, uint32_t id, EdgeType type, Node *from, Node *to, CString *name); + static Edge *NewEdge(Chunk *chunk, uint32_t id, EdgeType type, Node *from, Node *to, uint32_t index); static constexpr int EDGE_FIELD_COUNT = 3; ~Edge() = default; private: - uint64_t id_ {-1ULL}; + uint32_t id_ {-1U}; EdgeType edgeType_ {EdgeType::DEFAULT}; Node *from_ {nullptr}; Node *to_ {nullptr}; - CString *name_ {nullptr}; + union { + CString *name_; + uint32_t index_; + }; }; class TimeStamp { @@ -216,22 +228,22 @@ public: return timeStampUs_; } - int32_t GetSize() const + uint32_t GetSize() const { return size_; } - void SetSize(int32_t size) + void SetSize(uint32_t size) { size_ = size; } - int32_t GetCount() const + uint32_t GetCount() const { return count_; } - void SetCount(int32_t count) + void SetCount(uint32_t count) { count_ = count; } @@ -247,8 +259,8 @@ private: int lastSequenceId_ {0}; int64_t timeStampUs_ {0}; - int32_t size_ {0}; - int32_t count_ {0}; + uint32_t size_ {0}; + uint32_t count_ {0}; }; class HeapEntryMap { @@ -357,17 +369,29 @@ private: TraceNode root_; }; +struct Reference { + enum class ReferenceType { CONTEXT, ELEMENT, PROPERTY, INTERNAL, HIDDEN, SHORTCUT, WEAK, DEFAULT = PROPERTY }; + + Reference(const CString &name, JSTaggedValue value) : name_(name), value_(value) {} + Reference(const CString &name, JSTaggedValue value, ReferenceType type) : name_(name), value_(value), type_(type) {} + Reference(uint32_t index, JSTaggedValue value, ReferenceType type) : index_(index), value_(value), type_(type) {} + + CString name_; + uint32_t index_ {-1U}; + JSTaggedValue value_; + ReferenceType type_ {ReferenceType::DEFAULT}; +}; + class HeapSnapshot { public: static constexpr int SEQ_STEP = 2; NO_MOVE_SEMANTIC(HeapSnapshot); NO_COPY_SEMANTIC(HeapSnapshot); - HeapSnapshot(const EcmaVM *vm, const bool isVmMode, const bool isPrivate, const bool trackAllocations, - Chunk *chunk) - : stringTable_(vm), vm_(vm), isVmMode_(isVmMode), isPrivate_(isPrivate), trackAllocations_(trackAllocations), - chunk_(chunk) - { - } + HeapSnapshot(const EcmaVM *vm, const bool isVmMode, const bool isPrivate, const bool captureNumericValue, + const bool trackAllocations, EntryIdMap *entryIdMap, Chunk *chunk) + : stringTable_(vm), vm_(vm), isVmMode_(isVmMode), isPrivate_(isPrivate), + captureNumericValue_(captureNumericValue), trackAllocations_(trackAllocations), + entryIdMap_(entryIdMap), chunk_(chunk) {} ~HeapSnapshot(); bool BuildUp(); bool Verify(); @@ -380,7 +404,7 @@ public: bool FinishSnapshot(); void PushHeapStat(Stream* stream); int AddTraceNode(int sequenceId, int size); - void AddMethodInfo(MethodLiteral *methodLiteral, const FrameIterator &it, + bool AddMethodInfo(MethodLiteral *methodLiteral, const JSPandaFile *jsPandaFile, int sequenceId); void AddTraceNodeId(MethodLiteral *methodLiteral); @@ -462,10 +486,11 @@ public: private: void FillNodes(bool isInFinish = false); - Node *GenerateNode(JSTaggedValue entry, size_t size = 0, int sequenceId = -1, bool isInFinish = false); - Node *GeneratePrivateStringNode(size_t size, int sequenceId); - Node *GenerateStringNode(JSTaggedValue entry, size_t size, int sequenceId, bool isInFinish = false); + Node *GenerateNode(JSTaggedValue entry, size_t size = 0, bool isInFinish = false); + Node *GeneratePrivateStringNode(size_t size); + Node *GenerateStringNode(JSTaggedValue entry, size_t size, bool isInFinish = false); void FillEdges(); + void RenameFunction(const CString &edgeName, Node *entryFrom, Node *entryTo); void BridgeAllReferences(); CString *GenerateEdgeName(TaggedObject *from, TaggedObject *to); @@ -480,7 +505,6 @@ private: CList nodes_ {}; CList edges_ {}; CVector timeStamps_ {}; - std::atomic_int sequenceId_ {1}; // 1 Reversed for SyntheticRoot int nodeCount_ {0}; int edgeCount_ {0}; int totalNodesSize_ {0}; @@ -489,6 +513,7 @@ private: const EcmaVM *vm_; bool isVmMode_ {true}; bool isPrivate_ {false}; + bool captureNumericValue_ {false}; Node* privateStringNode_ {nullptr}; bool trackAllocations_ {false}; CVector traceInfoStack_ {}; @@ -497,6 +522,7 @@ private: TraceTree traceTree_; CMap methodToTraceNodeId_; CVector traceNodeIndex_; + EntryIdMap* entryIdMap_; Chunk *chunk_ {nullptr}; }; @@ -539,4 +565,4 @@ public: static FrontType Convert(NodeType type); }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_HPROF_HEAP_SNAPSHOT_H +#endif // ECMASCRIPT_DFX_HPROF_HEAP_SNAPSHOT_H diff --git a/ecmascript/dfx/hprof/heap_snapshot_json_serializer.cpp b/ecmascript/dfx/hprof/heap_snapshot_json_serializer.cpp index 2889f3a54ce1de9e911499a5aa6cfe4c869e3e0f..df85c6e5db7a94b5b15efb3c133385dedbd215da 100644 --- a/ecmascript/dfx/hprof/heap_snapshot_json_serializer.cpp +++ b/ecmascript/dfx/hprof/heap_snapshot_json_serializer.cpp @@ -20,254 +20,250 @@ namespace panda::ecmascript { -HeapSnapshotJSONSerializer::~HeapSnapshotJSONSerializer() -{ - if (!writer_) { - delete writer_; - } -} - bool HeapSnapshotJSONSerializer::Serialize(HeapSnapshot *snapshot, Stream *stream) { // Serialize Node/Edge/String-Table - LOG_ECMA(ERROR) << "HeapSnapshotJSONSerializer::Serialize begin"; - snapshot_ = snapshot; - ASSERT(snapshot_->GetNodes() != nullptr && snapshot_->GetEdges() != nullptr && - snapshot_->GetEcmaStringTable() != nullptr); - writer_ = new StreamWriter(stream); + LOG_ECMA(INFO) << "HeapSnapshotJSONSerializer::Serialize begin"; + ASSERT(snapshot->GetNodes() != nullptr && snapshot->GetEdges() != nullptr && + snapshot->GetEcmaStringTable() != nullptr); + auto writer = new StreamWriter(stream); + + SerializeSnapshotHeader(snapshot, writer); // 1. + SerializeNodes(snapshot, writer); // 2. + SerializeEdges(snapshot, writer); // 3. + SerializeTraceFunctionInfo(snapshot, writer); // 4. + SerializeTraceTree(snapshot, writer); // 5. + SerializeSamples(snapshot, writer); // 6. + SerializeLocations(writer); // 7. + SerializeStringTable(snapshot, writer); // 8. + SerializerSnapshotClosure(writer); // 9. + writer->End(); - SerializeSnapshotHeader(); // 1. - SerializeNodes(); // 2. - SerializeEdges(); // 3. - SerializeTraceFunctionInfo(); // 4. - SerializeTraceTree(); // 5. - SerializeSamples(); // 6. - SerializeLocations(); // 7. - SerializeStringTable(); // 8. - SerializerSnapshotClosure(); // 9. - writer_->End(); + delete writer; - LOG_ECMA(ERROR) << "HeapSnapshotJSONSerializer::Serialize exit"; + LOG_ECMA(INFO) << "HeapSnapshotJSONSerializer::Serialize exit"; return true; } -void HeapSnapshotJSONSerializer::SerializeSnapshotHeader() +void HeapSnapshotJSONSerializer::SerializeSnapshotHeader(HeapSnapshot *snapshot, StreamWriter *writer) { - writer_->Write("{\"snapshot\":\n"); // 1. - writer_->Write("{\"meta\":\n"); // 2. + writer->Write("{\"snapshot\":\n"); // 1. + writer->Write("{\"meta\":\n"); // 2. // NOLINTNEXTLINE(modernize-raw-string-literal) - writer_->Write("{\"node_fields\":[\"type\",\"name\",\"id\",\"self_size\",\"edge_count\",\"trace_node_id\","); - writer_->Write("\"detachedness\"],\n"); // 3. + writer->Write("{\"node_fields\":[\"type\",\"name\",\"id\",\"self_size\",\"edge_count\",\"trace_node_id\","); + writer->Write("\"detachedness\"],\n"); // 3. // NOLINTNEXTLINE(modernize-raw-string-literal) - writer_->Write("\"node_types\":[[\"hidden\",\"array\",\"string\",\"object\",\"code\",\"closure\",\"regexp\","); + writer->Write("\"node_types\":[[\"hidden\",\"array\",\"string\",\"object\",\"code\",\"closure\",\"regexp\","); // NOLINTNEXTLINE(modernize-raw-string-literal) - writer_->Write("\"number\",\"native\",\"synthetic\",\"concatenated string\",\"slicedstring\",\"symbol\","); + writer->Write("\"number\",\"native\",\"synthetic\",\"concatenated string\",\"slicedstring\",\"symbol\","); // NOLINTNEXTLINE(modernize-raw-string-literal) - writer_->Write("\"bigint\"],\"string\",\"number\",\"number\",\"number\",\"number\",\"number\"],\n"); // 4. + writer->Write("\"bigint\"],\"string\",\"number\",\"number\",\"number\",\"number\",\"number\"],\n"); // 4. // NOLINTNEXTLINE(modernize-raw-string-literal) - writer_->Write("\"edge_fields\":[\"type\",\"name_or_index\",\"to_node\"],\n"); // 5. + writer->Write("\"edge_fields\":[\"type\",\"name_or_index\",\"to_node\"],\n"); // 5. // NOLINTNEXTLINE(modernize-raw-string-literal) - writer_->Write("\"edge_types\":[[\"context\",\"element\",\"property\",\"internal\",\"hidden\",\"shortcut\","); + writer->Write("\"edge_types\":[[\"context\",\"element\",\"property\",\"internal\",\"hidden\",\"shortcut\","); // NOLINTNEXTLINE(modernize-raw-string-literal) - writer_->Write("\"weak\"],\"string_or_number\",\"node\"],\n"); // 6. + writer->Write("\"weak\"],\"string_or_number\",\"node\"],\n"); // 6. // NOLINTNEXTLINE(modernize-raw-string-literal) - writer_->Write("\"trace_function_info_fields\":[\"function_id\",\"name\",\"script_name\",\"script_id\","); + writer->Write("\"trace_function_info_fields\":[\"function_id\",\"name\",\"script_name\",\"script_id\","); // NOLINTNEXTLINE(modernize-raw-string-literal) - writer_->Write("\"line\",\"column\"],\n"); // 7. + writer->Write("\"line\",\"column\"],\n"); // 7. // NOLINTNEXTLINE(modernize-raw-string-literal) - writer_->Write("\"trace_node_fields\":[\"id\",\"function_info_index\",\"count\",\"size\",\"children\"],\n"); + writer->Write("\"trace_node_fields\":[\"id\",\"function_info_index\",\"count\",\"size\",\"children\"],\n"); // NOLINTNEXTLINE(modernize-raw-string-literal) - writer_->Write("\"sample_fields\":[\"timestamp_us\",\"last_assigned_id\"],\n"); // 9. + writer->Write("\"sample_fields\":[\"timestamp_us\",\"last_assigned_id\"],\n"); // 9. // NOLINTNEXTLINE(modernize-raw-string-literal) // 10. - writer_->Write("\"location_fields\":[\"object_index\",\"script_id\",\"line\",\"column\"]},\n\"node_count\":"); - writer_->Write(snapshot_->GetNodeCount()); // 11. - writer_->Write(",\n\"edge_count\":"); - writer_->Write(snapshot_->GetEdgeCount()); // 12. - writer_->Write(",\n\"trace_function_count\":"); - writer_->Write(snapshot_->GetTrackAllocationsStack().size()); // 13. - writer_->Write("\n},\n"); // 14. + writer->Write("\"location_fields\":[\"object_index\",\"script_id\",\"line\",\"column\"]},\n\"node_count\":"); + writer->Write(snapshot->GetNodeCount()); // 11. + writer->Write(",\n\"edge_count\":"); + writer->Write(snapshot->GetEdgeCount()); // 12. + writer->Write(",\n\"trace_function_count\":"); + writer->Write(snapshot->GetTrackAllocationsStack().size()); // 13. + writer->Write("\n},\n"); // 14. } -void HeapSnapshotJSONSerializer::SerializeNodes() +void HeapSnapshotJSONSerializer::SerializeNodes(HeapSnapshot *snapshot, StreamWriter *writer) { - const CList *nodes = snapshot_->GetNodes(); - const StringHashMap *stringTable = snapshot_->GetEcmaStringTable(); + const CList *nodes = snapshot->GetNodes(); + const StringHashMap *stringTable = snapshot->GetEcmaStringTable(); ASSERT(nodes != nullptr); - writer_->Write("\"nodes\":["); // Section Header + writer->Write("\"nodes\":["); // Section Header size_t i = 0; for (auto *node : *nodes) { if (i > 0) { - writer_->Write(","); // add comma except first line + writer->Write(","); // add comma except first line } - writer_->Write(static_cast(NodeTypeConverter::Convert(node->GetType()))); // 1. - writer_->Write(","); - writer_->Write(stringTable->GetStringId(node->GetName())); // 2. - writer_->Write(","); - writer_->Write(node->GetId()); // 3. - writer_->Write(","); - writer_->Write(node->GetSelfSize()); // 4. - writer_->Write(","); - writer_->Write(node->GetEdgeCount()); // 5. - writer_->Write(","); - writer_->Write(node->GetStackTraceId()); // 6. - writer_->Write(","); + writer->Write(static_cast(NodeTypeConverter::Convert(node->GetType()))); // 1. + writer->Write(","); + writer->Write(stringTable->GetStringId(node->GetName())); // 2. + writer->Write(","); + writer->Write(node->GetId()); // 3. + writer->Write(","); + writer->Write(node->GetSelfSize()); // 4. + writer->Write(","); + writer->Write(node->GetEdgeCount()); // 5. + writer->Write(","); + writer->Write(node->GetStackTraceId()); // 6. + writer->Write(","); if (i == nodes->size() - 1) { // add comma at last the line - writer_->Write("0],\n"); // 7. detachedness default + writer->Write("0],\n"); // 7. detachedness default } else { - writer_->Write("0\n"); // 7. + writer->Write("0\n"); // 7. } i++; } } -void HeapSnapshotJSONSerializer::SerializeEdges() +void HeapSnapshotJSONSerializer::SerializeEdges(HeapSnapshot *snapshot, StreamWriter *writer) { - const CList *edges = snapshot_->GetEdges(); - const StringHashMap *stringTable = snapshot_->GetEcmaStringTable(); + const CList *edges = snapshot->GetEdges(); + const StringHashMap *stringTable = snapshot->GetEcmaStringTable(); ASSERT(edges != nullptr); - writer_->Write("\"edges\":["); + writer->Write("\"edges\":["); size_t i = 0; for (auto *edge : *edges) { + StringId nameOrIndex = edge->GetType() == EdgeType::ELEMENT ? + edge->GetIndex() : stringTable->GetStringId(edge->GetName()); if (i > 0) { // add comma except the first line - writer_->Write(","); + writer->Write(","); } - writer_->Write(static_cast(edge->GetType())); // 1. - writer_->Write(","); - writer_->Write(stringTable->GetStringId(edge->GetName())); // 2. Use StringId - writer_->Write(","); + writer->Write(static_cast(edge->GetType())); // 1. + writer->Write(","); + writer->Write(nameOrIndex); // 2. Use StringId + writer->Write(","); if (i == edges->size() - 1) { // add comma at last the line - writer_->Write(edge->GetTo()->GetIndex() * Node::NODE_FIELD_COUNT); // 3. - writer_->Write("],\n"); + writer->Write(edge->GetTo()->GetIndex() * Node::NODE_FIELD_COUNT); // 3. + writer->Write("],\n"); } else { - writer_->Write(edge->GetTo()->GetIndex() * Node::NODE_FIELD_COUNT); // 3. - writer_->Write("\n"); + writer->Write(edge->GetTo()->GetIndex() * Node::NODE_FIELD_COUNT); // 3. + writer->Write("\n"); } i++; } } -void HeapSnapshotJSONSerializer::SerializeTraceFunctionInfo() +void HeapSnapshotJSONSerializer::SerializeTraceFunctionInfo(HeapSnapshot *snapshot, StreamWriter *writer) { - const CVector trackAllocationsStack = snapshot_->GetTrackAllocationsStack(); - const StringHashMap *stringTable = snapshot_->GetEcmaStringTable(); + const CVector trackAllocationsStack = snapshot->GetTrackAllocationsStack(); + const StringHashMap *stringTable = snapshot->GetEcmaStringTable(); - writer_->Write("\"trace_function_infos\":["); // Empty + writer->Write("\"trace_function_infos\":["); // Empty size_t i = 0; for (const auto &info : trackAllocationsStack) { if (i > 0) { // add comma except the first line - writer_->Write(","); + writer->Write(","); } - writer_->Write(info.functionId); - writer_->Write(","); + writer->Write(info.functionId); + writer->Write(","); CString functionName(info.functionName.c_str()); - writer_->Write(stringTable->GetStringId(&functionName)); - writer_->Write(","); + writer->Write(stringTable->GetStringId(&functionName)); + writer->Write(","); CString scriptName(info.scriptName.c_str()); - writer_->Write(stringTable->GetStringId(&scriptName)); - writer_->Write(","); - writer_->Write(info.scriptId); - writer_->Write(","); - writer_->Write(info.columnNumber); - writer_->Write(","); - writer_->Write(info.lineNumber); - writer_->Write("\n"); + writer->Write(stringTable->GetStringId(&scriptName)); + writer->Write(","); + writer->Write(info.scriptId); + writer->Write(","); + writer->Write(info.lineNumber); + writer->Write(","); + writer->Write(info.columnNumber); + writer->Write("\n"); i++; } - writer_->Write("],\n"); + writer->Write("],\n"); } -void HeapSnapshotJSONSerializer::SerializeTraceTree() +void HeapSnapshotJSONSerializer::SerializeTraceTree(HeapSnapshot *snapshot, StreamWriter *writer) { - writer_->Write("\"trace_tree\":["); - TraceTree* tree = snapshot_->GetTraceTree(); - if ((tree != nullptr) && (snapshot_->trackAllocations())) { - SerializeTraceNode(tree->GetRoot()); + writer->Write("\"trace_tree\":["); + TraceTree* tree = snapshot->GetTraceTree(); + if ((tree != nullptr) && (snapshot->trackAllocations())) { + SerializeTraceNode(tree->GetRoot(), writer); } - writer_->Write("],\n"); + writer->Write("],\n"); } -void HeapSnapshotJSONSerializer::SerializeTraceNode(TraceNode* node) +void HeapSnapshotJSONSerializer::SerializeTraceNode(TraceNode* node, StreamWriter *writer) { if (node == nullptr) { return; } - writer_->Write(node->GetId()); - writer_->Write(","); - writer_->Write(node->GetNodeIndex()); - writer_->Write(","); - writer_->Write(node->GetTotalCount()); - writer_->Write(","); - writer_->Write(node->GetTotalSize()); - writer_->Write(",["); + writer->Write(node->GetId()); + writer->Write(","); + writer->Write(node->GetNodeIndex()); + writer->Write(","); + writer->Write(node->GetTotalCount()); + writer->Write(","); + writer->Write(node->GetTotalSize()); + writer->Write(",["); int i = 0; for (TraceNode* child : node->GetChildren()) { if (i > 0) { - writer_->Write(","); + writer->Write(","); } - SerializeTraceNode(child); + SerializeTraceNode(child, writer); i++; } - writer_->Write("]"); + writer->Write("]"); } -void HeapSnapshotJSONSerializer::SerializeSamples() +void HeapSnapshotJSONSerializer::SerializeSamples(HeapSnapshot *snapshot, StreamWriter *writer) { - writer_->Write("\"samples\":["); - const CVector &timeStamps = snapshot_->GetTimeStamps(); + writer->Write("\"samples\":["); + const CVector &timeStamps = snapshot->GetTimeStamps(); if (!timeStamps.empty()) { auto firstTimeStamp = timeStamps[0]; bool isFirst = true; for (auto timeStamp : timeStamps) { if (!isFirst) { - writer_->Write("\n, "); + writer->Write("\n, "); } else { isFirst = false; } - writer_->Write(timeStamp.GetTimeStamp() - firstTimeStamp.GetTimeStamp()); - writer_->Write(", "); - writer_->Write(timeStamp.GetLastSequenceId()); + writer->Write(timeStamp.GetTimeStamp() - firstTimeStamp.GetTimeStamp()); + writer->Write(", "); + writer->Write(timeStamp.GetLastSequenceId()); } } - writer_->Write("],\n"); + writer->Write("],\n"); } -void HeapSnapshotJSONSerializer::SerializeLocations() +void HeapSnapshotJSONSerializer::SerializeLocations(StreamWriter *writer) { - writer_->Write("\"locations\":[],\n"); + writer->Write("\"locations\":[],\n"); } -void HeapSnapshotJSONSerializer::SerializeStringTable() +void HeapSnapshotJSONSerializer::SerializeStringTable(HeapSnapshot *snapshot, StreamWriter *writer) { - const StringHashMap *stringTable = snapshot_->GetEcmaStringTable(); + const StringHashMap *stringTable = snapshot->GetEcmaStringTable(); ASSERT(stringTable != nullptr); - writer_->Write("\"strings\":[\"\",\n"); - writer_->Write("\"\",\n"); - writer_->Write("\"GC roots\",\n"); + writer->Write("\"strings\":[\"\",\n"); + writer->Write("\"\",\n"); + writer->Write("\"GC roots\",\n"); // StringId Range from 3 size_t capcity = stringTable->GetCapcity(); size_t i = 0; for (auto key : stringTable->GetOrderedKeyStorage()) { if (i == capcity - 1) { - writer_->Write("\""); - writer_->Write(*(stringTable->GetStringByKey(key))); // No Comma for the last line - writer_->Write("\"\n"); + writer->Write("\""); + writer->Write(*(stringTable->GetStringByKey(key))); // No Comma for the last line + writer->Write("\"\n"); } else { - writer_->Write("\""); - writer_->Write(*(stringTable->GetStringByKey(key))); - writer_->Write("\",\n"); + writer->Write("\""); + writer->Write(*(stringTable->GetStringByKey(key))); + writer->Write("\",\n"); } i++; } - writer_->Write("]\n"); + writer->Write("]\n"); } -void HeapSnapshotJSONSerializer::SerializerSnapshotClosure() +void HeapSnapshotJSONSerializer::SerializerSnapshotClosure(StreamWriter *writer) { - writer_->Write("}\n"); + writer->Write("}\n"); } } // namespace panda::ecmascript diff --git a/ecmascript/dfx/hprof/heap_snapshot_json_serializer.h b/ecmascript/dfx/hprof/heap_snapshot_json_serializer.h index 39956fef9d7d95d2afff7f6a9a4410177c364a2e..a4a120e0063d2b659e1fa99e3d4c6ecec0da8afc 100644 --- a/ecmascript/dfx/hprof/heap_snapshot_json_serializer.h +++ b/ecmascript/dfx/hprof/heap_snapshot_json_serializer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_HPROF_HEAP_SNAPSHOT_SERIALIZER_H -#define ECMASCRIPT_HPROF_HEAP_SNAPSHOT_SERIALIZER_H +#ifndef ECMASCRIPT_DFX_HPROF_HEAP_SNAPSHOT_SERIALIZER_H +#define ECMASCRIPT_DFX_HPROF_HEAP_SNAPSHOT_SERIALIZER_H #include #include @@ -90,25 +90,22 @@ private: class HeapSnapshotJSONSerializer { public: explicit HeapSnapshotJSONSerializer() = default; - ~HeapSnapshotJSONSerializer(); + ~HeapSnapshotJSONSerializer() = default; NO_MOVE_SEMANTIC(HeapSnapshotJSONSerializer); NO_COPY_SEMANTIC(HeapSnapshotJSONSerializer); - bool Serialize(HeapSnapshot *snapshot, Stream *stream); + static bool Serialize(HeapSnapshot *snapshot, Stream *stream); private: - void SerializeSnapshotHeader(); - void SerializeNodes(); - void SerializeEdges(); - void SerializeTraceFunctionInfo(); - void SerializeTraceTree(); - void SerializeTraceNode(TraceNode *node); - void SerializeSamples(); - void SerializeLocations(); - void SerializeStringTable(); - void SerializerSnapshotClosure(); - - HeapSnapshot *snapshot_ {nullptr}; - StreamWriter *writer_ {nullptr}; + static void SerializeSnapshotHeader(HeapSnapshot *snapshot, StreamWriter *writer); + static void SerializeNodes(HeapSnapshot *snapshot, StreamWriter *writer); + static void SerializeEdges(HeapSnapshot *snapshot, StreamWriter *writer); + static void SerializeTraceFunctionInfo(HeapSnapshot *snapshot, StreamWriter *writer); + static void SerializeTraceTree(HeapSnapshot *snapshot, StreamWriter *writer); + static void SerializeTraceNode(TraceNode *node, StreamWriter *writer); + static void SerializeSamples(HeapSnapshot *snapshot, StreamWriter *writer); + static void SerializeLocations(StreamWriter *writer); + static void SerializeStringTable(HeapSnapshot *snapshot, StreamWriter *writer); + static void SerializerSnapshotClosure(StreamWriter *writer); }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_HPROF_HEAP_SNAPSHOT_SERIALIZER_H +#endif // ECMASCRIPT_DFX_HPROF_HEAP_SNAPSHOT_SERIALIZER_H diff --git a/ecmascript/dfx/hprof/heap_tracker.h b/ecmascript/dfx/hprof/heap_tracker.h index 571b0e518be678abe4bf0b472ec682343257a120..f7317a45767cf4cef9b252a62154f4581a75f3b0 100644 --- a/ecmascript/dfx/hprof/heap_tracker.h +++ b/ecmascript/dfx/hprof/heap_tracker.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_HPROF_HEAP_TRACKER_H -#define ECMASCRIPT_HPROF_HEAP_TRACKER_H +#ifndef ECMASCRIPT_DFX_HPROF_HEAP_TRACKER_H +#define ECMASCRIPT_DFX_HPROF_HEAP_TRACKER_H #include #include @@ -83,6 +83,11 @@ public: sample_.Stop(); } + HeapSnapshot* GetHeapSnapshot() const + { + return snapshot_; + } + void AllocationEvent(TaggedObject *address, size_t size); void MoveEvent(uintptr_t address, TaggedObject *forwardAddress, size_t size); @@ -94,4 +99,4 @@ private: HeapTrackerSample sample_; }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_HPROF_HEAP_TRACKER_H +#endif // ECMASCRIPT_DFX_HPROF_HEAP_TRACKER_H diff --git a/ecmascript/dfx/hprof/progress.h b/ecmascript/dfx/hprof/progress.h index 8ef98ebbb733661ce0327c18fcdf0ba741ff0a83..63f0ca7feac8c1258e2e367d4c623ddc97b907a3 100644 --- a/ecmascript/dfx/hprof/progress.h +++ b/ecmascript/dfx/hprof/progress.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_TOOLING_INTERFACE_PROGRESS_H -#define ECMASCRIPT_TOOLING_INTERFACE_PROGRESS_H +#ifndef ECMASCRIPT_DFX_HPROF_PROGRESS_H +#define ECMASCRIPT_DFX_HPROF_PROGRESS_H #include @@ -27,4 +27,4 @@ public: }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_TOOLING_INTERFACE_PROGRESS_H +#endif // ECMASCRIPT_DFX_HPROF_PROGRESS_H diff --git a/ecmascript/dfx/hprof/stream.h b/ecmascript/dfx/hprof/stream.h index 2fc6b818604c7defa139b4fd3c5551b32892cb03..428f2660dc759f009c70fb644e84a863bd437554 100644 --- a/ecmascript/dfx/hprof/stream.h +++ b/ecmascript/dfx/hprof/stream.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_TOOLING_INTERFACE_STREAM_H -#define ECMASCRIPT_TOOLING_INTERFACE_STREAM_H +#ifndef ECMASCRIPT_DFX_HPROF_STREAM_H +#define ECMASCRIPT_DFX_HPROF_STREAM_H namespace panda::ecmascript { class HeapStat { @@ -45,4 +45,4 @@ public: }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_TOOLING_INTERFACE_STREAM_H +#endif // ECMASCRIPT_DFX_HPROF_STREAM_H diff --git a/ecmascript/dfx/hprof/string_hashmap.h b/ecmascript/dfx/hprof/string_hashmap.h index 0b177e82eab60150fb647bf97cf55b9e1a6bc3bc..1311f97c8bbe30f1fa96727f6412288afcdc73a1 100644 --- a/ecmascript/dfx/hprof/string_hashmap.h +++ b/ecmascript/dfx/hprof/string_hashmap.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_HPROF_STRING_HASHMAP_H -#define ECMASCRIPT_HPROF_STRING_HASHMAP_H +#ifndef ECMASCRIPT_DFX_HPROF_STRING_HASHMAP_H +#define ECMASCRIPT_DFX_HPROF_STRING_HASHMAP_H #include "ecmascript/ecma_vm.h" #include "ecmascript/mem/c_containers.h" @@ -78,4 +78,4 @@ private: CUnorderedMap hashmap_; }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_HPROF_STRING_HASHMAP_H +#endif // ECMASCRIPT_DFX_HPROF_STRING_HASHMAP_H diff --git a/ecmascript/dfx/hprof/tests/heap_sampling_test.cpp b/ecmascript/dfx/hprof/tests/heap_sampling_test.cpp index a34c2cc1f2a3820b83192127649da40c1a6cec72..1e88b110989bda583347a4f7fad6a119c64dbab4 100644 --- a/ecmascript/dfx/hprof/tests/heap_sampling_test.cpp +++ b/ecmascript/dfx/hprof/tests/heap_sampling_test.cpp @@ -97,7 +97,7 @@ HWTEST_F_L0(HeapSamplingTest, ImplementSampling) int size = 1 << 15; // default size Address addr = 0; heapSampling->ImplementSampling(addr, size); - const struct SamplingInfo *result = heapSampling->GetAllocationProfile(); + const SamplingInfo *result = heapSampling->GetAllocationProfile(); EXPECT_TRUE(result != nullptr); EXPECT_TRUE(result->samples_[0].size_ == size); } diff --git a/ecmascript/dfx/hprof/tests/heap_tracker_test.cpp b/ecmascript/dfx/hprof/tests/heap_tracker_test.cpp index aa23caf527a9cc5c8d26599bc67a0296a35df590..c2efaf9363a288572fff780e022617006e9ab7a8 100644 --- a/ecmascript/dfx/hprof/tests/heap_tracker_test.cpp +++ b/ecmascript/dfx/hprof/tests/heap_tracker_test.cpp @@ -248,7 +248,7 @@ HWTEST_F_L0(HeapTrackerTest, DumpHeapSnapshot) FileStream stream(fileName.c_str()); TestProgress testProgress; - heapProfile->DumpHeapSnapshot(DumpFormat::JSON, &stream, &testProgress, true, true); + heapProfile->DumpHeapSnapshot(DumpFormat::JSON, &stream, &testProgress, true, true, false); HeapProfilerInterface::Destroy(instance); // Check @@ -282,7 +282,10 @@ HWTEST_F_L0(HeapTrackerTest, HeapSnapshotBuildUp) bool isVmMode = true; bool isPrivate = false; bool traceAllocation = false; - HeapSnapshot heapSnapshot(instance, isVmMode, isPrivate, traceAllocation, instance->GetChunk()); + bool captureNumericValue = false; + HeapProfiler heapProfiler(instance); + HeapSnapshot heapSnapshot(instance, isVmMode, isPrivate, captureNumericValue, traceAllocation, + heapProfiler.GetEntryIdMap(), instance->GetChunk()); EXPECT_TRUE(heapSnapshot.BuildUp()); } @@ -291,7 +294,10 @@ HWTEST_F_L0(HeapTrackerTest, HeapSnapshotUpdateNode) bool isVmMode = true; bool isPrivate = false; bool traceAllocation = false; - HeapSnapshot heapSnapshot(instance, isVmMode, isPrivate, traceAllocation, instance->GetChunk()); + bool captureNumericValue = false; + HeapProfiler heapProfiler(instance); + HeapSnapshot heapSnapshot(instance, isVmMode, isPrivate, captureNumericValue, traceAllocation, + heapProfiler.GetEntryIdMap(), instance->GetChunk()); size_t beginNode = heapSnapshot.GetNodeCount(); heapSnapshot.UpdateNodes(); size_t endNode = heapSnapshot.GetNodeCount(); @@ -325,7 +331,7 @@ HWTEST_F_L0(HeapTrackerTest, GenDumpFileName_001) stream.Clear(); EXPECT_TRUE(!stream.Good()); TestProgress testProgress; - heapProfile->DumpHeapSnapshot(DumpFormat::JSON, &stream, &testProgress, true, true); + heapProfile->DumpHeapSnapshot(DumpFormat::JSON, &stream, &testProgress, true, true, false); HeapProfilerInterface::Destroy(instance); } @@ -356,7 +362,7 @@ HWTEST_F_L0(HeapTrackerTest, GenDumpFileName_002) stream.Clear(); EXPECT_TRUE(!stream.Good()); TestProgress testProgress; - heapProfile->DumpHeapSnapshot(DumpFormat::BINARY, &stream, &testProgress, true, true); + heapProfile->DumpHeapSnapshot(DumpFormat::BINARY, &stream, &testProgress, true, true, false); HeapProfilerInterface::Destroy(instance); } @@ -387,7 +393,7 @@ HWTEST_F_L0(HeapTrackerTest, GenDumpFileName_003) stream.Clear(); EXPECT_TRUE(!stream.Good()); TestProgress testProgress; - heapProfile->DumpHeapSnapshot(DumpFormat::OTHER, &stream, &testProgress, true, true); + heapProfile->DumpHeapSnapshot(DumpFormat::OTHER, &stream, &testProgress, true, true, false); HeapProfilerInterface::Destroy(instance); } @@ -419,7 +425,7 @@ HWTEST_F_L0(HeapTrackerTest, GenDumpFileName_004) EXPECT_TRUE(!stream.Good()); TestProgress testProgress; DumpFormat dumFormat = static_cast(5); - heapProfile->DumpHeapSnapshot(dumFormat, &stream, &testProgress, true, true); + heapProfile->DumpHeapSnapshot(dumFormat, &stream, &testProgress, true, true, false); HeapProfilerInterface::Destroy(instance); } @@ -467,7 +473,7 @@ HWTEST_F_L0(HeapTrackerTest, StreamWriterEnd) stream.UpdateLastSeenObjectId(1, 1677567644913058); TestProgress testProgress; - heapProfile->DumpHeapSnapshot(DumpFormat::JSON, &stream, &testProgress, true, true); + heapProfile->DumpHeapSnapshot(DumpFormat::JSON, &stream, &testProgress, true, true, false); StreamWriter streamWriter(&stream); streamWriter.End(); HeapProfilerInterface::Destroy(instance); @@ -511,7 +517,10 @@ HWTEST_F_L0(HeapTrackerTest, FormatString) bool isVmMode = true; bool isPrivate = false; bool traceAllocation = false; - HeapSnapshot heapSnapshot(instance, isVmMode, isPrivate, traceAllocation, instance->GetChunk()); + bool captureNumericValue = false; + HeapProfiler heapProfiler(instance); + HeapSnapshot heapSnapshot(instance, isVmMode, isPrivate, captureNumericValue, traceAllocation, + heapProfiler.GetEntryIdMap(), instance->GetChunk()); StringHashMap stringHashMap(instance); CString ret = "H\"e\rl\nl\\o\t W\fo\31rld!"; diff --git a/ecmascript/dfx/hprof/tests/hprof_test.cpp b/ecmascript/dfx/hprof/tests/hprof_test.cpp index 6f3f5ca872254cc98723111fcce5ea26d6e406a3..2f163657d9e65c4ba40b9b626ea7ae3212db404d 100644 --- a/ecmascript/dfx/hprof/tests/hprof_test.cpp +++ b/ecmascript/dfx/hprof/tests/hprof_test.cpp @@ -112,47 +112,43 @@ public: class HProfTestHelper { public: - HProfTestHelper(const std::string &aFilePath, EcmaVM *vm) - : instance(vm), - filePath(aFilePath) + explicit HProfTestHelper(EcmaVM *vm) : instance(vm) {} + + ~HProfTestHelper() + { + HeapProfilerInterface::Destroy(instance); + } + + size_t GenerateSnapShot(const std::string &filePath) { + // first generate this file of filePath if not exist, + // so the function `realpath` of FileStream can not failed on arm/arm64. fstream outputString(filePath, std::ios::out); outputString.close(); outputString.clear(); - FileStream stream(filePath.c_str()); HeapProfilerInterface *heapProfile = HeapProfilerInterface::GetInstance(instance); heapProfile->DumpHeapSnapshot(DumpFormat::JSON, &stream); - - inputStream = fstream(filePath, std::ios::in); - HeapProfilerInterface::Destroy(instance); + return heapProfile->GetIdCount(); } - ~HProfTestHelper() + bool ContrastJSONLineHeader(const std::string &filePath, std::string lineHeader) { - inputStream.close(); - inputStream.clear(); - } - - bool ContrastJSONLineHeader(std::string lineHeader) - { - bool allSame = false; std::string line; - int i = 1; + std::ifstream inputStream(filePath); while (getline(inputStream, line)) { if (line.find(lineHeader) != line.npos) { - allSame = true; - break; + return true; } - i++; } - return allSame; + return false; } - bool ContrastJSONSectionPayload(std::string dataLable, int fieldNum) + bool ContrastJSONSectionPayload(const std::string &filePath, std::string dataLable, int fieldNum) { std::string line; int i = 1; + std::ifstream inputStream(filePath); while (getline(inputStream, line)) { if (i > 10 && line.find(dataLable) != line.npos) { // 10 : Hit the line std::string::size_type pos = 0; @@ -168,20 +164,21 @@ public: return false; // Lost the Line } - bool ContrastJSONClousure() + bool ContrastJSONClousure(const std::string &filePath) { std::string lineBk; // The Last Line std::string line; + std::ifstream inputStream(filePath); while (getline(inputStream, line)) { lineBk = line; } return lineBk.compare("}") == 0; } - int ExtractCountFromMeta(std::string typeLable) + int ExtractCountFromMeta(const std::string &filePath, std::string typeLable) { std::string line; - int i = 1; + std::ifstream inputStream(filePath); while (getline(inputStream, line)) { int length = line.length() - typeLable.length() - 1; if (line.find(typeLable) != line.npos) { // Get @@ -191,16 +188,16 @@ public: line = line.substr(typeLable.length(), length); return std::stoi(line.c_str()); } - i++; } return -1; } - int ExtractCountFromPayload(std::string dataLabel) + int ExtractCountFromPayload(const std::string &filePath, std::string dataLabel) { std::string line; bool hit = false; int loop = 0; + std::ifstream inputStream(filePath); while (getline(inputStream, line)) { if (!hit && line.find(dataLabel) != line.npos) { // Get loop += 1; // First Line @@ -227,71 +224,92 @@ public: private: EcmaVM *instance {nullptr}; - std::string filePath; - std::fstream inputStream {}; }; HWTEST_F_L0(HProfTest, ParseJSONHeader) { - HProfTestHelper tester("ParseJSONHeader.heapsnapshot", instance); - ASSERT_TRUE(tester.ContrastJSONLineHeader("{\"snapshot\":")); - ASSERT_TRUE(tester.ContrastJSONLineHeader("{\"meta\":")); - ASSERT_TRUE(tester.ContrastJSONLineHeader("{\"node_fields\":")); - ASSERT_TRUE(tester.ContrastJSONLineHeader("\"node_types\":")); - ASSERT_TRUE(tester.ContrastJSONLineHeader("\"edge_fields\":")); - ASSERT_TRUE(tester.ContrastJSONLineHeader("\"edge_types\":")); - ASSERT_TRUE(tester.ContrastJSONLineHeader("\"trace_function_info_fields\":")); - ASSERT_TRUE(tester.ContrastJSONLineHeader("\"trace_node_fields\":")); - ASSERT_TRUE(tester.ContrastJSONLineHeader("\"sample_fields\":")); - ASSERT_TRUE(tester.ContrastJSONLineHeader("\"location_fields\":")); + HProfTestHelper tester(instance); + tester.GenerateSnapShot("test.heapsnapshot"); + ASSERT_TRUE(tester.ContrastJSONLineHeader("test.heapsnapshot", "{\"snapshot\":")); + ASSERT_TRUE(tester.ContrastJSONLineHeader("test.heapsnapshot", "{\"meta\":")); + ASSERT_TRUE(tester.ContrastJSONLineHeader("test.heapsnapshot", "{\"node_fields\":")); + ASSERT_TRUE(tester.ContrastJSONLineHeader("test.heapsnapshot", "\"node_types\":")); + ASSERT_TRUE(tester.ContrastJSONLineHeader("test.heapsnapshot", "\"edge_fields\":")); + ASSERT_TRUE(tester.ContrastJSONLineHeader("test.heapsnapshot", "\"edge_types\":")); + ASSERT_TRUE(tester.ContrastJSONLineHeader("test.heapsnapshot", "\"trace_function_info_fields\":")); + ASSERT_TRUE(tester.ContrastJSONLineHeader("test.heapsnapshot", "\"trace_node_fields\":")); + ASSERT_TRUE(tester.ContrastJSONLineHeader("test.heapsnapshot", "\"sample_fields\":")); + ASSERT_TRUE(tester.ContrastJSONLineHeader("test.heapsnapshot", "\"location_fields\":")); } HWTEST_F_L0(HProfTest, ContrastTraceFunctionInfo) { - HProfTestHelper tester("ContrastTraceFunctionInfo.heapsnapshot", instance); - ASSERT_TRUE(tester.ContrastJSONSectionPayload("\"trace_function_infos\":", 2)); // Empty + HProfTestHelper tester(instance); + tester.GenerateSnapShot("test.heapsnapshot"); + ASSERT_TRUE(tester.ContrastJSONSectionPayload("test.heapsnapshot", "\"trace_function_infos\":", 2)); // Empty } HWTEST_F_L0(HProfTest, ContrastTraceTree) { - HProfTestHelper tester("ContrastTraceTree.heapsnapshot", instance); - ASSERT_TRUE(tester.ContrastJSONSectionPayload("\"trace_tree\":", 2)); // Empty + HProfTestHelper tester(instance); + tester.GenerateSnapShot("test.heapsnapshot"); + ASSERT_TRUE(tester.ContrastJSONSectionPayload("test.heapsnapshot", "\"trace_tree\":", 2)); // Empty } HWTEST_F_L0(HProfTest, ContrastSamples) { - HProfTestHelper tester("ContrastSamples.heapsnapshot", instance); - ASSERT_TRUE(tester.ContrastJSONSectionPayload("\"samples\":", 2)); // Empty + HProfTestHelper tester(instance); + tester.GenerateSnapShot("test.heapsnapshot"); + ASSERT_TRUE(tester.ContrastJSONSectionPayload("test.heapsnapshot", "\"samples\":", 2)); // Empty } HWTEST_F_L0(HProfTest, ContrastLocations) { - HProfTestHelper tester("ContrastLocations.heapsnapshot", instance); - ASSERT_TRUE(tester.ContrastJSONSectionPayload("\"locations\":", 2)); // Empty + HProfTestHelper tester(instance); + tester.GenerateSnapShot("test.heapsnapshot"); + ASSERT_TRUE(tester.ContrastJSONSectionPayload("test.heapsnapshot", "\"locations\":", 2)); // Empty } HWTEST_F_L0(HProfTest, ContrastString) { - HProfTestHelper tester("ContrastString.heapsnapshot", instance); - ASSERT_TRUE(tester.ContrastJSONSectionPayload("\"strings\":[", 1 + 1)); + HProfTestHelper tester(instance); + tester.GenerateSnapShot("test.heapsnapshot"); + ASSERT_TRUE(tester.ContrastJSONSectionPayload("test.heapsnapshot", "\"strings\":[", 2)); } HWTEST_F_L0(HProfTest, ContrastClosure) { - HProfTestHelper tester("ContrastClosure.heapsnapshot", instance); - ASSERT_TRUE(tester.ContrastJSONClousure()); + HProfTestHelper tester(instance); + tester.GenerateSnapShot("test.heapsnapshot"); + ASSERT_TRUE(tester.ContrastJSONClousure("test.heapsnapshot")); } HWTEST_F_L0(HProfTest, ContrastEdgeCount) { - HProfTestHelper tester("ContrastEdgeCount.heapsnapshot", instance); - ASSERT_TRUE(tester.ExtractCountFromMeta("\"edge_count\":") == tester.ExtractCountFromPayload("\"edges\":[")); + HProfTestHelper tester(instance); + tester.GenerateSnapShot("test.heapsnapshot"); + ASSERT_TRUE(tester.ExtractCountFromMeta("test.heapsnapshot", "\"edge_count\":") == + tester.ExtractCountFromPayload("test.heapsnapshot", "\"edges\":[")); } -HWTEST_F_L0(HProfTest, ContrastTraceFunctionInfoCount) +HWTEST_F_L0(HProfTest, TraceFuncInfoCount) { - HProfTestHelper tester("ContrastTraceFunctionInfoCount.heapsnapshot", instance); - ASSERT_TRUE(tester.ExtractCountFromMeta("\"trace_function_count\":") == - tester.ExtractCountFromPayload("\"trace_function_infos\":")); + HProfTestHelper tester(instance); + tester.GenerateSnapShot("test.heapsnapshot"); + ASSERT_TRUE(tester.ExtractCountFromMeta("test.heapsnapshot", "\"trace_function_count\":") == + tester.ExtractCountFromPayload("test.heapsnapshot", "\"trace_function_infos\":")); +} + +HWTEST_F_L0(HProfTest, TestIdConsistency) +{ + HProfTestHelper tester(instance); + int64_t count1 = tester.GenerateSnapShot("TestIdConsistency_1.heapsnapshot"); + for (int i = 0; i < 100; ++i) { + instance->GetFactory()->NewJSAsyncFuncObject(); + instance->GetFactory()->NewJSSymbol(); + } + int64_t count2 = tester.GenerateSnapShot("TestIdConsistency_2.heapsnapshot"); + ASSERT_TRUE(std::abs(count1 - count2) <= 500LL); + // load two heapsnapshots into chrome, and further use "Comparision View" } } // namespace panda::test diff --git a/ecmascript/dfx/stackinfo/js_stackgetter.cpp b/ecmascript/dfx/stackinfo/js_stackgetter.cpp index 1a1f665072cfa3f996d9e28d27c1a78892691058..2b48d087d9edd337a784db7c6b3945cc7f423f85 100644 --- a/ecmascript/dfx/stackinfo/js_stackgetter.cpp +++ b/ecmascript/dfx/stackinfo/js_stackgetter.cpp @@ -18,6 +18,7 @@ #include "ecmascript/compiler/aot_file/aot_file_manager.h" #include "ecmascript/compiler/assembler/assembler.h" #include "ecmascript/frames.h" +#include "ecmascript/global_env_constants-inl.h" #include "ecmascript/jspandafile/js_pandafile_manager.h" namespace panda::ecmascript { @@ -55,44 +56,51 @@ bool JsStackGetter::ParseMethodInfo(struct MethodKey &methodKey, FrameInfoTemp &codeEntry, bool isCpuProfiler) { - const JSPandaFile *jsPandaFile = it.CheckAndGetMethod()->GetJSPandaFile(); + auto method = it.CheckAndGetMethod(); + const JSPandaFile *jsPandaFile = method->GetJSPandaFile(); codeEntry.methodKey = methodKey; - if (jsPandaFile == nullptr) { + if (method->IsNativeWithCallField()) { FrameIterator itNext(it.GetSp(), it.GetThread()); itNext.Advance(); GetNativeMethodCallPos(itNext, codeEntry); GetNativeStack(vm, it, codeEntry.functionName, sizeof(codeEntry.functionName), isCpuProfiler); } else { EntityId methodId = reinterpret_cast(methodKey.methodIdentifier)->GetMethodId(); - const char *tempVariable = MethodLiteral::GetMethodName(jsPandaFile, methodId); - uint8_t length = strlen(tempVariable); - if (length != 0 && tempVariable[0] == '#') { + // function name + const char *functionName = MethodLiteral::GetMethodName(jsPandaFile, methodId); + uint8_t length = strlen(functionName); + if (length != 0 && functionName[0] == '#') { uint8_t index = length - 1; - while (tempVariable[index] != '#') { + while (functionName[index] != '#') { index--; } - tempVariable += (index + 1); + functionName += (index + 1); } - if (strlen(tempVariable) == 0) { - tempVariable = "anonymous"; + if (strlen(functionName) == 0) { + functionName = "anonymous"; } - if (!CheckAndCopy(codeEntry.functionName, sizeof(codeEntry.functionName), tempVariable)) { + if (!CheckAndCopy(codeEntry.functionName, sizeof(codeEntry.functionName), functionName)) { return false; } - // source file + // record name + const char *recordName = MethodLiteral::GetRecordNameWithSymbol(jsPandaFile, methodId); + if (strlen(recordName) != 0) { + if (!CheckAndCopy(codeEntry.recordName, sizeof(codeEntry.recordName), recordName)) { + return false; + } + } + DebugInfoExtractor *debugExtractor = JSPandaFileManager::GetInstance()->GetJSPtExtractor(jsPandaFile); if (debugExtractor == nullptr) { return false; } + // source file const std::string &sourceFile = debugExtractor->GetSourceFile(methodId); - if (sourceFile.empty()) { - tempVariable = ""; - } else { - tempVariable = sourceFile.c_str(); - } - if (!CheckAndCopy(codeEntry.url, sizeof(codeEntry.url), tempVariable)) { - return false; + if (!sourceFile.empty()) { + if (!CheckAndCopy(codeEntry.url, sizeof(codeEntry.url), sourceFile.c_str())) { + return false; + } } // line number and clomn number codeEntry.lineNumber = debugExtractor->GetFristLine(methodId); @@ -154,12 +162,11 @@ void JsStackGetter::GetNativeStack(const EcmaVM *vm, const FrameIterator &it, ch } RunningState JsStackGetter::GetRunningState(const FrameIterator &it, const EcmaVM *vm, - const JSPandaFile *jsPandaFile, bool topFrame, + bool isNative, bool topFrame, bool enableVMTag) { JSThread *thread = vm->GetAssociatedJSThread(); JSFunction* function = JSFunction::Cast(it.GetFunction().GetTaggedObject()); - bool isNative = jsPandaFile == nullptr; if (enableVMTag) { if (topFrame) { @@ -266,12 +273,11 @@ void *JsStackGetter::GetMethodIdentifier(Method *method, const FrameIterator &it { JSFunction* function = JSFunction::Cast(it.GetFunction().GetTaggedObject()); JSTaggedValue extraInfoValue = function->GetNativeFunctionExtraInfo(); - if (extraInfoValue.CheckIsJSNativePointer()) { - JSNativePointer *extraInfo = JSNativePointer::Cast(extraInfoValue.GetTaggedObject()); - return reinterpret_cast(extraInfo->GetData()); - } - - if (method->GetJSPandaFile() == nullptr) { + if (method->IsNativeWithCallField()) { + if (extraInfoValue.CheckIsJSNativePointer()) { + JSNativePointer *extraInfo = JSNativePointer::Cast(extraInfoValue.GetTaggedObject()); + return reinterpret_cast(extraInfo->GetData()); + } return const_cast(method->GetNativePointer()); } diff --git a/ecmascript/dfx/stackinfo/js_stackgetter.h b/ecmascript/dfx/stackinfo/js_stackgetter.h index a10bfd0eac618c7923fb71847cbea572b4caebcc..477a45c71b2f02e79592b1b275a7042473c3f14c 100644 --- a/ecmascript/dfx/stackinfo/js_stackgetter.h +++ b/ecmascript/dfx/stackinfo/js_stackgetter.h @@ -18,7 +18,7 @@ #include -#include "ecmascript/compiler/gate_meta_data.h" +#include "ecmascript/compiler/share_gate_meta_data.h" #include "ecmascript/deoptimizer/deoptimizer.h" #include "ecmascript/interpreter/frame_handler.h" @@ -61,6 +61,7 @@ struct NodeKey { struct FrameInfoTemp { char codeType[20] = {0}; // 20:the maximum size of the codeType char functionName[100] = {0}; // 100:the maximum size of the functionName + char recordName[500] = {0}; // 500:the maximum size of the recordName int columnNumber = -1; int lineNumber = -1; int scriptId = 0; @@ -79,7 +80,7 @@ public: static bool CheckAndCopy(char *dest, size_t length, const char *src); static void GetNativeStack(const EcmaVM *vm, const FrameIterator &it, char *functionName, size_t size, bool isCpuProfiler); - static RunningState GetRunningState(const FrameIterator &it, const EcmaVM *vm, const JSPandaFile *jsPandaFile, + static RunningState GetRunningState(const FrameIterator &it, const EcmaVM *vm, bool isNative, bool topFrame, bool enableVMTag = false); static void GetNativeMethodCallPos(FrameIterator &it, FrameInfoTemp &codeEntry); static void *GetMethodIdentifier(Method *method, const FrameIterator &it); diff --git a/ecmascript/dfx/stackinfo/js_stackinfo.cpp b/ecmascript/dfx/stackinfo/js_stackinfo.cpp index c2f94cbe280191523fa25489c60846904a1ce4c8..a58b89f29b3819aabe162193fea31027d022407b 100644 --- a/ecmascript/dfx/stackinfo/js_stackinfo.cpp +++ b/ecmascript/dfx/stackinfo/js_stackinfo.cpp @@ -19,6 +19,7 @@ #include "ecmascript/interpreter/frame_handler.h" #include "ecmascript/interpreter/interpreter.h" #include "ecmascript/jspandafile/js_pandafile_manager.h" +#include "ecmascript/mem/heap-inl.h" #include "ecmascript/message_string.h" #include "ecmascript/platform/os.h" #if defined(ENABLE_EXCEPTION_BACKTRACE) @@ -26,7 +27,7 @@ #endif namespace panda::ecmascript { -std::string JsStackInfo::BuildMethodTrace(Method *method, uint32_t pcOffset) +std::string JsStackInfo::BuildMethodTrace(Method *method, uint32_t pcOffset, bool enableStackSourceFile) { std::string data; data.append(" at "); @@ -39,12 +40,17 @@ std::string JsStackInfo::BuildMethodTrace(Method *method, uint32_t pcOffset) // source file DebugInfoExtractor *debugExtractor = JSPandaFileManager::GetInstance()->GetJSPtExtractor(method->GetJSPandaFile()); - const std::string &sourceFile = debugExtractor->GetSourceFile(method->GetMethodId()); - if (sourceFile.empty()) { - data.push_back('?'); + if (enableStackSourceFile) { + const std::string &sourceFile = debugExtractor->GetSourceFile(method->GetMethodId()); + if (sourceFile.empty()) { + data.push_back('?'); + } else { + data += sourceFile; + } } else { - data += sourceFile; + data.append("hidden"); } + data.push_back(':'); // line number and column number auto callbackLineFunc = [&data](int32_t line) -> bool { @@ -66,12 +72,38 @@ std::string JsStackInfo::BuildMethodTrace(Method *method, uint32_t pcOffset) return data; } +std::string JsStackInfo::BuildInlinedMethodTrace(const JSPandaFile *pf, std::map &methodOffsets) +{ + std::string data; + std::map::reverse_iterator it; + for (it = methodOffsets.rbegin(); it != methodOffsets.rend(); it++) { + uint32_t methodId = it->second; + std::string name; + if (methodId == 0) { + name = "unknown"; + } else { + name = std::string(MethodLiteral::GetMethodName(pf, EntityId(methodId))); + if (name == "") { + name = "anonymous"; + } + } + data.append(" at "); + data.append(name); + data.append(" (maybe inlined)."); + data.append(" depth: "); + data.append(std::to_string(it->first)); + + data.push_back('\n'); + } + return data; +} + std::string JsStackInfo::BuildJsStackTrace(JSThread *thread, bool needNative) { std::string data; JSTaggedType *current = const_cast(thread->GetCurrentFrame()); FrameIterator it(current, thread); - for (; !it.Done(); it.Advance()) { + for (; !it.Done(); it.Advance()) { if (!it.IsJSFrame()) { continue; } @@ -81,7 +113,10 @@ std::string JsStackInfo::BuildJsStackTrace(JSThread *thread, bool needNative) } if (!method->IsNativeWithCallField()) { auto pcOffset = it.GetBytecodeOffset(); - data += BuildMethodTrace(method, pcOffset); + const JSPandaFile *pf = method->GetJSPandaFile(); + std::map methodOffsets = it.GetInlinedMethodInfo(); + data += BuildInlinedMethodTrace(pf, methodOffsets); + data += BuildMethodTrace(method, pcOffset, thread->GetEnableStackSourceFile()); } else if (needNative) { auto addr = method->GetNativePointer(); std::stringstream strm; @@ -200,7 +235,7 @@ void CrashCallback(char *buf __attribute__((unused)), size_t len __attribute__(( std::string faultInfo; if (method != nullptr) { std::string methodName = method->GetMethodName(); - std::string recordName = method->GetRecordName().c_str(); + std::string recordName = method->GetRecordNameStr().c_str(); faultInfo = "Method Name:" + methodName + " Record Name:" + recordName; } else { faultInfo = "method is nullptr!"; @@ -209,7 +244,7 @@ void CrashCallback(char *buf __attribute__((unused)), size_t len __attribute__(( uintptr_t func = uctx->uc_mcontext.regs[2]; // 2: func JSTaggedValue builtinMethod = JSFunction::Cast(reinterpret_cast(func))->GetMethod(); uint8_t builtinId = Method::Cast(builtinMethod.GetTaggedObject())->GetBuiltinId(); - size_t builtinStart = static_cast(GET_MESSAGE_STRING_ID(CharCodeAt) - 1); // 1: offset NONE + size_t builtinStart = static_cast(GET_MESSAGE_STRING_ID(StringCharCodeAt) - 1); // 1: offset NONE std::string builtinStr = MessageString::GetMessageString(builtinStart + builtinId); faultInfo += " " + builtinStr; } @@ -219,9 +254,29 @@ void CrashCallback(char *buf __attribute__((unused)), size_t len __attribute__(( #endif } -bool ReadUintptrFromAddr(int pid, uintptr_t addr, uintptr_t &value) +bool ReadUintptrFromAddr(int pid, uintptr_t addr, uintptr_t &value, bool needCheckRegion) { if (pid == getpid()) { + if (needCheckRegion) { + bool flag = false; + auto callback = [addr, &flag](Region *region) { + uintptr_t regionBegin = region->GetBegin(); + uintptr_t regionEnd = region->GetEnd(); + if (regionBegin <= addr && addr <= regionEnd) { + flag = true; + } + }; + if (JsStackInfo::loader != nullptr) { + const Heap *heap = JsStackInfo::loader->GetHeap(); + if (heap != nullptr) { + heap->EnumerateRegions(callback); + } + } + if (!flag) { + LOG_ECMA(ERROR) << "addr not in Region, addr: " << addr; + return false; + } + } value = *(reinterpret_cast(addr)); return true; } @@ -329,10 +384,15 @@ bool StepArkManagedNativeFrame(int pid, uintptr_t *pc, uintptr_t *fp, uintptr_t LOG_ECMA(ERROR) << "fp is nullptr in StepArkManagedNativeFrame()!"; return false; } + if (pid == getpid() && JsStackInfo::loader != nullptr && + !JsStackInfo::loader->InsideStub(*pc) && !JsStackInfo::loader->InsideAOT(*pc)) { + LOG_ECMA(ERROR) << "invalid pc in StepArkManagedNativeFrame()!"; + return false; + } while (true) { currentPtr -= sizeof(FrameType); uintptr_t frameType = 0; - if (!ReadUintptrFromAddr(pid, currentPtr, frameType)) { + if (!ReadUintptrFromAddr(pid, currentPtr, frameType, true)) { return false; } uintptr_t typeOffset = 0; @@ -348,7 +408,7 @@ bool StepArkManagedNativeFrame(int pid, uintptr_t *pc, uintptr_t *fp, uintptr_t } currentPtr -= typeOffset; currentPtr += prevOffset; - if (!ReadUintptrFromAddr(pid, currentPtr, currentPtr)) { + if (!ReadUintptrFromAddr(pid, currentPtr, currentPtr, true)) { return false; } if (currentPtr == 0) { @@ -359,7 +419,7 @@ bool StepArkManagedNativeFrame(int pid, uintptr_t *pc, uintptr_t *fp, uintptr_t currentPtr += sizeof(FrameType); *fp = currentPtr; currentPtr += FP_SIZE; - if (!ReadUintptrFromAddr(pid, currentPtr, *pc)) { + if (!ReadUintptrFromAddr(pid, currentPtr, *pc, true)) { return false; } currentPtr += LR_SIZE; @@ -418,7 +478,7 @@ bool GetArkJSHeapCrashInfo(int pid, uintptr_t *bytecodePc, uintptr_t *fp, bool o } currentPtr -= sizeof(FrameType); uintptr_t frameType = 0; - if (!ReadUintptrFromAddr(pid, currentPtr, frameType)) { + if (!ReadUintptrFromAddr(pid, currentPtr, frameType, false)) { return false; } if (static_cast(frameType) != FrameType::ASM_INTERPRETER_FRAME) { @@ -426,7 +486,7 @@ bool GetArkJSHeapCrashInfo(int pid, uintptr_t *bytecodePc, uintptr_t *fp, bool o } size_t strIndex = 0; uintptr_t registerBytecode = 0; - if (!ReadUintptrFromAddr(pid, *bytecodePc, registerBytecode)) { + if (!ReadUintptrFromAddr(pid, *bytecodePc, registerBytecode, false)) { return false; } CopyBytecodeInfoToBuffer("RegisterBytecode:", registerBytecode, strIndex, outStr, strLen); @@ -436,10 +496,10 @@ bool GetArkJSHeapCrashInfo(int pid, uintptr_t *bytecodePc, uintptr_t *fp, bool o currentPtr += pcOffset; uintptr_t framePc = 0; uintptr_t frameBytecode = 0; - if (!ReadUintptrFromAddr(pid, currentPtr, framePc)) { + if (!ReadUintptrFromAddr(pid, currentPtr, framePc, false)) { return false; } - if (!ReadUintptrFromAddr(pid, framePc, frameBytecode)) { + if (!ReadUintptrFromAddr(pid, framePc, frameBytecode, false)) { return false; } CopyBytecodeInfoToBuffer(" FrameBytecode:", frameBytecode, strIndex, outStr, strLen); @@ -448,7 +508,7 @@ bool GetArkJSHeapCrashInfo(int pid, uintptr_t *bytecodePc, uintptr_t *fp, bool o currentPtr -= pcOffset; currentPtr += functionOffset; uintptr_t functionAddress = 0; - if (!ReadUintptrFromAddr(pid, currentPtr, functionAddress)) { + if (!ReadUintptrFromAddr(pid, currentPtr, functionAddress, false)) { return false; } JSTaggedValue functionValue(static_cast(functionAddress)); diff --git a/ecmascript/dfx/stackinfo/js_stackinfo.h b/ecmascript/dfx/stackinfo/js_stackinfo.h index 75228c02ef29d4593ed1549c329d6d341fd4f940..2395aff81db3dbfd10a0e26dfdf21b23f12c7e10 100644 --- a/ecmascript/dfx/stackinfo/js_stackinfo.h +++ b/ecmascript/dfx/stackinfo/js_stackinfo.h @@ -29,9 +29,10 @@ struct JsFrameInfo { }; class JsStackInfo { public: + static std::string BuildInlinedMethodTrace(const JSPandaFile *pf, std::map &methodOffsets); static std::string BuildJsStackTrace(JSThread *thread, bool needNative); static std::vector BuildJsStackInfo(JSThread *thread); - static std::string BuildMethodTrace(Method *method, uint32_t pcOffset); + static std::string BuildMethodTrace(Method *method, uint32_t pcOffset, bool enableStackSourceFile = true); static AOTFileManager *loader; }; void CrashCallback(char *buf, size_t len, void *ucontext); diff --git a/ecmascript/dfx/vm_thread_control.cpp b/ecmascript/dfx/vm_thread_control.cpp index 8b6ad945c4a0f41c65c6b695ffacd14260ffed77..869dfbfac68d64aab4ac1ab2f67358771baf3027 100644 --- a/ecmascript/dfx/vm_thread_control.cpp +++ b/ecmascript/dfx/vm_thread_control.cpp @@ -16,6 +16,8 @@ #include "ecmascript/dfx/vm_thread_control.h" namespace panda::ecmascript { +constexpr int32_t TIME_OUT_MS = 1500; + bool VmThreadControl::NotifyVMThreadSuspension() // block caller thread { if (VMNeedSuspension()) { // only enable one thread to post suspension @@ -23,9 +25,13 @@ bool VmThreadControl::NotifyVMThreadSuspension() // block caller thread } SetVMNeedSuspension(true); thread_->SetCheckSafePointStatus(); - os::memory::LockHolder lock(vmThreadSuspensionMutex_); + LockHolder lock(vmThreadSuspensionMutex_); while (!IsSuspended()) { - vmThreadNeedSuspensionCV_.Wait(&vmThreadSuspensionMutex_); + if (vmThreadNeedSuspensionCV_.TimedWait(&vmThreadSuspensionMutex_, TIME_OUT_MS)) { + SetVMNeedSuspension(false); + thread_->ResetCheckSafePointStatus(); + return false; + } } return true; } @@ -52,7 +58,7 @@ bool VmThreadControl::IsSuspended() const void VmThreadControl::SuspendVM() // block vm thread { - os::memory::LockHolder lock(vmThreadSuspensionMutex_); + LockHolder lock(vmThreadSuspensionMutex_); SetVMSuspended(true); vmThreadNeedSuspensionCV_.Signal(); // wake up the thread who needs suspend vmthread while (VMNeedSuspension()) { @@ -63,7 +69,7 @@ void VmThreadControl::SuspendVM() // block vm thread void VmThreadControl::ResumeVM() { - os::memory::LockHolder lock(vmThreadSuspensionMutex_); + LockHolder lock(vmThreadSuspensionMutex_); SetVMNeedSuspension(false); vmThreadHasSuspendedCV_.Signal(); } diff --git a/ecmascript/dfx/vm_thread_control.h b/ecmascript/dfx/vm_thread_control.h index 7d8f5fd82a75104a7a03a63cdb1f5b0f1c0a8fb0..19cf253534f32123d2a883c9c120e2fecea92cc4 100644 --- a/ecmascript/dfx/vm_thread_control.h +++ b/ecmascript/dfx/vm_thread_control.h @@ -17,7 +17,7 @@ #define ECMASCRIPT_VM_THREAD_CONTROL_H #include "ecmascript/js_thread.h" -#include "libpandabase/os/mutex.h" +#include "ecmascript/platform/mutex.h" #include "libpandabase/utils/bit_field.h" namespace panda::ecmascript { @@ -46,9 +46,9 @@ public: private: JSThread *thread_; - os::memory::Mutex vmThreadSuspensionMutex_; - os::memory::ConditionVariable vmThreadNeedSuspensionCV_; - os::memory::ConditionVariable vmThreadHasSuspendedCV_; + Mutex vmThreadSuspensionMutex_; + ConditionVariable vmThreadNeedSuspensionCV_; + ConditionVariable vmThreadHasSuspendedCV_; }; } // namespace panda::ecmascript #endif // ECMASCRIPT_VM_THREAD_CONTROL_H \ No newline at end of file diff --git a/ecmascript/dfx/vmstat/caller_stat.h b/ecmascript/dfx/vmstat/caller_stat.h index 928e38959bc38fb6fb6c8d774a0f24dfcca03ed7..ba765e32418791300f16ce6d49c5572d05699e57 100644 --- a/ecmascript/dfx/vmstat/caller_stat.h +++ b/ecmascript/dfx/vmstat/caller_stat.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_VMSTAT_CALLER_STAT_H -#define ECMASCRIPT_VMSTAT_CALLER_STAT_H +#ifndef ECMASCRIPT_DFX_VMSTAT_CALLER_STAT_H +#define ECMASCRIPT_DFX_VMSTAT_CALLER_STAT_H #include #include @@ -128,4 +128,4 @@ private: friend class FunctionCallTimer; }; } // namespace panda::ecmascript -#endif +#endif // ECMASCRIPT_DFX_VMSTAT_CALLER_STAT_H diff --git a/ecmascript/dfx/vmstat/function_call_timer.cpp b/ecmascript/dfx/vmstat/function_call_timer.cpp index 55d610de456c3c4a43cd5d44faa61ca248f5d1f5..19534105906eff6cbb23fcbe957eed6c1059837f 100644 --- a/ecmascript/dfx/vmstat/function_call_timer.cpp +++ b/ecmascript/dfx/vmstat/function_call_timer.cpp @@ -52,7 +52,7 @@ void FunctionCallTimer::StopCount(Method *method) CString FunctionCallTimer::GetFullName(Method *method) { CString funcName(method->GetMethodName()); - CString recordName = method->GetRecordName(); + CString recordName = method->GetRecordNameStr(); CString fullName = funcName + "@" + recordName; return fullName; } diff --git a/ecmascript/dfx/vmstat/function_call_timer.h b/ecmascript/dfx/vmstat/function_call_timer.h index 7b15322fc6177a4d9ae3d763c5b0979733d46eca..ec983974a46f8534c5775d6c7725980aa1fde311 100644 --- a/ecmascript/dfx/vmstat/function_call_timer.h +++ b/ecmascript/dfx/vmstat/function_call_timer.h @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_FCUNTION_CALL_TIMER_H -#define ECMASCRIPT_FCUNTION_CALL_TIMER_H +#ifndef ECMASCRIPT_DFX_VMSTAT_FCUNTION_CALL_TIMER_H +#define ECMASCRIPT_DFX_VMSTAT_FCUNTION_CALL_TIMER_H #include "ecmascript/dfx/vmstat/caller_stat.h" #include "ecmascript/mem/c_containers.h" @@ -82,4 +82,4 @@ private: CMap callTimer_ {}; }; } -#endif \ No newline at end of file +#endif // ECMASCRIPT_DFX_VMSTAT_FCUNTION_CALL_TIMER_H \ No newline at end of file diff --git a/ecmascript/dfx/vmstat/runtime_stat.h b/ecmascript/dfx/vmstat/runtime_stat.h index 3b86badc80439cd90b0e1a8182a57c7a430842ae..34093bd37239396683e04f6743b14aea65db77d8 100644 --- a/ecmascript/dfx/vmstat/runtime_stat.h +++ b/ecmascript/dfx/vmstat/runtime_stat.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_VMSTAT_RUNTIME_STAT_H -#define ECMASCRIPT_VMSTAT_RUNTIME_STAT_H +#ifndef ECMASCRIPT_DFX_VMSTAT_RUNTIME_STAT_H +#define ECMASCRIPT_DFX_VMSTAT_RUNTIME_STAT_H #include "ecmascript/ecma_vm.h" #include "ecmascript/js_thread.h" @@ -85,4 +85,4 @@ private: EcmaRuntimeStat *stats_ {nullptr}; }; } // namespace panda::ecmascript -#endif +#endif // ECMASCRIPT_DFX_VMSTAT_RUNTIME_STAT_H diff --git a/ecmascript/dump.cpp b/ecmascript/dump.cpp index 9b2178ba3a7b0bdb584f5c1d94e6d3d4d5328a3d..52f3730e9309186699d173a0ce9f961355183c59 100644 --- a/ecmascript/dump.cpp +++ b/ecmascript/dump.cpp @@ -19,11 +19,13 @@ #include #include "ecmascript/accessor_data.h" +#include "ecmascript/dfx/hprof/heap_snapshot.h" #include "ecmascript/ecma_vm.h" #include "ecmascript/global_dictionary-inl.h" #include "ecmascript/global_env.h" #include "ecmascript/vtable.h" #include "ecmascript/ic/ic_handler.h" +#include "ecmascript/ic/profile_type_info.h" #include "ecmascript/ic/property_box.h" #include "ecmascript/ic/proto_change_details.h" #include "ecmascript/interpreter/frame_handler.h" @@ -96,6 +98,7 @@ #include "ecmascript/layout_info-inl.h" #include "ecmascript/lexical_env.h" #include "ecmascript/linked_hash_table.h" +#include "ecmascript/marker_cell.h" #include "ecmascript/mem/assert_scope.h" #include "ecmascript/mem/c_containers.h" #include "ecmascript/mem/machine_code.h" @@ -145,11 +148,14 @@ CString JSHClass::DumpJSType(JSType type) return "TaggedDictionary"; case JSType::CONSTANT_POOL: return "ConstantPool"; + case JSType::PROFILE_TYPE_INFO: + return "ProfileTypeInfo"; case JSType::COW_TAGGED_ARRAY: return "COWArray"; case JSType::LINE_STRING: case JSType::CONSTANT_STRING: case JSType::TREE_STRING: + case JSType::SLICED_STRING: return "BaseString"; case JSType::JS_NATIVE_POINTER: return "NativePointer"; @@ -345,6 +351,8 @@ CString JSHClass::DumpJSType(JSType type) return "JSGeneratorContext"; case JSType::PROTO_CHANGE_MARKER: return "ProtoChangeMarker"; + case JSType::MARKER_CELL: + return "MarkerCell"; case JSType::PROTOTYPE_INFO: return "PrototypeInfo"; case JSType::PROGRAM: @@ -531,39 +539,61 @@ static void DumpHClass(const JSHClass *jshclass, std::ostream &os, bool withDeta LayoutInfo *layoutInfo = LayoutInfo::Cast(attrs.GetTaggedObject()); layoutInfo->Dump(os); } + os << " - Transitions :" << std::setw(DUMP_TYPE_OFFSET); JSTaggedValue transtions = jshclass->GetTransitions(); transtions.DumpTaggedValue(os); os << "\n"; - if (withDetail && !transtions.IsUndefined()) { + if (withDetail && !transtions.IsWeakForHeapObject() && transtions.IsDictionary()) { transtions.Dump(os); } + os << " - ProtoChangeMarker :" << std::setw(DUMP_TYPE_OFFSET); + JSTaggedValue marker = jshclass->GetProtoChangeMarker(); + marker.DumpTaggedValue(os); + if (marker.IsHeapObject()) { + ProtoChangeMarker::Cast(marker.GetTaggedObject())->Dump(os); + } else { + os << "\n"; + } + + os << " - ProtoChangeDetails :" << std::setw(DUMP_TYPE_OFFSET); + JSTaggedValue details = jshclass->GetProtoChangeDetails(); + details.DumpTaggedValue(os); + if (details.IsHeapObject()) { + ProtoChangeDetails::Cast(details.GetTaggedObject())->Dump(os); + } else { + os << "\n"; + } + JSTaggedValue supers = jshclass->GetSupers(); uint32_t length = 0; if (supers.IsTaggedArray()) { length = WeakVector::Cast(supers.GetTaggedObject())->GetExtraLength(); } - os << " - Supers[" << std::dec << length << "]:\n"; + os << " - Supers[" << std::dec << length << "]: "; + os << std::setw(DUMP_TYPE_OFFSET); supers.DumpTaggedValue(os); - os << "\n"; if (withDetail && !supers.IsUndefined()) { WeakVector::Cast(supers.GetTaggedObject())->Dump(os); + } else { + os << "\n"; } os << " - VTable :" << std::setw(DUMP_TYPE_OFFSET); JSTaggedValue vtable = jshclass->GetVTable(); vtable.DumpTaggedValue(os); - os << "\n"; if (withDetail && !vtable.IsUndefined()) { VTable::Cast(vtable.GetTaggedObject())->Dump(os); + } else { + os << "\n"; } os << " - Flags : " << std::setw(DUMP_TYPE_OFFSET); os << "IsCtor :" << std::boolalpha << jshclass->IsConstructor(); os << "| IsCallable :" << std::boolalpha << jshclass->IsCallable(); os << "| IsExtensible :" << std::boolalpha << jshclass->IsExtensible(); - os << "| ElementRepresentation :" << static_cast(jshclass->GetElementRepresentation()); + os << "| ElementsKind :" << Elements::GetString(jshclass->GetElementsKind()); os << "| NumberOfProps :" << std::dec << jshclass->NumberOfProps(); os << "| InlinedProperties :" << std::dec << jshclass->GetInlinedProperties(); os << "| IsTS :" << std::boolalpha << jshclass->IsTS(); @@ -573,9 +603,8 @@ static void DumpHClass(const JSHClass *jshclass, std::ostream &os, bool withDeta static void DumpClass(TaggedObject *obj, std::ostream &os) { - JSHClass *hclass = obj->GetClass(); - os << "JSHClass :" << std::setw(DUMP_TYPE_OFFSET) << " klass_(" << std::hex << hclass << ")\n"; - DumpHClass(hclass, os, true); + ASSERT(obj->GetClass()->GetObjectType() == JSType::HCLASS); + DumpHClass(JSHClass::Cast(obj), os, true); } static void DumpAttr(const PropertyAttributes &attr, bool fastMode, std::ostream &os) @@ -615,6 +644,7 @@ static void DumpObject(TaggedObject *obj, std::ostream &os) auto jsHclass = obj->GetClass(); JSType type = jsHclass->GetObjectType(); + bool needDumpHClass = false; switch (type) { case JSType::HCLASS: return DumpClass(obj, os); @@ -629,12 +659,16 @@ static void DumpObject(TaggedObject *obj, std::ostream &os) case JSType::CONSTANT_POOL: DumpConstantPoolClass(ConstantPool::Cast(obj), os); break; + case JSType::PROFILE_TYPE_INFO: + ProfileTypeInfo::Cast(obj)->Dump(os); + break; case JSType::VTABLE: VTable::Cast(obj)->Dump(os); break; case JSType::LINE_STRING: case JSType::CONSTANT_STRING: case JSType::TREE_STRING: + case JSType::SLICED_STRING: DumpStringClass(EcmaString::Cast(obj), os); os << "\n"; break; @@ -652,9 +686,11 @@ static void DumpObject(TaggedObject *obj, std::ostream &os) case JSType::JS_SYNTAX_ERROR: case JSType::JS_OOM_ERROR: case JSType::JS_ARGUMENTS: + needDumpHClass = true; JSObject::Cast(obj)->Dump(os); break; case JSType::JS_FUNCTION_BASE: + needDumpHClass = true; JSFunctionBase::Cast(obj)->Dump(os); break; case JSType::GLOBAL_ENV: @@ -663,24 +699,31 @@ static void DumpObject(TaggedObject *obj, std::ostream &os) case JSType::ACCESSOR_DATA: break; case JSType::JS_FUNCTION: + needDumpHClass = true; JSFunction::Cast(obj)->Dump(os); break; case JSType::JS_BOUND_FUNCTION: + needDumpHClass = true; JSBoundFunction::Cast(obj)->Dump(os); break; case JSType::JS_SET: + needDumpHClass = true; JSSet::Cast(obj)->Dump(os); break; case JSType::JS_MAP: + needDumpHClass = true; JSMap::Cast(obj)->Dump(os); break; case JSType::JS_WEAK_SET: + needDumpHClass = true; JSWeakSet::Cast(obj)->Dump(os); break; case JSType::JS_WEAK_MAP: + needDumpHClass = true; JSWeakMap::Cast(obj)->Dump(os); break; case JSType::JS_WEAK_REF: + needDumpHClass = true; JSWeakRef::Cast(obj)->Dump(os); break; case JSType::JS_FINALIZATION_REGISTRY: @@ -690,12 +733,15 @@ static void DumpObject(TaggedObject *obj, std::ostream &os) CellRecord::Cast(obj)->Dump(os); break; case JSType::JS_REG_EXP: + needDumpHClass = true; JSRegExp::Cast(obj)->Dump(os); break; case JSType::JS_DATE: + needDumpHClass = true; JSDate::Cast(obj)->Dump(os); break; case JSType::JS_ARRAY: + needDumpHClass = true; JSArray::Cast(obj)->Dump(os); break; case JSType::JS_TYPED_ARRAY: @@ -710,6 +756,7 @@ static void DumpObject(TaggedObject *obj, std::ostream &os) case JSType::JS_FLOAT64_ARRAY: case JSType::JS_BIGINT64_ARRAY: case JSType::JS_BIGUINT64_ARRAY: + needDumpHClass = true; JSTypedArray::Cast(obj)->Dump(os); break; case JSType::BIGINT: @@ -719,6 +766,7 @@ static void DumpObject(TaggedObject *obj, std::ostream &os) ByteArray::Cast(obj)->Dump(os); break; case JSType::JS_PROXY: + needDumpHClass = true; JSProxy::Cast(obj)->Dump(os); break; case JSType::JS_PRIMITIVE_REF: @@ -752,6 +800,7 @@ static void DumpObject(TaggedObject *obj, std::ostream &os) ResolvingFunctionsRecord::Cast(obj)->Dump(os); break; case JSType::JS_PROMISE: + needDumpHClass = true; JSPromise::Cast(obj)->Dump(os); break; case JSType::JS_PROMISE_REACTIONS_FUNCTION: @@ -800,6 +849,7 @@ static void DumpObject(TaggedObject *obj, std::ostream &os) JSProxyRevocFunction::Cast(obj)->Dump(os); break; case JSType::JS_ASYNC_FUNCTION: + needDumpHClass = true; JSAsyncFunction::Cast(obj)->Dump(os); break; case JSType::JS_ASYNC_AWAIT_STATUS_FUNCTION: @@ -809,6 +859,7 @@ static void DumpObject(TaggedObject *obj, std::ostream &os) JSGeneratorFunction::Cast(obj)->Dump(os); break; case JSType::JS_ASYNC_GENERATOR_FUNCTION: + needDumpHClass = true; JSAsyncGeneratorFunction::Cast(obj)->Dump(os); break; case JSType::JS_ASYNC_GENERATOR_RESUME_NEXT_RETURN_PROCESSOR_RST_FTN: @@ -855,13 +906,16 @@ static void DumpObject(TaggedObject *obj, std::ostream &os) PropertyBox::Cast(obj)->Dump(os); break; case JSType::JS_REALM: + needDumpHClass = true; JSRealm::Cast(obj)->Dump(os); break; #ifdef ARK_SUPPORT_INTL case JSType::JS_INTL: + needDumpHClass = true; JSIntl::Cast(obj)->Dump(os); break; case JSType::JS_LOCALE: + needDumpHClass = true; JSLocale::Cast(obj)->Dump(os); break; case JSType::JS_DATE_TIME_FORMAT: @@ -874,6 +928,7 @@ static void DumpObject(TaggedObject *obj, std::ostream &os) JSNumberFormat::Cast(obj)->Dump(os); break; case JSType::JS_COLLATOR: + needDumpHClass = true; JSCollator::Cast(obj)->Dump(os); break; case JSType::JS_PLURAL_RULES: @@ -898,12 +953,15 @@ static void DumpObject(TaggedObject *obj, std::ostream &os) break; #endif case JSType::JS_GENERATOR_OBJECT: + needDumpHClass = true; JSGeneratorObject::Cast(obj)->Dump(os); break; case JSType::JS_ASYNC_GENERATOR_OBJECT: + needDumpHClass = true; JSAsyncGeneratorObject::Cast(obj)->Dump(os); break; case JSType::JS_ASYNC_FUNC_OBJECT: + needDumpHClass = true; JSAsyncFuncObject::Cast(obj)->Dump(os); break; case JSType::JS_GENERATOR_CONTEXT: @@ -912,9 +970,15 @@ static void DumpObject(TaggedObject *obj, std::ostream &os) case JSType::PROTOTYPE_INFO: ProtoChangeDetails::Cast(obj)->Dump(os); break; + case JSType::TRACK_INFO: + TrackInfo::Cast(obj)->Dump(os); + break; case JSType::PROTO_CHANGE_MARKER: ProtoChangeMarker::Cast(obj)->Dump(os); break; + case JSType::MARKER_CELL: + MarkerCell::Cast(obj)->Dump(os); + break; case JSType::PROGRAM: Program::Cast(obj)->Dump(os); break; @@ -1083,7 +1147,9 @@ static void DumpObject(TaggedObject *obj, std::ostream &os) break; } - DumpHClass(jsHclass, os, false); + if (needDumpHClass) { + DumpHClass(jsHclass, os, false); + } } void JSTaggedValue::DumpSpecialValue(std::ostream &os) const @@ -1122,8 +1188,7 @@ void JSTaggedValue::DumpHeapObjectType(std::ostream &os) const bool isWeak = IsWeak(); TaggedObject *obj = isWeak ? GetTaggedWeakRef() : GetTaggedObject(); if (isWeak) { - os << "----------Dump Weak Referent----------" - << "\n"; + os << " [Weak Ref] "; } JSType type = obj->GetClass()->GetObjectType(); @@ -1409,7 +1474,7 @@ void JSObject::Dump(std::ostream &os) const os << ": ("; JSTaggedValue val; if (attr.IsInlinedProps()) { - val = GetPropertyInlinedProps(i); + val = GetPropertyInlinedPropsWithRep(i, attr); } else { val = properties->Get(i - static_cast(jshclass->GetInlinedProperties())); } @@ -1460,6 +1525,21 @@ void ConstantPool::Dump(std::ostream &os) const DumpArrayClass(this, os); } +void ProfileTypeInfo::Dump(std::ostream &os) const +{ + DISALLOW_GARBAGE_COLLECTION; + uint32_t len = GetCacheLength(); + os << " \n"; + for (uint32_t i = 0; i < len; i++) { + JSTaggedValue val(Get(i)); + if (!val.IsHole()) { + os << std::right << std::setw(DUMP_PROPERTY_OFFSET) << i << ": "; + val.DumpTaggedValue(os); + os << "\n"; + } + } +} + void VTable::Dump(std::ostream &os) const { DISALLOW_GARBAGE_COLLECTION; @@ -1495,9 +1575,6 @@ void JSFunction::Dump(std::ostream &os) const os << " - FunctionExtraInfo: "; GetFunctionExtraInfo().Dump(os); os << "\n"; - os << " - Module: "; - GetModule().Dump(os); - os << "\n"; os << " - Method: "; GetMethod().Dump(os); os << "\n"; @@ -1572,7 +1649,7 @@ void JSAPITreeMap::Dump(std::ostream &os) const map->Dump(os); } -void JSAPITreeMap::DumpForSnapshot(std::vector> &vec) const +void JSAPITreeMap::DumpForSnapshot(std::vector &vec) const { TaggedTreeMap *map = TaggedTreeMap::Cast(GetTreeMap().GetTaggedObject()); map->DumpForSnapshot(vec); @@ -1595,13 +1672,13 @@ void JSAPITreeMapIterator::Dump(std::ostream &os) const map->Dump(os); } -void JSAPITreeMapIterator::DumpForSnapshot(std::vector> &vec) const +void JSAPITreeMapIterator::DumpForSnapshot(std::vector &vec) const { TaggedTreeMap *map = TaggedTreeMap::Cast(JSAPITreeMap::Cast(GetIteratedMap().GetTaggedObject())->GetTreeMap().GetTaggedObject()); map->DumpForSnapshot(vec); - vec.emplace_back("NextIndex", JSTaggedValue(GetNextIndex())); - vec.emplace_back("IterationKind", JSTaggedValue(static_cast(GetIterationKind()))); + vec.emplace_back(CString("NextIndex"), JSTaggedValue(GetNextIndex())); + vec.emplace_back(CString("IterationKind"), JSTaggedValue(static_cast(GetIterationKind()))); JSObject::DumpForSnapshot(vec); } @@ -1663,8 +1740,8 @@ void TaggedTreeMap::Dump(std::ostream &os) const node.DumpTaggedValue(os); os << std::right << "}" << "\n"; - int capacity = NumberOfElements() + NumberOfDeletedElements(); - for (int index = 0; index < capacity; index++) { + uint32_t capacity = NumberOfElements() + NumberOfDeletedElements(); + for (uint32_t index = 0; index < capacity; index++) { if (GetKey(index).IsHole()) { os << std::left << std::setw(DUMP_ELEMENT_OFFSET) << "[entry] " << index << ": "; GetKey(index).DumpTaggedValue(os); @@ -1687,7 +1764,7 @@ void JSAPITreeSet::Dump(std::ostream &os) const set->Dump(os); } -void JSAPITreeSet::DumpForSnapshot(std::vector> &vec) const +void JSAPITreeSet::DumpForSnapshot(std::vector &vec) const { TaggedTreeSet *set = TaggedTreeSet::Cast(GetTreeSet().GetTaggedObject()); set->DumpForSnapshot(vec); @@ -1710,13 +1787,13 @@ void JSAPITreeSetIterator::Dump(std::ostream &os) const set->Dump(os); } -void JSAPITreeSetIterator::DumpForSnapshot(std::vector> &vec) const +void JSAPITreeSetIterator::DumpForSnapshot(std::vector &vec) const { TaggedTreeSet *set = TaggedTreeSet::Cast(JSAPITreeSet::Cast(GetIteratedSet().GetTaggedObject())->GetTreeSet().GetTaggedObject()); set->DumpForSnapshot(vec); - vec.emplace_back("NextIndex", JSTaggedValue(GetNextIndex())); - vec.emplace_back("IterationKind", JSTaggedValue(static_cast(GetIterationKind()))); + vec.emplace_back(CString("NextIndex"), JSTaggedValue(GetNextIndex())); + vec.emplace_back(CString("IterationKind"), JSTaggedValue(static_cast(GetIterationKind()))); JSObject::DumpForSnapshot(vec); } @@ -1740,8 +1817,8 @@ void TaggedTreeSet::Dump(std::ostream &os) const node.DumpTaggedValue(os); os << std::right << "}" << "\n"; - int capacity = NumberOfElements() + NumberOfDeletedElements(); - for (int index = 0; index < capacity; index++) { + uint32_t capacity = NumberOfElements() + NumberOfDeletedElements(); + for (uint32_t index = 0; index < capacity; index++) { if (GetKey(index).IsHole()) { os << std::left << std::setw(DUMP_ELEMENT_OFFSET) << "[entry] " << index << ": "; GetKey(index).DumpTaggedValue(os); @@ -1769,7 +1846,7 @@ void JSAPIPlainArray::Dump(std::ostream &os) const JSObject::Dump(os); } -void JSAPIPlainArray::DumpForSnapshot(std::vector> &vec) const +void JSAPIPlainArray::DumpForSnapshot(std::vector &vec) const { JSObject::DumpForSnapshot(vec); } @@ -1782,11 +1859,11 @@ void JSAPIPlainArrayIterator::Dump(std::ostream &os) const JSObject::Dump(os); } -void JSAPIPlainArrayIterator::DumpForSnapshot(std::vector> &vec) const +void JSAPIPlainArrayIterator::DumpForSnapshot(std::vector &vec) const { JSAPIPlainArray *array = JSAPIPlainArray::Cast(GetIteratedPlainArray().GetTaggedObject()); array->DumpForSnapshot(vec); - vec.emplace_back("NextIndex", JSTaggedValue(GetNextIndex())); + vec.emplace_back(CString("NextIndex"), JSTaggedValue(GetNextIndex())); JSObject::DumpForSnapshot(vec); } @@ -1966,9 +2043,9 @@ void JSAPIDequeIterator::Dump(std::ostream &os) const void JSAPILightWeightMap::Dump(std::ostream &os) const { - int capacity = GetSize(); + uint32_t capacity = GetSize(); os << " - length: " << std::dec << capacity << "\n"; - int i = 0; + uint32_t i = 0; TaggedArray *hashArray = TaggedArray::Cast(GetHashes().GetTaggedObject()); TaggedArray *keyArray = TaggedArray::Cast(GetKeys().GetTaggedObject()); TaggedArray *valueArray = TaggedArray::Cast(GetValues().GetTaggedObject()); @@ -2000,7 +2077,7 @@ void JSAPIHashMap::Dump(std::ostream &os) const JSObject::Dump(os); } -void JSAPIHashMap::DumpForSnapshot(std::vector> &vec) const +void JSAPIHashMap::DumpForSnapshot(std::vector &vec) const { JSObject::DumpForSnapshot(vec); } @@ -2013,7 +2090,7 @@ void JSAPIHashSet::Dump(std::ostream &os) const JSObject::Dump(os); } -void JSAPIHashSet::DumpForSnapshot(std::vector> &vec) const +void JSAPIHashSet::DumpForSnapshot(std::vector &vec) const { JSObject::DumpForSnapshot(vec); } @@ -2025,7 +2102,7 @@ void JSAPIHashMapIterator::Dump(std::ostream &os) const JSObject::Dump(os); } -void JSAPIHashMapIterator::DumpForSnapshot(std::vector> &vec) const +void JSAPIHashMapIterator::DumpForSnapshot(std::vector &vec) const { JSObject::DumpForSnapshot(vec); } @@ -2037,7 +2114,7 @@ void JSAPIHashSetIterator::Dump(std::ostream &os) const JSObject::Dump(os); } -void JSAPIHashSetIterator::DumpForSnapshot(std::vector> &vec) const +void JSAPIHashSetIterator::DumpForSnapshot(std::vector &vec) const { JSObject::DumpForSnapshot(vec); } @@ -2076,10 +2153,10 @@ void JSAPIList::Dump(std::ostream &os) const list->Dump(os); } -void JSAPIList::DumpForSnapshot(std::vector> &vec) const +void JSAPIList::DumpForSnapshot(std::vector &vec) const { - TaggedSingleList *map = TaggedSingleList::Cast(GetSingleList().GetTaggedObject()); - map->DumpForSnapshot(vec); + TaggedSingleList *list = TaggedSingleList::Cast(GetSingleList().GetTaggedObject()); + list->DumpForSnapshot(vec); JSObject::DumpForSnapshot(vec); } @@ -2095,11 +2172,11 @@ void JSAPIListIterator::Dump(std::ostream &os) const list->Dump(os); } -void JSAPIListIterator::DumpForSnapshot(std::vector> &vec) const +void JSAPIListIterator::DumpForSnapshot(std::vector &vec) const { TaggedSingleList *list = TaggedSingleList::Cast(GetIteratedList().GetTaggedObject()); list->DumpForSnapshot(vec); - vec.emplace_back("NextIndex", JSTaggedValue(GetNextIndex())); + vec.emplace_back(CString("NextIndex"), JSTaggedValue(GetNextIndex())); JSObject::DumpForSnapshot(vec); } @@ -2113,10 +2190,10 @@ void JSAPILinkedList::Dump(std::ostream &os) const linkedList->Dump(os); } -void JSAPILinkedList::DumpForSnapshot(std::vector> &vec) const +void JSAPILinkedList::DumpForSnapshot(std::vector &vec) const { - TaggedDoubleList *map = TaggedDoubleList::Cast(GetDoubleList().GetTaggedObject()); - map->DumpForSnapshot(vec); + TaggedDoubleList *list = TaggedDoubleList::Cast(GetDoubleList().GetTaggedObject()); + list->DumpForSnapshot(vec); JSObject::DumpForSnapshot(vec); } @@ -2132,11 +2209,11 @@ void JSAPILinkedListIterator::Dump(std::ostream &os) const linkedList->Dump(os); } -void JSAPILinkedListIterator::DumpForSnapshot(std::vector> &vec) const +void JSAPILinkedListIterator::DumpForSnapshot(std::vector &vec) const { TaggedDoubleList *linkedList = TaggedDoubleList::Cast(GetIteratedLinkedList().GetTaggedObject()); linkedList->DumpForSnapshot(vec); - vec.emplace_back("NextIndex", JSTaggedValue(GetNextIndex())); + vec.emplace_back(CString("NextIndex"), JSTaggedValue(GetNextIndex())); JSObject::DumpForSnapshot(vec); } @@ -3095,13 +3172,22 @@ void ProtoChangeMarker::Dump(std::ostream &os) const os << " - HasChanged: " << GetHasChanged() << "\n"; } +void MarkerCell::Dump(std::ostream &os) const +{ + os << " - IsDetectorInvalid: " << GetIsDetectorInvalid() << "\n"; +} + void ProtoChangeDetails::Dump(std::ostream &os) const { os << " - ChangeListener: "; GetChangeListener().Dump(os); + os << " \t- RegisterIndex: " << GetRegisterIndex(); os << "\n"; - os << " - RegisterIndex: " << GetRegisterIndex(); - os << "\n"; +} + +void TrackInfo::Dump(std::ostream &os) const +{ + os << " - ElementsKind: " << static_cast(GetElementsKind()) << "\n"; } void MachineCode::Dump(std::ostream &os) const @@ -3564,7 +3650,7 @@ void CjsExports::Dump(std::ostream &os) const os << ": ("; JSTaggedValue val; if (attr.IsInlinedProps()) { - val = GetPropertyInlinedProps(i); + val = GetPropertyInlinedPropsWithRep(i, attr); } else { val = properties->Get(i - static_cast(jshclass->GetInlinedProperties())); } @@ -3598,6 +3684,9 @@ void Method::Dump(std::ostream &os) const os << " - ProfileTypeInfo: "; GetProfileTypeInfo().Dump(os); os << "\n"; + os << " - Module: "; + GetModule().Dump(os); + os << "\n"; os << " - FunctionKind: " << static_cast(GetFunctionKind()); os << "\n"; os << " - CodeEntryOrLiteral: " << std::hex << GetCodeEntryOrLiteral() << "\n"; @@ -3618,8 +3707,7 @@ void ClassLiteral::Dump(std::ostream &os) const // ######################################################################################## // Dump for Snapshot // ######################################################################################## -static void DumpArrayClass(const TaggedArray *arr, - std::vector> &vec) +static void DumpArrayClass(const TaggedArray *arr, std::vector &vec) { DISALLOW_GARBAGE_COLLECTION; uint32_t len = arr->GetLength(); @@ -3631,8 +3719,18 @@ static void DumpArrayClass(const TaggedArray *arr, } } -static void DumpConstantPoolClass(const ConstantPool *arr, - std::vector> &vec) +static void DumpElementClass(const TaggedArray *arr, std::vector &vec) +{ + DISALLOW_GARBAGE_COLLECTION; + uint32_t len = arr->GetLength(); + vec.reserve(vec.size() + len); + for (uint32_t i = 0; i < len; i++) { + JSTaggedValue val(arr->Get(i)); + vec.emplace_back(i, val, Reference::ReferenceType::ELEMENT); + } +} + +static void DumpConstantPoolClass(const ConstantPool *arr, std::vector &vec) { DISALLOW_GARBAGE_COLLECTION; uint32_t len = arr->GetCacheLength(); @@ -3644,21 +3742,19 @@ static void DumpConstantPoolClass(const ConstantPool *arr, } } -static void DumpStringClass(const EcmaString *str, - std::vector> &vec) +static void DumpStringClass([[maybe_unused]] const EcmaString *str, [[maybe_unused]] std::vector &vec) { - vec.emplace_back("string", JSTaggedValue(str)); + // Before EcmaString dump self node, it need not show, so delete. + // If some properties need be shown, add here. } -static void DumpClass(TaggedObject *obj, - std::vector> &vec) +static void DumpClass(TaggedObject *obj, std::vector &vec) { JSHClass *jshclass = obj->GetClass(); - vec.emplace_back("__proto__", jshclass->GetPrototype()); + vec.emplace_back(CString("__proto__"), jshclass->GetPrototype()); } -static void DumpObject(TaggedObject *obj, - std::vector> &vec, bool isVmMode) +static void DumpObject(TaggedObject *obj, std::vector &vec, bool isVmMode) { DISALLOW_GARBAGE_COLLECTION; auto jsHclass = obj->GetClass(); @@ -3672,14 +3768,22 @@ static void DumpObject(TaggedObject *obj, case JSType::TAGGED_DICTIONARY: case JSType::LEXICAL_ENV: case JSType::COW_TAGGED_ARRAY: + case JSType::AOT_LITERAL_INFO: DumpArrayClass(TaggedArray::Cast(obj), vec); return; case JSType::CONSTANT_POOL: DumpConstantPoolClass(ConstantPool::Cast(obj), vec); return; + case JSType::VTABLE: + VTable::Cast(obj)->DumpForSnapshot(vec); + return; + case JSType::PROFILE_TYPE_INFO: + ProfileTypeInfo::Cast(obj)->DumpForSnapshot(vec); + return; case JSType::LINE_STRING: case JSType::CONSTANT_STRING: case JSType::TREE_STRING: + case JSType::SLICED_STRING: DumpStringClass(EcmaString::Cast(obj), vec); return; case JSType::JS_NATIVE_POINTER: @@ -4058,9 +4162,15 @@ static void DumpObject(TaggedObject *obj, case JSType::PROTO_CHANGE_MARKER: ProtoChangeMarker::Cast(obj)->DumpForSnapshot(vec); return; + case JSType::MARKER_CELL: + MarkerCell::Cast(obj)->DumpForSnapshot(vec); + return; case JSType::PROTOTYPE_INFO: ProtoChangeDetails::Cast(obj)->DumpForSnapshot(vec); return; + case JSType::TRACK_INFO: + TrackInfo::Cast(obj)->DumpForSnapshot(vec); + return; case JSType::PROGRAM: Program::Cast(obj)->DumpForSnapshot(vec); return; @@ -4158,7 +4268,7 @@ static void KeyToStd(CString &res, JSTaggedValue key) } } -void JSTaggedValue::DumpForSnapshot(std::vector> &vec, bool isVmMode) const +void JSTaggedValue::DumpForSnapshot(std::vector &vec, bool isVmMode) const { if (IsHeapObject()) { return DumpObject(GetTaggedObject(), vec, isVmMode); @@ -4167,7 +4277,7 @@ void JSTaggedValue::DumpForSnapshot(std::vector> &vec) const +void NumberDictionary::DumpForSnapshot(std::vector &vec) const { DISALLOW_GARBAGE_COLLECTION; int size = Size(); @@ -4176,13 +4286,13 @@ void NumberDictionary::DumpForSnapshot(std::vector(JSTaggedNumber(key).GetNumber())); - vec.emplace_back(str, val); + vec.emplace_back( + static_cast(JSTaggedNumber(key).GetNumber()), val, Reference::ReferenceType::ELEMENT); } } } -void NameDictionary::DumpForSnapshot(std::vector> &vec) const +void NameDictionary::DumpForSnapshot(std::vector &vec) const { DISALLOW_GARBAGE_COLLECTION; int size = Size(); @@ -4198,7 +4308,7 @@ void NameDictionary::DumpForSnapshot(std::vector> &vec) const +void GlobalDictionary::DumpForSnapshot(std::vector &vec) const { DISALLOW_GARBAGE_COLLECTION; int size = Size(); @@ -4214,7 +4324,7 @@ void GlobalDictionary::DumpForSnapshot(std::vector> &vec) const +void LinkedHashSet::DumpForSnapshot(std::vector &vec) const { DISALLOW_GARBAGE_COLLECTION; int capacity = NumberOfElements() + NumberOfDeletedElements(); @@ -4229,7 +4339,7 @@ void LinkedHashSet::DumpForSnapshot(std::vector> &vec) const +void LinkedHashMap::DumpForSnapshot(std::vector &vec) const { DISALLOW_GARBAGE_COLLECTION; int capacity = NumberOfElements() + NumberOfDeletedElements(); @@ -4245,12 +4355,12 @@ void LinkedHashMap::DumpForSnapshot(std::vector> &vec) const +void TaggedTreeMap::DumpForSnapshot(std::vector &vec) const { DISALLOW_GARBAGE_COLLECTION; - int capacity = NumberOfElements() + NumberOfDeletedElements(); + uint32_t capacity = NumberOfElements() + NumberOfDeletedElements(); vec.reserve(vec.size() + capacity); - for (int index = 0; index < capacity; index++) { + for (uint32_t index = 0; index < capacity; index++) { JSTaggedValue key(GetKey(index)); if (!key.IsUndefined() && !key.IsHole() && !key.IsNull()) { JSTaggedValue val = GetValue(index); @@ -4261,12 +4371,12 @@ void TaggedTreeMap::DumpForSnapshot(std::vector> &vec) const +void TaggedTreeSet::DumpForSnapshot(std::vector &vec) const { DISALLOW_GARBAGE_COLLECTION; - int capacity = NumberOfElements() + NumberOfDeletedElements(); + uint32_t capacity = NumberOfElements() + NumberOfDeletedElements(); vec.reserve(vec.size() + capacity); - for (int index = 0; index < capacity; index++) { + for (uint32_t index = 0; index < capacity; index++) { JSTaggedValue key(GetKey(index)); if (!key.IsUndefined() && !key.IsHole() && !key.IsNull()) { CString str; @@ -4276,7 +4386,7 @@ void TaggedTreeSet::DumpForSnapshot(std::vector> &vec) const +void TaggedDoubleList::DumpForSnapshot(std::vector &vec) const { DISALLOW_GARBAGE_COLLECTION; int capacity = NumberOfNodes(); @@ -4289,7 +4399,7 @@ void TaggedDoubleList::DumpForSnapshot(std::vector> &vec) const +void TaggedSingleList::DumpForSnapshot(std::vector &vec) const { DISALLOW_GARBAGE_COLLECTION; int capacity = NumberOfNodes(); @@ -4302,16 +4412,16 @@ void TaggedSingleList::DumpForSnapshot(std::vector> &vec) const +void JSObject::DumpForSnapshot(std::vector &vec) const { DISALLOW_GARBAGE_COLLECTION; JSHClass *jshclass = GetJSHClass(); - vec.emplace_back("__proto__", jshclass->GetPrototype()); + vec.emplace_back(CString("__proto__"), jshclass->GetPrototype()); TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); if (elements->GetLength() == 0) { } else if (!elements->IsDictionaryMode()) { - DumpArrayClass(elements, vec); + DumpElementClass(elements, vec); } else { NumberDictionary *dict = NumberDictionary::Cast(elements); dict->DumpForSnapshot(vec); @@ -4339,7 +4449,7 @@ void JSObject::DumpForSnapshot(std::vector> &v ASSERT(i == static_cast(attr.GetOffset())); JSTaggedValue val; if (attr.IsInlinedProps()) { - val = GetPropertyInlinedProps(i); + val = GetPropertyInlinedPropsWithRep(i, attr); } else { val = properties->Get(i - static_cast(jshclass->GetInlinedProperties())); } @@ -4354,76 +4464,89 @@ void JSObject::DumpForSnapshot(std::vector> &v } } -void JSHClass::DumpForSnapshot([[maybe_unused]] std::vector> &vec) const +void JSHClass::DumpForSnapshot([[maybe_unused]] std::vector &vec) const { } -void JSFunction::DumpForSnapshot(std::vector> &vec) const +void JSFunction::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("ProtoOrHClass", GetProtoOrHClass()); - vec.emplace_back("LexicalEnv", GetLexicalEnv()); - vec.emplace_back("HomeObject", GetHomeObject()); - vec.emplace_back("FunctionKind", JSTaggedValue(static_cast(GetFunctionKind()))); - vec.emplace_back("FunctionExtraInfo", GetFunctionExtraInfo()); + vec.emplace_back(CString("ProtoOrHClass"), GetProtoOrHClass()); + vec.emplace_back(CString("LexicalEnv"), GetLexicalEnv()); + vec.emplace_back(CString("HomeObject"), GetHomeObject()); + vec.emplace_back(CString("FunctionKind"), JSTaggedValue(static_cast(GetFunctionKind()))); + vec.emplace_back(CString("FunctionExtraInfo"), GetFunctionExtraInfo()); JSObject::DumpForSnapshot(vec); } -void Method::DumpForSnapshot(std::vector> &vec) const +void Method::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("ConstantPool", GetConstantPool()); - vec.emplace_back("ProfileTypeInfo", GetProfileTypeInfo()); + vec.emplace_back(CString("ConstantPool"), GetConstantPool()); + vec.emplace_back(CString("ProfileTypeInfo"), GetProfileTypeInfo()); + vec.emplace_back(CString("Module"), GetModule()); } -void Program::DumpForSnapshot(std::vector> &vec) const +void Program::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("MainFunction", GetMainFunction()); + vec.emplace_back(CString("MainFunction"), GetMainFunction()); } -void ConstantPool::DumpForSnapshot(std::vector> &vec) const +void ConstantPool::DumpForSnapshot(std::vector &vec) const { DumpArrayClass(this, vec); } -void VTable::DumpForSnapshot(std::vector> &vec) const +void VTable::DumpForSnapshot(std::vector &vec) const { DumpArrayClass(this, vec); } -void COWTaggedArray::DumpForSnapshot(std::vector> &vec) const +void ProfileTypeInfo::DumpForSnapshot(std::vector &vec) const +{ + DISALLOW_GARBAGE_COLLECTION; + uint32_t len = GetCacheLength(); + vec.reserve(vec.size() + len); + for (uint32_t i = 0; i < len; i++) { + JSTaggedValue val(Get(i)); + CString str = ToCString(i); + vec.emplace_back(str, val); + } +} + +void COWTaggedArray::DumpForSnapshot(std::vector &vec) const { DumpArrayClass(this, vec); } -void JSBoundFunction::DumpForSnapshot(std::vector> &vec) const +void JSBoundFunction::DumpForSnapshot(std::vector &vec) const { JSObject::DumpForSnapshot(vec); - vec.emplace_back("BoundTarget", GetBoundTarget()); - vec.emplace_back("BoundThis", GetBoundThis()); - vec.emplace_back("BoundArguments", GetBoundArguments()); + vec.emplace_back(CString("BoundTarget"), GetBoundTarget()); + vec.emplace_back(CString("BoundThis"), GetBoundThis()); + vec.emplace_back(CString("BoundArguments"), GetBoundArguments()); } -void JSPrimitiveRef::DumpForSnapshot(std::vector> &vec) const +void JSPrimitiveRef::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("subValue", GetValue()); + vec.emplace_back(CString("subValue"), GetValue()); JSObject::DumpForSnapshot(vec); } -void BigInt::DumpForSnapshot(std::vector> &vec) const +void BigInt::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("Length", JSTaggedValue(GetLength())); - vec.emplace_back("Sign", JSTaggedValue(GetSign())); + vec.emplace_back(CString("Length"), JSTaggedValue(GetLength())); + vec.emplace_back(CString("Sign"), JSTaggedValue(GetSign())); } -void JSDate::DumpForSnapshot(std::vector> &vec) const +void JSDate::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("time", GetTime()); - vec.emplace_back("localOffset", GetLocalOffset()); + vec.emplace_back(CString("time"), GetTime()); + vec.emplace_back(CString("localOffset"), GetLocalOffset()); JSObject::DumpForSnapshot(vec); } -void JSMap::DumpForSnapshot(std::vector> &vec) const +void JSMap::DumpForSnapshot(std::vector &vec) const { LinkedHashMap *map = LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject()); map->DumpForSnapshot(vec); @@ -4431,25 +4554,25 @@ void JSMap::DumpForSnapshot(std::vector> &vec) JSObject::DumpForSnapshot(vec); } -void JSForInIterator::DumpForSnapshot(std::vector> &vec) const +void JSForInIterator::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("Object", GetObject()); - vec.emplace_back("WasVisited", JSTaggedValue(GetWasVisited())); - vec.emplace_back("VisitedObjs", GetVisitedObjs()); - vec.emplace_back("RemainingKeys", GetRemainingKeys()); + vec.emplace_back(CString("Object"), GetObject()); + vec.emplace_back(CString("WasVisited"), JSTaggedValue(GetWasVisited())); + vec.emplace_back(CString("VisitedObjs"), GetVisitedObjs()); + vec.emplace_back(CString("RemainingKeys"), GetRemainingKeys()); JSObject::DumpForSnapshot(vec); } -void JSMapIterator::DumpForSnapshot(std::vector> &vec) const +void JSMapIterator::DumpForSnapshot(std::vector &vec) const { LinkedHashMap *map = LinkedHashMap::Cast(GetIteratedMap().GetTaggedObject()); map->DumpForSnapshot(vec); - vec.emplace_back("NextIndex", JSTaggedValue(GetNextIndex())); - vec.emplace_back("IterationKind", JSTaggedValue(static_cast(GetIterationKind()))); + vec.emplace_back(CString("NextIndex"), JSTaggedValue(GetNextIndex())); + vec.emplace_back(CString("IterationKind"), JSTaggedValue(static_cast(GetIterationKind()))); JSObject::DumpForSnapshot(vec); } -void JSSet::DumpForSnapshot(std::vector> &vec) const +void JSSet::DumpForSnapshot(std::vector &vec) const { LinkedHashSet *set = LinkedHashSet::Cast(GetLinkedSet().GetTaggedObject()); set->DumpForSnapshot(vec); @@ -4457,7 +4580,7 @@ void JSSet::DumpForSnapshot(std::vector> &vec) JSObject::DumpForSnapshot(vec); } -void JSWeakMap::DumpForSnapshot(std::vector> &vec) const +void JSWeakMap::DumpForSnapshot(std::vector &vec) const { LinkedHashMap *map = LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject()); map->DumpForSnapshot(vec); @@ -4465,7 +4588,7 @@ void JSWeakMap::DumpForSnapshot(std::vector> & JSObject::DumpForSnapshot(vec); } -void JSWeakSet::DumpForSnapshot(std::vector> &vec) const +void JSWeakSet::DumpForSnapshot(std::vector &vec) const { LinkedHashSet *set = LinkedHashSet::Cast(GetLinkedSet().GetTaggedObject()); set->DumpForSnapshot(vec); @@ -4473,930 +4596,939 @@ void JSWeakSet::DumpForSnapshot(std::vector> & JSObject::DumpForSnapshot(vec); } -void JSWeakRef::DumpForSnapshot(std::vector> &vec) const +void JSWeakRef::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("WeakObject", GetWeakObject()); + vec.emplace_back(CString("WeakObject"), GetWeakObject()); JSObject::DumpForSnapshot(vec); } -void JSFinalizationRegistry::DumpForSnapshot(std::vector> &vec) const +void JSFinalizationRegistry::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("CleanupCallback", GetCleanupCallback()); + vec.emplace_back(CString("CleanupCallback"), GetCleanupCallback()); LinkedHashMap *map = LinkedHashMap::Cast(GetMaybeUnregister().GetTaggedObject()); map->DumpForSnapshot(vec); - vec.emplace_back("MaybeUnregister", GetMaybeUnregister()); + vec.emplace_back(CString("MaybeUnregister"), GetMaybeUnregister()); JSObject::DumpForSnapshot(vec); } -void CellRecord::DumpForSnapshot(std::vector> &vec) const +void CellRecord::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("WeakRefTarget", GetWeakRefTarget()); - vec.emplace_back("HeldValue", GetHeldValue()); + vec.emplace_back(CString("WeakRefTarget"), GetWeakRefTarget()); + vec.emplace_back(CString("HeldValue"), GetHeldValue()); } -void JSSetIterator::DumpForSnapshot(std::vector> &vec) const +void JSSetIterator::DumpForSnapshot(std::vector &vec) const { LinkedHashSet *set = LinkedHashSet::Cast(GetIteratedSet().GetTaggedObject()); set->DumpForSnapshot(vec); - vec.emplace_back("NextIndex", JSTaggedValue(GetNextIndex())); - vec.emplace_back("IterationKind", JSTaggedValue(static_cast(GetIterationKind()))); + vec.emplace_back(CString("NextIndex"), JSTaggedValue(GetNextIndex())); + vec.emplace_back(CString("IterationKind"), JSTaggedValue(static_cast(GetIterationKind()))); JSObject::DumpForSnapshot(vec); } -void JSArray::DumpForSnapshot(std::vector> &vec) const +void JSArray::DumpForSnapshot(std::vector &vec) const { JSObject::DumpForSnapshot(vec); } -void JSAPIArrayList::DumpForSnapshot(std::vector> &vec) const +void JSAPIArrayList::DumpForSnapshot(std::vector &vec) const { JSObject::DumpForSnapshot(vec); } -void JSAPIArrayListIterator::DumpForSnapshot(std::vector> &vec) const +void JSAPIArrayListIterator::DumpForSnapshot(std::vector &vec) const { JSAPIArrayList *arraylist = JSAPIArrayList::Cast(GetIteratedArrayList().GetTaggedObject()); arraylist->DumpForSnapshot(vec); - vec.emplace_back("NextIndex", JSTaggedValue(GetNextIndex())); + vec.emplace_back(CString("NextIndex"), JSTaggedValue(GetNextIndex())); JSObject::DumpForSnapshot(vec); } -void JSAPILightWeightMap::DumpForSnapshot(std::vector> &vec) const +void JSAPILightWeightMap::DumpForSnapshot(std::vector &vec) const { JSObject::DumpForSnapshot(vec); } -void JSAPILightWeightMapIterator::DumpForSnapshot(std::vector> &vec) const +void JSAPILightWeightMapIterator::DumpForSnapshot(std::vector &vec) const { JSAPILightWeightMap *map = JSAPILightWeightMap::Cast(GetIteratedLightWeightMap().GetTaggedObject()); map->DumpForSnapshot(vec); - vec.emplace_back("NextIndex", JSTaggedValue(GetNextIndex())); - vec.emplace_back("IterationKind", JSTaggedValue(static_cast(GetIterationKind()))); + vec.emplace_back(CString("NextIndex"), JSTaggedValue(GetNextIndex())); + vec.emplace_back(CString("IterationKind"), JSTaggedValue(static_cast(GetIterationKind()))); JSObject::DumpForSnapshot(vec); } -void JSAPIQueue::DumpForSnapshot(std::vector> &vec) const +void JSAPIQueue::DumpForSnapshot(std::vector &vec) const { JSObject::DumpForSnapshot(vec); } -void JSAPIQueueIterator::DumpForSnapshot(std::vector> &vec) const +void JSAPIQueueIterator::DumpForSnapshot(std::vector &vec) const { JSAPIQueue *queue = JSAPIQueue::Cast(GetIteratedQueue().GetTaggedObject()); queue->DumpForSnapshot(vec); - vec.emplace_back("NextIndex", JSTaggedValue(GetNextIndex())); + vec.emplace_back(CString("NextIndex"), JSTaggedValue(GetNextIndex())); JSObject::DumpForSnapshot(vec); } -void JSAPIDeque::DumpForSnapshot(std::vector> &vec) const +void JSAPIDeque::DumpForSnapshot(std::vector &vec) const { JSObject::DumpForSnapshot(vec); } -void JSAPIDequeIterator::DumpForSnapshot(std::vector> &vec) const +void JSAPIDequeIterator::DumpForSnapshot(std::vector &vec) const { JSAPIDeque *deque = JSAPIDeque::Cast(GetIteratedDeque().GetTaggedObject()); deque->DumpForSnapshot(vec); - vec.emplace_back("NextIndex", JSTaggedValue(GetNextIndex())); + vec.emplace_back(CString("NextIndex"), JSTaggedValue(GetNextIndex())); JSObject::DumpForSnapshot(vec); } -void JSAPILightWeightSet::DumpForSnapshot(std::vector> &vec) const +void JSAPILightWeightSet::DumpForSnapshot(std::vector &vec) const { JSObject::DumpForSnapshot(vec); } -void JSAPILightWeightSetIterator::DumpForSnapshot(std::vector> &vec) const +void JSAPILightWeightSetIterator::DumpForSnapshot(std::vector &vec) const { JSAPILightWeightSet *set = JSAPILightWeightSet::Cast(GetIteratedLightWeightSet().GetTaggedObject()); set->DumpForSnapshot(vec); - vec.emplace_back("NextIndex", JSTaggedValue(GetNextIndex())); - vec.emplace_back("IterationKind", JSTaggedValue(static_cast(GetIterationKind()))); + vec.emplace_back(CString("NextIndex"), JSTaggedValue(GetNextIndex())); + vec.emplace_back(CString("IterationKind"), JSTaggedValue(static_cast(GetIterationKind()))); JSObject::DumpForSnapshot(vec); } -void JSAPIStack::DumpForSnapshot(std::vector> &vec) const +void JSAPIStack::DumpForSnapshot(std::vector &vec) const { JSObject::DumpForSnapshot(vec); } -void JSAPIStackIterator::DumpForSnapshot(std::vector> &vec) const +void JSAPIStackIterator::DumpForSnapshot(std::vector &vec) const { JSAPIStack *stack = JSAPIStack::Cast(GetIteratedStack().GetTaggedObject()); stack->DumpForSnapshot(vec); - vec.emplace_back("NextIndex", JSTaggedValue(GetNextIndex())); + vec.emplace_back(CString("NextIndex"), JSTaggedValue(GetNextIndex())); JSObject::DumpForSnapshot(vec); } -void JSArrayIterator::DumpForSnapshot(std::vector> &vec) const +void JSArrayIterator::DumpForSnapshot(std::vector &vec) const { JSArray *array = JSArray::Cast(GetIteratedArray().GetTaggedObject()); array->DumpForSnapshot(vec); - vec.emplace_back("NextIndex", JSTaggedValue(GetNextIndex())); - vec.emplace_back("IterationKind", JSTaggedValue(static_cast(GetIterationKind()))); + vec.emplace_back(CString("NextIndex"), JSTaggedValue(GetNextIndex())); + vec.emplace_back(CString("IterationKind"), JSTaggedValue(static_cast(GetIterationKind()))); JSObject::DumpForSnapshot(vec); } -void JSAPIVector::DumpForSnapshot(std::vector> &vec) const +void JSAPIVector::DumpForSnapshot(std::vector &vec) const { JSObject::DumpForSnapshot(vec); } -void JSAPIVectorIterator::DumpForSnapshot(std::vector> &vec) const +void JSAPIVectorIterator::DumpForSnapshot(std::vector &vec) const { JSAPIVector *vector = JSAPIVector::Cast(GetIteratedVector().GetTaggedObject()); vector->DumpForSnapshot(vec); - vec.emplace_back("NextIndex", JSTaggedValue(GetNextIndex())); + vec.emplace_back(CString("NextIndex"), JSTaggedValue(GetNextIndex())); JSObject::DumpForSnapshot(vec); } -void JSStringIterator::DumpForSnapshot(std::vector> &vec) const +void JSStringIterator::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("IteratedString", GetIteratedString()); - vec.emplace_back("StringIteratorNextIndex", JSTaggedValue(GetStringIteratorNextIndex())); + vec.emplace_back(CString("IteratedString"), GetIteratedString()); + vec.emplace_back(CString("StringIteratorNextIndex"), JSTaggedValue(GetStringIteratorNextIndex())); JSObject::DumpForSnapshot(vec); } -void JSTypedArray::DumpForSnapshot(std::vector> &vec) const +void JSTypedArray::DumpForSnapshot(std::vector &vec) const { // please update the NUM_OF_ITEMS if you change the items below constexpr int16_t NUM_OF_ITEMS = 5; vec.reserve(vec.size() + NUM_OF_ITEMS); - vec.emplace_back("viewed-array-buffer", GetViewedArrayBufferOrByteArray()); - vec.emplace_back("typed-array-name", GetTypedArrayName()); - vec.emplace_back("byte-length", JSTaggedValue(GetByteLength())); - vec.emplace_back("byte-offset", JSTaggedValue(GetByteOffset())); - vec.emplace_back("array-length", JSTaggedValue(GetArrayLength())); + vec.emplace_back(CString("viewed-array-buffer"), GetViewedArrayBufferOrByteArray()); + vec.emplace_back(CString("typed-array-name"), GetTypedArrayName()); + vec.emplace_back(CString("byte-length"), JSTaggedValue(GetByteLength())); + vec.emplace_back(CString("byte-offset"), JSTaggedValue(GetByteOffset())); + vec.emplace_back(CString("array-length"), JSTaggedValue(GetArrayLength())); } -void ByteArray::DumpForSnapshot(std::vector> &vec) const +void ByteArray::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("array-length", JSTaggedValue(GetArrayLength())); - vec.emplace_back("byte-length", JSTaggedValue(GetByteLength())); + vec.emplace_back(CString("array-length"), JSTaggedValue(GetArrayLength())); + vec.emplace_back(CString("byte-length"), JSTaggedValue(GetByteLength())); } -void JSRegExp::DumpForSnapshot(std::vector> &vec) const +void JSRegExp::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("originalSource", GetOriginalSource()); - vec.emplace_back("originalFlags", GetOriginalFlags()); - vec.emplace_back("groupName", GetGroupName()); + vec.emplace_back(CString("originalSource"), GetOriginalSource()); + vec.emplace_back(CString("originalFlags"), GetOriginalFlags()); + vec.emplace_back(CString("groupName"), GetGroupName()); JSObject::DumpForSnapshot(vec); } -void JSRegExpIterator::DumpForSnapshot(std::vector> &vec) const +void JSRegExpIterator::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("IteratingRegExp", GetIteratingRegExp()); - vec.emplace_back("IteratedString", GetIteratedString()); - vec.emplace_back("Global", JSTaggedValue(GetGlobal())); - vec.emplace_back("Unicode", JSTaggedValue(GetUnicode())); - vec.emplace_back("Done", JSTaggedValue(GetDone())); + vec.emplace_back(CString("IteratingRegExp"), GetIteratingRegExp()); + vec.emplace_back(CString("IteratedString"), GetIteratedString()); + vec.emplace_back(CString("Global"), JSTaggedValue(GetGlobal())); + vec.emplace_back(CString("Unicode"), JSTaggedValue(GetUnicode())); + vec.emplace_back(CString("Done"), JSTaggedValue(GetDone())); JSObject::DumpForSnapshot(vec); } -void JSProxy::DumpForSnapshot(std::vector> &vec) const +void JSProxy::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("target", GetTarget()); - vec.emplace_back("handler", GetHandler()); + vec.emplace_back(CString("target"), GetTarget()); + vec.emplace_back(CString("handler"), GetHandler()); } -void JSSymbol::DumpForSnapshot(std::vector> &vec) const +void JSSymbol::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("hash-field", JSTaggedValue(GetHashField())); - vec.emplace_back("flags", JSTaggedValue(GetFlags())); - vec.emplace_back("description", GetDescription()); + vec.emplace_back(CString("hash-field"), JSTaggedValue(GetHashField())); + vec.emplace_back(CString("flags"), JSTaggedValue(GetFlags())); + vec.emplace_back(CString("description"), GetDescription()); } -void AccessorData::DumpForSnapshot(std::vector> &vec) const +void AccessorData::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("getter", GetGetter()); - vec.emplace_back("setter", GetSetter()); + vec.emplace_back(CString("getter"), GetGetter()); + vec.emplace_back(CString("setter"), GetSetter()); } -void LexicalEnv::DumpForSnapshot(std::vector> &vec) const +void LexicalEnv::DumpForSnapshot(std::vector &vec) const { DumpArrayClass(this, vec); } -void GlobalEnv::DumpForSnapshot(std::vector> &vec) const +void GlobalEnv::DumpForSnapshot(std::vector &vec) const { auto globalConst = GetJSThread()->GlobalConstants(); // please update the NUM_OF_ITEMS if you change the items below constexpr int16_t NUM_OF_ITEMS = 137; vec.reserve(vec.size() + NUM_OF_ITEMS); - vec.emplace_back("ObjectFunction", GetObjectFunction().GetTaggedValue()); - vec.emplace_back("FunctionFunction", GetFunctionFunction().GetTaggedValue()); - vec.emplace_back("NumberFunction", GetNumberFunction().GetTaggedValue()); - vec.emplace_back("BigIntFunction", GetBigIntFunction().GetTaggedValue()); - vec.emplace_back("DateFunction", GetDateFunction().GetTaggedValue()); - vec.emplace_back("BooleanFunction", GetBooleanFunction().GetTaggedValue()); - vec.emplace_back("ErrorFunction", GetErrorFunction().GetTaggedValue()); - vec.emplace_back("ArrayFunction", GetArrayFunction().GetTaggedValue()); - vec.emplace_back("TypedArrayFunction", GetTypedArrayFunction().GetTaggedValue()); - vec.emplace_back("Int8ArrayFunction", GetInt8ArrayFunction().GetTaggedValue()); - vec.emplace_back("Uint8ArrayFunction", GetUint8ArrayFunction().GetTaggedValue()); - vec.emplace_back("Uint8ClampedArrayFunction", GetUint8ClampedArrayFunction().GetTaggedValue()); - vec.emplace_back("Int16ArrayFunction", GetInt16ArrayFunction().GetTaggedValue()); - vec.emplace_back("Uint16ArrayFunction", GetUint16ArrayFunction().GetTaggedValue()); - vec.emplace_back("Int32ArrayFunction", GetInt32ArrayFunction().GetTaggedValue()); - vec.emplace_back("Uint32ArrayFunction", GetUint32ArrayFunction().GetTaggedValue()); - vec.emplace_back("Float32ArrayFunction", GetFloat32ArrayFunction().GetTaggedValue()); - vec.emplace_back("Float64ArrayFunction", GetFloat64ArrayFunction().GetTaggedValue()); - vec.emplace_back("ArrayBufferFunction", GetArrayBufferFunction().GetTaggedValue()); - vec.emplace_back("SharedArrayBufferFunction", GetSharedArrayBufferFunction().GetTaggedValue()); - vec.emplace_back("SymbolFunction", GetSymbolFunction().GetTaggedValue()); - vec.emplace_back("RangeErrorFunction", GetRangeErrorFunction().GetTaggedValue()); - vec.emplace_back("ReferenceErrorFunction", GetReferenceErrorFunction().GetTaggedValue()); - vec.emplace_back("TypeErrorFunction", GetTypeErrorFunction().GetTaggedValue()); - vec.emplace_back("AggregateErrorFunction", GetAggregateErrorFunction().GetTaggedValue()); - vec.emplace_back("URIErrorFunction", GetURIErrorFunction().GetTaggedValue()); - vec.emplace_back("SyntaxErrorFunction", GetSyntaxErrorFunction().GetTaggedValue()); - vec.emplace_back("EvalErrorFunction", GetEvalErrorFunction().GetTaggedValue()); - vec.emplace_back("OOMErrorFunction", GetOOMErrorFunction().GetTaggedValue()); - vec.emplace_back("RegExpFunction", GetRegExpFunction().GetTaggedValue()); - vec.emplace_back("BuiltinsSetFunction", GetBuiltinsSetFunction().GetTaggedValue()); - vec.emplace_back("BuiltinsMapFunction", GetBuiltinsMapFunction().GetTaggedValue()); - vec.emplace_back("BuiltinsWeakSetFunction", GetBuiltinsWeakSetFunction().GetTaggedValue()); - vec.emplace_back("BuiltinsWeakMapFunction", GetBuiltinsWeakMapFunction().GetTaggedValue()); - vec.emplace_back("BuiltinsWeakRefFunction", GetBuiltinsWeakRefFunction().GetTaggedValue()); - vec.emplace_back("BuiltinsFinalizationRegistryFunction", + vec.emplace_back(CString("ObjectFunction"), GetObjectFunction().GetTaggedValue()); + vec.emplace_back(CString("FunctionFunction"), GetFunctionFunction().GetTaggedValue()); + vec.emplace_back(CString("NumberFunction"), GetNumberFunction().GetTaggedValue()); + vec.emplace_back(CString("BigIntFunction"), GetBigIntFunction().GetTaggedValue()); + vec.emplace_back(CString("DateFunction"), GetDateFunction().GetTaggedValue()); + vec.emplace_back(CString("BooleanFunction"), GetBooleanFunction().GetTaggedValue()); + vec.emplace_back(CString("ErrorFunction"), GetErrorFunction().GetTaggedValue()); + vec.emplace_back(CString("ArrayFunction"), GetArrayFunction().GetTaggedValue()); + vec.emplace_back(CString("TypedArrayFunction"), GetTypedArrayFunction().GetTaggedValue()); + vec.emplace_back(CString("Int8ArrayFunction"), GetInt8ArrayFunction().GetTaggedValue()); + vec.emplace_back(CString("Uint8ArrayFunction"), GetUint8ArrayFunction().GetTaggedValue()); + vec.emplace_back(CString("Uint8ClampedArrayFunction"), GetUint8ClampedArrayFunction().GetTaggedValue()); + vec.emplace_back(CString("Int16ArrayFunction"), GetInt16ArrayFunction().GetTaggedValue()); + vec.emplace_back(CString("Uint16ArrayFunction"), GetUint16ArrayFunction().GetTaggedValue()); + vec.emplace_back(CString("Int32ArrayFunction"), GetInt32ArrayFunction().GetTaggedValue()); + vec.emplace_back(CString("Uint32ArrayFunction"), GetUint32ArrayFunction().GetTaggedValue()); + vec.emplace_back(CString("Float32ArrayFunction"), GetFloat32ArrayFunction().GetTaggedValue()); + vec.emplace_back(CString("Float64ArrayFunction"), GetFloat64ArrayFunction().GetTaggedValue()); + vec.emplace_back(CString("ArrayBufferFunction"), GetArrayBufferFunction().GetTaggedValue()); + vec.emplace_back(CString("SharedArrayBufferFunction"), GetSharedArrayBufferFunction().GetTaggedValue()); + vec.emplace_back(CString("SymbolFunction"), GetSymbolFunction().GetTaggedValue()); + vec.emplace_back(CString("RangeErrorFunction"), GetRangeErrorFunction().GetTaggedValue()); + vec.emplace_back(CString("ReferenceErrorFunction"), GetReferenceErrorFunction().GetTaggedValue()); + vec.emplace_back(CString("TypeErrorFunction"), GetTypeErrorFunction().GetTaggedValue()); + vec.emplace_back(CString("AggregateErrorFunction"), GetAggregateErrorFunction().GetTaggedValue()); + vec.emplace_back(CString("URIErrorFunction"), GetURIErrorFunction().GetTaggedValue()); + vec.emplace_back(CString("SyntaxErrorFunction"), GetSyntaxErrorFunction().GetTaggedValue()); + vec.emplace_back(CString("EvalErrorFunction"), GetEvalErrorFunction().GetTaggedValue()); + vec.emplace_back(CString("OOMErrorFunction"), GetOOMErrorFunction().GetTaggedValue()); + vec.emplace_back(CString("RegExpFunction"), GetRegExpFunction().GetTaggedValue()); + vec.emplace_back(CString("BuiltinsSetFunction"), GetBuiltinsSetFunction().GetTaggedValue()); + vec.emplace_back(CString("BuiltinsMapFunction"), GetBuiltinsMapFunction().GetTaggedValue()); + vec.emplace_back(CString("BuiltinsWeakSetFunction"), GetBuiltinsWeakSetFunction().GetTaggedValue()); + vec.emplace_back(CString("BuiltinsWeakMapFunction"), GetBuiltinsWeakMapFunction().GetTaggedValue()); + vec.emplace_back(CString("BuiltinsWeakRefFunction"), GetBuiltinsWeakRefFunction().GetTaggedValue()); + vec.emplace_back(CString("BuiltinsFinalizationRegistryFunction"), GetBuiltinsFinalizationRegistryFunction().GetTaggedValue()); - vec.emplace_back("MathFunction", GetMathFunction().GetTaggedValue()); - vec.emplace_back("AtomicsFunction", GetAtomicsFunction().GetTaggedValue()); - vec.emplace_back("JsonFunction", GetJsonFunction().GetTaggedValue()); - vec.emplace_back("StringFunction", GetStringFunction().GetTaggedValue()); - vec.emplace_back("ProxyFunction", GetProxyFunction().GetTaggedValue()); - vec.emplace_back("ReflectFunction", GetReflectFunction().GetTaggedValue()); - vec.emplace_back("AsyncFunction", GetAsyncFunction().GetTaggedValue()); - vec.emplace_back("AsyncFunctionPrototype", GetAsyncFunctionPrototype().GetTaggedValue()); - vec.emplace_back("JSGlobalObject", GetJSGlobalObject().GetTaggedValue()); - vec.emplace_back("EmptyArray", globalConst->GetEmptyArray()); - vec.emplace_back("EmptyString", globalConst->GetEmptyString()); - vec.emplace_back("EmptyTaggedQueue", globalConst->GetEmptyTaggedQueue()); - vec.emplace_back("PrototypeString", globalConst->GetPrototypeString()); - vec.emplace_back("HasInstanceSymbol", GetHasInstanceSymbol().GetTaggedValue()); - vec.emplace_back("IsConcatSpreadableSymbol", GetIsConcatSpreadableSymbol().GetTaggedValue()); - vec.emplace_back("ToStringTagSymbol", GetToStringTagSymbol().GetTaggedValue()); - vec.emplace_back("IteratorSymbol", GetIteratorSymbol().GetTaggedValue()); - vec.emplace_back("AsyncIteratorSymbol", GetAsyncIteratorSymbol().GetTaggedValue()); - vec.emplace_back("MatchSymbol", GetMatchSymbol().GetTaggedValue()); - vec.emplace_back("MatchAllSymbol", GetMatchAllSymbol().GetTaggedValue()); - vec.emplace_back("ReplaceSymbol", GetReplaceSymbol().GetTaggedValue()); - vec.emplace_back("SearchSymbol", GetSearchSymbol().GetTaggedValue()); - vec.emplace_back("SpeciesSymbol", GetSpeciesSymbol().GetTaggedValue()); - vec.emplace_back("SplitSymbol", GetSplitSymbol().GetTaggedValue()); - vec.emplace_back("ToPrimitiveSymbol", GetToPrimitiveSymbol().GetTaggedValue()); - vec.emplace_back("UnscopablesSymbol", GetUnscopablesSymbol().GetTaggedValue()); - vec.emplace_back("HoleySymbol", GetHoleySymbol().GetTaggedValue()); - vec.emplace_back("AttachSymbol", GetAttachSymbol().GetTaggedValue()); - vec.emplace_back("DetachSymbol", GetDetachSymbol().GetTaggedValue()); - vec.emplace_back("ConstructorString", globalConst->GetConstructorString()); - vec.emplace_back("IteratorPrototype", GetIteratorPrototype().GetTaggedValue()); - vec.emplace_back("ForinIteratorPrototype", GetForinIteratorPrototype().GetTaggedValue()); - vec.emplace_back("StringIterator", GetStringIterator().GetTaggedValue()); - vec.emplace_back("MapIteratorPrototype", GetMapIteratorPrototype().GetTaggedValue()); - vec.emplace_back("SetIteratorPrototype", GetSetIteratorPrototype().GetTaggedValue()); - vec.emplace_back("RegExpIteratorPrototype", GetRegExpIteratorPrototype().GetTaggedValue()); - vec.emplace_back("ArrayIteratorPrototype", GetArrayIteratorPrototype().GetTaggedValue()); - vec.emplace_back("StringIteratorPrototype", GetStringIteratorPrototype().GetTaggedValue()); - vec.emplace_back("LengthString", globalConst->GetLengthString()); - vec.emplace_back("ValueString", globalConst->GetValueString()); - vec.emplace_back("WritableString", globalConst->GetWritableString()); - vec.emplace_back("GetString", globalConst->GetGetString()); - vec.emplace_back("SetString", globalConst->GetSetString()); - vec.emplace_back("EnumerableString", globalConst->GetEnumerableString()); - vec.emplace_back("ConfigurableString", globalConst->GetConfigurableString()); - vec.emplace_back("NameString", globalConst->GetNameString()); - vec.emplace_back("ValueOfString", globalConst->GetValueOfString()); - vec.emplace_back("ToStringString", globalConst->GetToStringString()); - vec.emplace_back("ToLocaleStringString", globalConst->GetToLocaleStringString()); - vec.emplace_back("UndefinedString", globalConst->GetUndefinedString()); - vec.emplace_back("NullString", globalConst->GetNullString()); - vec.emplace_back("TrueString", globalConst->GetTrueString()); - vec.emplace_back("FalseString", globalConst->GetFalseString()); - vec.emplace_back("RegisterSymbols", GetRegisterSymbols().GetTaggedValue()); - vec.emplace_back("ThrowTypeError", GetThrowTypeError().GetTaggedValue()); - vec.emplace_back("GetPrototypeOfString", globalConst->GetGetPrototypeOfString()); - vec.emplace_back("SetPrototypeOfString", globalConst->GetSetPrototypeOfString()); - vec.emplace_back("IsExtensibleString", globalConst->GetIsExtensibleString()); - vec.emplace_back("PreventExtensionsString", globalConst->GetPreventExtensionsString()); - vec.emplace_back("GetOwnPropertyDescriptorString", globalConst->GetGetOwnPropertyDescriptorString()); - vec.emplace_back("DefinePropertyString", globalConst->GetDefinePropertyString()); - vec.emplace_back("HasString", globalConst->GetHasString()); - vec.emplace_back("DeletePropertyString", globalConst->GetDeletePropertyString()); - vec.emplace_back("EnumerateString", globalConst->GetEnumerateString()); - vec.emplace_back("OwnKeysString", globalConst->GetOwnKeysString()); - vec.emplace_back("ApplyString", globalConst->GetApplyString()); - vec.emplace_back("ProxyString", globalConst->GetProxyString()); - vec.emplace_back("RevokeString", globalConst->GetRevokeString()); - vec.emplace_back("ProxyConstructString", globalConst->GetProxyConstructString()); - vec.emplace_back("ProxyCallString", globalConst->GetProxyCallString()); - vec.emplace_back("DoneString", globalConst->GetDoneString()); - vec.emplace_back("NegativeZeroString", globalConst->GetNegativeZeroString()); - vec.emplace_back("NextString", globalConst->GetNextString()); - vec.emplace_back("PromiseThenString", globalConst->GetPromiseThenString()); - vec.emplace_back("PromiseFunction", GetPromiseFunction().GetTaggedValue()); - vec.emplace_back("PromiseReactionJob", GetPromiseReactionJob().GetTaggedValue()); - vec.emplace_back("PromiseResolveThenableJob", GetPromiseResolveThenableJob().GetTaggedValue()); - vec.emplace_back("DynamicImportJob", GetDynamicImportJob().GetTaggedValue()); - vec.emplace_back("ScriptJobString", globalConst->GetScriptJobString()); - vec.emplace_back("PromiseString", globalConst->GetPromiseString()); - vec.emplace_back("IdentityString", globalConst->GetIdentityString()); - vec.emplace_back("AsyncFunctionString", globalConst->GetAsyncFunctionString()); - vec.emplace_back("ThrowerString", globalConst->GetThrowerString()); - vec.emplace_back("Undefined", globalConst->GetUndefined()); - vec.emplace_back("ArrayListFunction", globalConst->GetArrayListFunction()); - vec.emplace_back("ArrayListIteratorPrototype", globalConst->GetArrayListIteratorPrototype()); - vec.emplace_back("HashMapIteratorPrototype", globalConst->GetHashMapIteratorPrototype()); - vec.emplace_back("HashSetIteratorPrototype", globalConst->GetHashSetIteratorPrototype()); - vec.emplace_back("LightWeightMapIteratorPrototype", globalConst->GetLightWeightMapIteratorPrototype()); - vec.emplace_back("LightWeightSetIteratorPrototype", globalConst->GetLightWeightSetIteratorPrototype()); - vec.emplace_back("TreeMapIteratorPrototype", globalConst->GetTreeMapIteratorPrototype()); - vec.emplace_back("TreeSetIteratorPrototype", globalConst->GetTreeSetIteratorPrototype()); - vec.emplace_back("VectorFunction", globalConst->GetVectorFunction()); - vec.emplace_back("VectorIteratorPrototype", globalConst->GetVectorIteratorPrototype()); - vec.emplace_back("QueueIteratorPrototype", globalConst->GetQueueIteratorPrototype()); - vec.emplace_back("PlainArrayIteratorPrototype", globalConst->GetPlainArrayIteratorPrototype()); - vec.emplace_back("DequeIteratorPrototype", globalConst->GetDequeIteratorPrototype()); - vec.emplace_back("StackIteratorPrototype", globalConst->GetStackIteratorPrototype()); - vec.emplace_back("LinkedListIteratorPrototype", globalConst->GetLinkedListIteratorPrototype()); - vec.emplace_back("ListIteratorPrototype", globalConst->GetListIteratorPrototype()); - vec.emplace_back("GlobalPatch", GetGlobalPatch().GetTaggedValue()); -} - -void JSDataView::DumpForSnapshot(std::vector> &vec) const -{ - vec.emplace_back("data-view", GetDataView()); - vec.emplace_back("buffer", GetViewedArrayBuffer()); - vec.emplace_back("byte-length", JSTaggedValue(GetByteLength())); - vec.emplace_back("byte-offset", JSTaggedValue(GetByteOffset())); -} - -void JSArrayBuffer::DumpForSnapshot(std::vector> &vec) const -{ - vec.emplace_back("buffer-data", GetArrayBufferData()); - vec.emplace_back("byte-length", JSTaggedValue(GetArrayBufferByteLength())); - vec.emplace_back("shared", JSTaggedValue(GetShared())); -} - -void PromiseReaction::DumpForSnapshot(std::vector> &vec) const -{ - vec.emplace_back("promise-capability", GetPromiseCapability()); - vec.emplace_back("handler", GetHandler()); - vec.emplace_back("type", JSTaggedValue(static_cast(GetType()))); -} - -void PromiseCapability::DumpForSnapshot(std::vector> &vec) const -{ - vec.emplace_back("promise", GetPromise()); - vec.emplace_back("resolve", GetResolve()); - vec.emplace_back("reject", GetReject()); -} - -void PromiseIteratorRecord::DumpForSnapshot(std::vector> &vec) const + vec.emplace_back(CString("MathFunction"), GetMathFunction().GetTaggedValue()); + vec.emplace_back(CString("AtomicsFunction"), GetAtomicsFunction().GetTaggedValue()); + vec.emplace_back(CString("JsonFunction"), GetJsonFunction().GetTaggedValue()); + vec.emplace_back(CString("StringFunction"), GetStringFunction().GetTaggedValue()); + vec.emplace_back(CString("ProxyFunction"), GetProxyFunction().GetTaggedValue()); + vec.emplace_back(CString("ReflectFunction"), GetReflectFunction().GetTaggedValue()); + vec.emplace_back(CString("AsyncFunction"), GetAsyncFunction().GetTaggedValue()); + vec.emplace_back(CString("AsyncFunctionPrototype"), GetAsyncFunctionPrototype().GetTaggedValue()); + vec.emplace_back(CString("JSGlobalObject"), GetJSGlobalObject().GetTaggedValue()); + vec.emplace_back(CString("EmptyArray"), globalConst->GetEmptyArray()); + vec.emplace_back(CString("EmptyString"), globalConst->GetEmptyString()); + vec.emplace_back(CString("EmptyTaggedQueue"), globalConst->GetEmptyTaggedQueue()); + vec.emplace_back(CString("PrototypeString"), globalConst->GetPrototypeString()); + vec.emplace_back(CString("HasInstanceSymbol"), GetHasInstanceSymbol().GetTaggedValue()); + vec.emplace_back(CString("IsConcatSpreadableSymbol"), GetIsConcatSpreadableSymbol().GetTaggedValue()); + vec.emplace_back(CString("ToStringTagSymbol"), GetToStringTagSymbol().GetTaggedValue()); + vec.emplace_back(CString("IteratorSymbol"), GetIteratorSymbol().GetTaggedValue()); + vec.emplace_back(CString("AsyncIteratorSymbol"), GetAsyncIteratorSymbol().GetTaggedValue()); + vec.emplace_back(CString("MatchSymbol"), GetMatchSymbol().GetTaggedValue()); + vec.emplace_back(CString("MatchAllSymbol"), GetMatchAllSymbol().GetTaggedValue()); + vec.emplace_back(CString("ReplaceSymbol"), GetReplaceSymbol().GetTaggedValue()); + vec.emplace_back(CString("SearchSymbol"), GetSearchSymbol().GetTaggedValue()); + vec.emplace_back(CString("SpeciesSymbol"), GetSpeciesSymbol().GetTaggedValue()); + vec.emplace_back(CString("SplitSymbol"), GetSplitSymbol().GetTaggedValue()); + vec.emplace_back(CString("ToPrimitiveSymbol"), GetToPrimitiveSymbol().GetTaggedValue()); + vec.emplace_back(CString("UnscopablesSymbol"), GetUnscopablesSymbol().GetTaggedValue()); + vec.emplace_back(CString("HoleySymbol"), GetHoleySymbol().GetTaggedValue()); + vec.emplace_back(CString("AttachSymbol"), GetAttachSymbol().GetTaggedValue()); + vec.emplace_back(CString("DetachSymbol"), GetDetachSymbol().GetTaggedValue()); + vec.emplace_back(CString("ConstructorString"), globalConst->GetConstructorString()); + vec.emplace_back(CString("IteratorPrototype"), GetIteratorPrototype().GetTaggedValue()); + vec.emplace_back(CString("ForinIteratorPrototype"), GetForinIteratorPrototype().GetTaggedValue()); + vec.emplace_back(CString("StringIterator"), GetStringIterator().GetTaggedValue()); + vec.emplace_back(CString("MapIteratorPrototype"), GetMapIteratorPrototype().GetTaggedValue()); + vec.emplace_back(CString("SetIteratorPrototype"), GetSetIteratorPrototype().GetTaggedValue()); + vec.emplace_back(CString("RegExpIteratorPrototype"), GetRegExpIteratorPrototype().GetTaggedValue()); + vec.emplace_back(CString("ArrayIteratorPrototype"), GetArrayIteratorPrototype().GetTaggedValue()); + vec.emplace_back(CString("StringIteratorPrototype"), GetStringIteratorPrototype().GetTaggedValue()); + vec.emplace_back(CString("LengthString"), globalConst->GetLengthString()); + vec.emplace_back(CString("ValueString"), globalConst->GetValueString()); + vec.emplace_back(CString("WritableString"), globalConst->GetWritableString()); + vec.emplace_back(CString("GetString"), globalConst->GetGetString()); + vec.emplace_back(CString("SetString"), globalConst->GetSetString()); + vec.emplace_back(CString("EnumerableString"), globalConst->GetEnumerableString()); + vec.emplace_back(CString("ConfigurableString"), globalConst->GetConfigurableString()); + vec.emplace_back(CString("NameString"), globalConst->GetNameString()); + vec.emplace_back(CString("ValueOfString"), globalConst->GetValueOfString()); + vec.emplace_back(CString("ToStringString"), globalConst->GetToStringString()); + vec.emplace_back(CString("ToLocaleStringString"), globalConst->GetToLocaleStringString()); + vec.emplace_back(CString("UndefinedString"), globalConst->GetUndefinedString()); + vec.emplace_back(CString("NullString"), globalConst->GetNullString()); + vec.emplace_back(CString("TrueString"), globalConst->GetTrueString()); + vec.emplace_back(CString("FalseString"), globalConst->GetFalseString()); + vec.emplace_back(CString("RegisterSymbols"), GetRegisterSymbols().GetTaggedValue()); + vec.emplace_back(CString("ThrowTypeError"), GetThrowTypeError().GetTaggedValue()); + vec.emplace_back(CString("GetPrototypeOfString"), globalConst->GetGetPrototypeOfString()); + vec.emplace_back(CString("SetPrototypeOfString"), globalConst->GetSetPrototypeOfString()); + vec.emplace_back(CString("IsExtensibleString"), globalConst->GetIsExtensibleString()); + vec.emplace_back(CString("PreventExtensionsString"), globalConst->GetPreventExtensionsString()); + vec.emplace_back(CString("GetOwnPropertyDescriptorString"), globalConst->GetGetOwnPropertyDescriptorString()); + vec.emplace_back(CString("DefinePropertyString"), globalConst->GetDefinePropertyString()); + vec.emplace_back(CString("HasString"), globalConst->GetHasString()); + vec.emplace_back(CString("DeletePropertyString"), globalConst->GetDeletePropertyString()); + vec.emplace_back(CString("EnumerateString"), globalConst->GetEnumerateString()); + vec.emplace_back(CString("OwnKeysString"), globalConst->GetOwnKeysString()); + vec.emplace_back(CString("ApplyString"), globalConst->GetApplyString()); + vec.emplace_back(CString("ProxyString"), globalConst->GetProxyString()); + vec.emplace_back(CString("RevokeString"), globalConst->GetRevokeString()); + vec.emplace_back(CString("ProxyConstructString"), globalConst->GetProxyConstructString()); + vec.emplace_back(CString("ProxyCallString"), globalConst->GetProxyCallString()); + vec.emplace_back(CString("DoneString"), globalConst->GetDoneString()); + vec.emplace_back(CString("NegativeZeroString"), globalConst->GetNegativeZeroString()); + vec.emplace_back(CString("NextString"), globalConst->GetNextString()); + vec.emplace_back(CString("PromiseThenString"), globalConst->GetPromiseThenString()); + vec.emplace_back(CString("PromiseFunction"), GetPromiseFunction().GetTaggedValue()); + vec.emplace_back(CString("PromiseReactionJob"), GetPromiseReactionJob().GetTaggedValue()); + vec.emplace_back(CString("PromiseResolveThenableJob"), GetPromiseResolveThenableJob().GetTaggedValue()); + vec.emplace_back(CString("DynamicImportJob"), GetDynamicImportJob().GetTaggedValue()); + vec.emplace_back(CString("ScriptJobString"), globalConst->GetScriptJobString()); + vec.emplace_back(CString("PromiseString"), globalConst->GetPromiseString()); + vec.emplace_back(CString("IdentityString"), globalConst->GetIdentityString()); + vec.emplace_back(CString("AsyncFunctionString"), globalConst->GetAsyncFunctionString()); + vec.emplace_back(CString("ThrowerString"), globalConst->GetThrowerString()); + vec.emplace_back(CString("Undefined"), globalConst->GetUndefined()); + vec.emplace_back(CString("ArrayListFunction"), globalConst->GetArrayListFunction()); + vec.emplace_back(CString("ArrayListIteratorPrototype"), globalConst->GetArrayListIteratorPrototype()); + vec.emplace_back(CString("HashMapIteratorPrototype"), globalConst->GetHashMapIteratorPrototype()); + vec.emplace_back(CString("HashSetIteratorPrototype"), globalConst->GetHashSetIteratorPrototype()); + vec.emplace_back(CString("LightWeightMapIteratorPrototype"), globalConst->GetLightWeightMapIteratorPrototype()); + vec.emplace_back(CString("LightWeightSetIteratorPrototype"), globalConst->GetLightWeightSetIteratorPrototype()); + vec.emplace_back(CString("TreeMapIteratorPrototype"), globalConst->GetTreeMapIteratorPrototype()); + vec.emplace_back(CString("TreeSetIteratorPrototype"), globalConst->GetTreeSetIteratorPrototype()); + vec.emplace_back(CString("VectorFunction"), globalConst->GetVectorFunction()); + vec.emplace_back(CString("VectorIteratorPrototype"), globalConst->GetVectorIteratorPrototype()); + vec.emplace_back(CString("QueueIteratorPrototype"), globalConst->GetQueueIteratorPrototype()); + vec.emplace_back(CString("PlainArrayIteratorPrototype"), globalConst->GetPlainArrayIteratorPrototype()); + vec.emplace_back(CString("DequeIteratorPrototype"), globalConst->GetDequeIteratorPrototype()); + vec.emplace_back(CString("StackIteratorPrototype"), globalConst->GetStackIteratorPrototype()); + vec.emplace_back(CString("LinkedListIteratorPrototype"), globalConst->GetLinkedListIteratorPrototype()); + vec.emplace_back(CString("ListIteratorPrototype"), globalConst->GetListIteratorPrototype()); + vec.emplace_back(CString("GlobalPatch"), GetGlobalPatch().GetTaggedValue()); +} + +void JSDataView::DumpForSnapshot(std::vector &vec) const +{ + vec.emplace_back(CString("data-view"), GetDataView()); + vec.emplace_back(CString("buffer"), GetViewedArrayBuffer()); + vec.emplace_back(CString("byte-length"), JSTaggedValue(GetByteLength())); + vec.emplace_back(CString("byte-offset"), JSTaggedValue(GetByteOffset())); +} + +void JSArrayBuffer::DumpForSnapshot(std::vector &vec) const +{ + vec.emplace_back(CString("buffer-data"), GetArrayBufferData()); + vec.emplace_back(CString("byte-length"), JSTaggedValue(GetArrayBufferByteLength())); + vec.emplace_back(CString("shared"), JSTaggedValue(GetShared())); +} + +void PromiseReaction::DumpForSnapshot(std::vector &vec) const +{ + vec.emplace_back(CString("promise-capability"), GetPromiseCapability()); + vec.emplace_back(CString("handler"), GetHandler()); + vec.emplace_back(CString("type"), JSTaggedValue(static_cast(GetType()))); +} + +void PromiseCapability::DumpForSnapshot(std::vector &vec) const +{ + vec.emplace_back(CString("promise"), GetPromise()); + vec.emplace_back(CString("resolve"), GetResolve()); + vec.emplace_back(CString("reject"), GetReject()); +} + +void PromiseIteratorRecord::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("iterator", GetIterator()); - vec.emplace_back("done", JSTaggedValue(GetDone())); + vec.emplace_back(CString("iterator"), GetIterator()); + vec.emplace_back(CString("done"), JSTaggedValue(GetDone())); } -void PromiseRecord::DumpForSnapshot(std::vector> &vec) const +void PromiseRecord::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("value", GetValue()); + vec.emplace_back(CString("value"), GetValue()); } -void ResolvingFunctionsRecord::DumpForSnapshot(std::vector> &vec) const +void ResolvingFunctionsRecord::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("resolve-function", GetResolveFunction()); - vec.emplace_back("reject-function", GetRejectFunction()); + vec.emplace_back(CString("resolve-function"), GetResolveFunction()); + vec.emplace_back(CString("reject-function"), GetRejectFunction()); } -void AsyncGeneratorRequest::DumpForSnapshot(std::vector> &vec) const +void AsyncGeneratorRequest::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("completion", GetCompletion()); - vec.emplace_back("capability", GetCapability()); + vec.emplace_back(CString("completion"), GetCompletion()); + vec.emplace_back(CString("capability"), GetCapability()); } -void AsyncIteratorRecord::DumpForSnapshot(std::vector> &vec) const +void AsyncIteratorRecord::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("iterator", GetIterator()); - vec.emplace_back("nextmethod", GetNextMethod()); - vec.emplace_back("done", JSTaggedValue(GetDone())); + vec.emplace_back(CString("iterator"), GetIterator()); + vec.emplace_back(CString("nextmethod"), GetNextMethod()); + vec.emplace_back(CString("done"), JSTaggedValue(GetDone())); } -void JSAsyncFromSyncIterator::DumpForSnapshot(std::vector> &vec) const +void JSAsyncFromSyncIterator::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("synciteratorrecord", GetSyncIteratorRecord()); + vec.emplace_back(CString("synciteratorrecord"), GetSyncIteratorRecord()); } -void JSAsyncFromSyncIterUnwarpFunction::DumpForSnapshot(std::vector> &vec) const +void JSAsyncFromSyncIterUnwarpFunction::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("done", JSTaggedValue(GetDone())); + vec.emplace_back(CString("done"), JSTaggedValue(GetDone())); } -void JSPromise::DumpForSnapshot(std::vector> &vec) const +void JSPromise::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("promise-state", JSTaggedValue(static_cast(GetPromiseState()))); - vec.emplace_back("promise-result", GetPromiseResult()); - vec.emplace_back("promise-fulfill-reactions", GetPromiseFulfillReactions()); - vec.emplace_back("promise-reject-reactions", GetPromiseRejectReactions()); - vec.emplace_back("promise-is-handled", JSTaggedValue(GetPromiseIsHandled())); + vec.emplace_back(CString("promise-state"), JSTaggedValue(static_cast(GetPromiseState()))); + vec.emplace_back(CString("promise-result"), GetPromiseResult()); + vec.emplace_back(CString("promise-fulfill-reactions"), GetPromiseFulfillReactions()); + vec.emplace_back(CString("promise-reject-reactions"), GetPromiseRejectReactions()); + vec.emplace_back(CString("promise-is-handled"), JSTaggedValue(GetPromiseIsHandled())); JSObject::DumpForSnapshot(vec); } -void JSPromiseReactionsFunction::DumpForSnapshot(std::vector> &vec) const +void JSPromiseReactionsFunction::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("promise", GetPromise()); - vec.emplace_back("already-resolved", GetAlreadyResolved()); + vec.emplace_back(CString("promise"), GetPromise()); + vec.emplace_back(CString("already-resolved"), GetAlreadyResolved()); JSObject::DumpForSnapshot(vec); } -void JSAsyncGeneratorResNextRetProRstFtn::DumpForSnapshot(std::vector> &vec) const +void JSAsyncGeneratorResNextRetProRstFtn::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("async-generator-object", GetAsyncGeneratorObject()); + vec.emplace_back(CString("async-generator-object"), GetAsyncGeneratorObject()); JSObject::DumpForSnapshot(vec); } -void JSPromiseExecutorFunction::DumpForSnapshot(std::vector> &vec) const +void JSPromiseExecutorFunction::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("capability", GetCapability()); + vec.emplace_back(CString("capability"), GetCapability()); JSObject::DumpForSnapshot(vec); } -void JSPromiseAllResolveElementFunction::DumpForSnapshot(std::vector> &vec) const +void JSPromiseAllResolveElementFunction::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("index", GetIndex()); - vec.emplace_back("values", GetValues()); - vec.emplace_back("capabilities", GetCapabilities()); - vec.emplace_back("remaining-elements", GetRemainingElements()); - vec.emplace_back("already-called", GetAlreadyCalled()); + vec.emplace_back(CString("index"), GetIndex()); + vec.emplace_back(CString("values"), GetValues()); + vec.emplace_back(CString("capabilities"), GetCapabilities()); + vec.emplace_back(CString("remaining-elements"), GetRemainingElements()); + vec.emplace_back(CString("already-called"), GetAlreadyCalled()); JSObject::DumpForSnapshot(vec); } -void JSPromiseAnyRejectElementFunction::DumpForSnapshot(std::vector> &vec) const +void JSPromiseAnyRejectElementFunction::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("index", JSTaggedValue(GetIndex())); - vec.emplace_back("errors", GetErrors()); - vec.emplace_back("capability", GetCapability()); - vec.emplace_back("remaining-elements", GetRemainingElements()); - vec.emplace_back("already-called", GetAlreadyCalled()); + vec.emplace_back(CString("index"), JSTaggedValue(GetIndex())); + vec.emplace_back(CString("errors"), GetErrors()); + vec.emplace_back(CString("capability"), GetCapability()); + vec.emplace_back(CString("remaining-elements"), GetRemainingElements()); + vec.emplace_back(CString("already-called"), GetAlreadyCalled()); JSObject::DumpForSnapshot(vec); } -void JSPromiseAllSettledElementFunction::DumpForSnapshot(std::vector> &vec) const +void JSPromiseAllSettledElementFunction::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("already-called", GetAlreadyCalled()); - vec.emplace_back("index", JSTaggedValue(GetIndex())); - vec.emplace_back("values", GetValues()); - vec.emplace_back("capability", GetCapability()); - vec.emplace_back("remaining-elements", GetRemainingElements()); + vec.emplace_back(CString("already-called"), GetAlreadyCalled()); + vec.emplace_back(CString("index"), JSTaggedValue(GetIndex())); + vec.emplace_back(CString("values"), GetValues()); + vec.emplace_back(CString("capability"), GetCapability()); + vec.emplace_back(CString("remaining-elements"), GetRemainingElements()); JSObject::DumpForSnapshot(vec); } -void JSPromiseFinallyFunction::DumpForSnapshot(std::vector> &vec) const +void JSPromiseFinallyFunction::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("constructor", GetConstructor()); - vec.emplace_back("onFinally", GetOnFinally()); + vec.emplace_back(CString("constructor"), GetConstructor()); + vec.emplace_back(CString("onFinally"), GetOnFinally()); JSObject::DumpForSnapshot(vec); } -void JSPromiseValueThunkOrThrowerFunction::DumpForSnapshot(std::vector> &vec) const +void JSPromiseValueThunkOrThrowerFunction::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("result", GetResult()); + vec.emplace_back(CString("result"), GetResult()); JSObject::DumpForSnapshot(vec); } -void MicroJobQueue::DumpForSnapshot(std::vector> &vec) const +void MicroJobQueue::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("promise-job-queue", GetPromiseJobQueue()); - vec.emplace_back("script-job-queue", GetScriptJobQueue()); + vec.emplace_back(CString("promise-job-queue"), GetPromiseJobQueue()); + vec.emplace_back(CString("script-job-queue"), GetScriptJobQueue()); } -void PendingJob::DumpForSnapshot(std::vector> &vec) const +void PendingJob::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("job", GetJob()); - vec.emplace_back("arguments", GetArguments()); + vec.emplace_back(CString("job"), GetJob()); + vec.emplace_back(CString("arguments"), GetArguments()); } -void CompletionRecord::DumpForSnapshot(std::vector> &vec) const +void CompletionRecord::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("value", GetValue()); - vec.emplace_back("type", JSTaggedValue(static_cast(GetType()))); + vec.emplace_back(CString("value"), GetValue()); + vec.emplace_back(CString("type"), JSTaggedValue(static_cast(GetType()))); } -void JSProxyRevocFunction::DumpForSnapshot(std::vector> &vec) const +void JSProxyRevocFunction::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("RevocableProxy", GetRevocableProxy()); + vec.emplace_back(CString("RevocableProxy"), GetRevocableProxy()); } -void JSAsyncFunction::DumpForSnapshot(std::vector> &vec) const +void JSAsyncFunction::DumpForSnapshot(std::vector &vec) const { JSFunction::DumpForSnapshot(vec); } -void JSAsyncAwaitStatusFunction::DumpForSnapshot(std::vector> &vec) const +void JSAsyncAwaitStatusFunction::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("AsyncContext", GetAsyncContext()); + vec.emplace_back(CString("AsyncContext"), GetAsyncContext()); } -void JSGeneratorFunction::DumpForSnapshot(std::vector> &vec) const +void JSGeneratorFunction::DumpForSnapshot(std::vector &vec) const { JSFunction::DumpForSnapshot(vec); } -void JSAsyncGeneratorFunction::DumpForSnapshot(std::vector> &vec) const +void JSAsyncGeneratorFunction::DumpForSnapshot(std::vector &vec) const { JSFunction::DumpForSnapshot(vec); } -void JSIntlBoundFunction::DumpForSnapshot(std::vector> &vec) const +void JSIntlBoundFunction::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("NumberFormat", GetNumberFormat()); - vec.emplace_back("DateTimeFormat", GetDateTimeFormat()); - vec.emplace_back("Collator", GetCollator()); + vec.emplace_back(CString("NumberFormat"), GetNumberFormat()); + vec.emplace_back(CString("DateTimeFormat"), GetDateTimeFormat()); + vec.emplace_back(CString("Collator"), GetCollator()); JSObject::DumpForSnapshot(vec); } -void PropertyBox::DumpForSnapshot(std::vector> &vec) const +void PropertyBox::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("Value", GetValue()); + vec.emplace_back(CString("Value"), GetValue()); } -void PrototypeHandler::DumpForSnapshot(std::vector> &vec) const +void PrototypeHandler::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("HandlerInfo", GetHandlerInfo()); - vec.emplace_back("ProtoCell", GetProtoCell()); - vec.emplace_back("Holder", GetHolder()); + vec.emplace_back(CString("HandlerInfo"), GetHandlerInfo()); + vec.emplace_back(CString("ProtoCell"), GetProtoCell()); + vec.emplace_back(CString("Holder"), GetHolder()); } -void TransitionHandler::DumpForSnapshot(std::vector> &vec) const +void TransitionHandler::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("HandlerInfo", GetHandlerInfo()); - vec.emplace_back("TransitionHClass", GetTransitionHClass()); + vec.emplace_back(CString("HandlerInfo"), GetHandlerInfo()); + vec.emplace_back(CString("TransitionHClass"), GetTransitionHClass()); } -void TransWithProtoHandler::DumpForSnapshot(std::vector> &vec) const +void TransWithProtoHandler::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("HandlerInfo", GetHandlerInfo()); - vec.emplace_back("TransitionHClass", GetTransitionHClass()); - vec.emplace_back("ProtoCell", GetProtoCell()); + vec.emplace_back(CString("HandlerInfo"), GetHandlerInfo()); + vec.emplace_back(CString("TransitionHClass"), GetTransitionHClass()); + vec.emplace_back(CString("ProtoCell"), GetProtoCell()); } -void StoreTSHandler::DumpForSnapshot(std::vector> &vec) const +void StoreTSHandler::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("HandlerInfo", GetHandlerInfo()); - vec.emplace_back("ProtoCell", GetProtoCell()); - vec.emplace_back("Holder", GetHolder()); + vec.emplace_back(CString("HandlerInfo"), GetHandlerInfo()); + vec.emplace_back(CString("ProtoCell"), GetProtoCell()); + vec.emplace_back(CString("Holder"), GetHolder()); } -void JSRealm::DumpForSnapshot(std::vector> &vec) const +void JSRealm::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("Value", GetValue()); - vec.emplace_back("GLobalEnv", GetGlobalEnv()); + vec.emplace_back(CString("Value"), GetValue()); + vec.emplace_back(CString("GLobalEnv"), GetGlobalEnv()); JSObject::DumpForSnapshot(vec); } #ifdef ARK_SUPPORT_INTL -void JSIntl::DumpForSnapshot(std::vector> &vec) const +void JSIntl::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("FallbackSymbol", GetFallbackSymbol()); + vec.emplace_back(CString("FallbackSymbol"), GetFallbackSymbol()); JSObject::DumpForSnapshot(vec); } -void JSLocale::DumpForSnapshot(std::vector> &vec) const +void JSLocale::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("IcuField", GetIcuField()); + vec.emplace_back(CString("IcuField"), GetIcuField()); JSObject::DumpForSnapshot(vec); } -void JSDateTimeFormat::DumpForSnapshot(std::vector> &vec) const +void JSDateTimeFormat::DumpForSnapshot(std::vector &vec) const { // please update the NUM_OF_ITEMS if you change the items below constexpr int16_t NUM_OF_ITEMS = 11; vec.reserve(vec.size() + NUM_OF_ITEMS); - vec.emplace_back("Locale", GetLocale()); - vec.emplace_back("Calendar", GetCalendar()); - vec.emplace_back("NumberingSystem", GetNumberingSystem()); - vec.emplace_back("TimeZone", GetTimeZone()); - vec.emplace_back("HourCycle", JSTaggedValue(static_cast(GetHourCycle()))); - vec.emplace_back("LocaleIcu", GetLocaleIcu()); - vec.emplace_back("SimpleDateTimeFormatIcu", GetSimpleDateTimeFormatIcu()); - vec.emplace_back("Iso8601", GetIso8601()); - vec.emplace_back("DateStyle", JSTaggedValue(static_cast(GetDateStyle()))); - vec.emplace_back("TimeStyle", JSTaggedValue(static_cast(GetTimeStyle()))); - vec.emplace_back("BoundFormat", GetBoundFormat()); + vec.emplace_back(CString("Locale"), GetLocale()); + vec.emplace_back(CString("Calendar"), GetCalendar()); + vec.emplace_back(CString("NumberingSystem"), GetNumberingSystem()); + vec.emplace_back(CString("TimeZone"), GetTimeZone()); + vec.emplace_back(CString("HourCycle"), JSTaggedValue(static_cast(GetHourCycle()))); + vec.emplace_back(CString("LocaleIcu"), GetLocaleIcu()); + vec.emplace_back(CString("SimpleDateTimeFormatIcu"), GetSimpleDateTimeFormatIcu()); + vec.emplace_back(CString("Iso8601"), GetIso8601()); + vec.emplace_back(CString("DateStyle"), JSTaggedValue(static_cast(GetDateStyle()))); + vec.emplace_back(CString("TimeStyle"), JSTaggedValue(static_cast(GetTimeStyle()))); + vec.emplace_back(CString("BoundFormat"), GetBoundFormat()); JSObject::DumpForSnapshot(vec); } -void JSRelativeTimeFormat::DumpForSnapshot(std::vector> &vec) const +void JSRelativeTimeFormat::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("Locale", GetLocale()); - vec.emplace_back("NumberingSystem", GetNumberingSystem()); - vec.emplace_back("Style", JSTaggedValue(static_cast(GetStyle()))); - vec.emplace_back("Numeric", JSTaggedValue(static_cast(GetNumeric()))); - vec.emplace_back("IcuField", GetIcuField()); + vec.emplace_back(CString("Locale"), GetLocale()); + vec.emplace_back(CString("NumberingSystem"), GetNumberingSystem()); + vec.emplace_back(CString("Style"), JSTaggedValue(static_cast(GetStyle()))); + vec.emplace_back(CString("Numeric"), JSTaggedValue(static_cast(GetNumeric()))); + vec.emplace_back(CString("IcuField"), GetIcuField()); JSObject::DumpForSnapshot(vec); } -void JSNumberFormat::DumpForSnapshot(std::vector> &vec) const +void JSNumberFormat::DumpForSnapshot(std::vector &vec) const { // please update the NUM_OF_ITEMS if you change the items below constexpr int16_t NUM_OF_ITEMS = 20; vec.reserve(vec.size() + NUM_OF_ITEMS); - vec.emplace_back("Locale", GetLocale()); - vec.emplace_back("NumberingSystem", GetNumberingSystem()); - vec.emplace_back("Style", JSTaggedValue(static_cast(GetStyle()))); - vec.emplace_back("Currency", GetCurrency()); - vec.emplace_back("CurrencyDisplay", JSTaggedValue(static_cast(GetCurrencyDisplay()))); - vec.emplace_back("CurrencySign", JSTaggedValue(static_cast(GetCurrencySign()))); - vec.emplace_back("Unit", GetUnit()); - vec.emplace_back("UnitDisplay", JSTaggedValue(static_cast(GetUnitDisplay()))); - vec.emplace_back("MinimumIntegerDigits", GetMinimumIntegerDigits()); - vec.emplace_back("MinimumFractionDigits", GetMinimumFractionDigits()); - vec.emplace_back("MaximumFractionDigits", GetMaximumFractionDigits()); - vec.emplace_back("MinimumSignificantDigits", GetMinimumSignificantDigits()); - vec.emplace_back("MaximumSignificantDigits", GetMaximumSignificantDigits()); - vec.emplace_back("UseGrouping", GetUseGrouping()); - vec.emplace_back("RoundingType", JSTaggedValue(static_cast(GetRoundingType()))); - vec.emplace_back("Notation", JSTaggedValue(static_cast(GetNotation()))); - vec.emplace_back("CompactDisplay", JSTaggedValue(static_cast(GetCompactDisplay()))); - vec.emplace_back("SignDisplay", JSTaggedValue(static_cast(GetSignDisplay()))); - vec.emplace_back("BoundFormat", GetBoundFormat()); - vec.emplace_back("IcuField", GetIcuField()); + vec.emplace_back(CString("Locale"), GetLocale()); + vec.emplace_back(CString("NumberingSystem"), GetNumberingSystem()); + vec.emplace_back(CString("Style"), JSTaggedValue(static_cast(GetStyle()))); + vec.emplace_back(CString("Currency"), GetCurrency()); + vec.emplace_back(CString("CurrencyDisplay"), JSTaggedValue(static_cast(GetCurrencyDisplay()))); + vec.emplace_back(CString("CurrencySign"), JSTaggedValue(static_cast(GetCurrencySign()))); + vec.emplace_back(CString("Unit"), GetUnit()); + vec.emplace_back(CString("UnitDisplay"), JSTaggedValue(static_cast(GetUnitDisplay()))); + vec.emplace_back(CString("MinimumIntegerDigits"), GetMinimumIntegerDigits()); + vec.emplace_back(CString("MinimumFractionDigits"), GetMinimumFractionDigits()); + vec.emplace_back(CString("MaximumFractionDigits"), GetMaximumFractionDigits()); + vec.emplace_back(CString("MinimumSignificantDigits"), GetMinimumSignificantDigits()); + vec.emplace_back(CString("MaximumSignificantDigits"), GetMaximumSignificantDigits()); + vec.emplace_back(CString("UseGrouping"), GetUseGrouping()); + vec.emplace_back(CString("RoundingType"), JSTaggedValue(static_cast(GetRoundingType()))); + vec.emplace_back(CString("Notation"), JSTaggedValue(static_cast(GetNotation()))); + vec.emplace_back(CString("CompactDisplay"), JSTaggedValue(static_cast(GetCompactDisplay()))); + vec.emplace_back(CString("SignDisplay"), JSTaggedValue(static_cast(GetSignDisplay()))); + vec.emplace_back(CString("BoundFormat"), GetBoundFormat()); + vec.emplace_back(CString("IcuField"), GetIcuField()); JSObject::DumpForSnapshot(vec); } -void JSCollator::DumpForSnapshot(std::vector> &vec) const +void JSCollator::DumpForSnapshot(std::vector &vec) const { // please update the NUM_OF_ITEMS if you change the items below constexpr int16_t NUM_OF_ITEMS = 9; vec.reserve(vec.size() + NUM_OF_ITEMS); - vec.emplace_back("IcuField", GetIcuField()); - vec.emplace_back("Locale", GetLocale()); - vec.emplace_back("Collation", GetCollation()); - vec.emplace_back("BoundCompare", GetBoundCompare()); - vec.emplace_back("CaseFirst", JSTaggedValue(static_cast(GetCaseFirst()))); - vec.emplace_back("Usage", JSTaggedValue(static_cast(GetUsage()))); - vec.emplace_back("Sensitivity", JSTaggedValue(static_cast(GetSensitivity()))); - vec.emplace_back("IgnorePunctuation", JSTaggedValue(GetIgnorePunctuation())); - vec.emplace_back("Numeric", JSTaggedValue(GetNumeric())); + vec.emplace_back(CString("IcuField"), GetIcuField()); + vec.emplace_back(CString("Locale"), GetLocale()); + vec.emplace_back(CString("Collation"), GetCollation()); + vec.emplace_back(CString("BoundCompare"), GetBoundCompare()); + vec.emplace_back(CString("CaseFirst"), JSTaggedValue(static_cast(GetCaseFirst()))); + vec.emplace_back(CString("Usage"), JSTaggedValue(static_cast(GetUsage()))); + vec.emplace_back(CString("Sensitivity"), JSTaggedValue(static_cast(GetSensitivity()))); + vec.emplace_back(CString("IgnorePunctuation"), JSTaggedValue(GetIgnorePunctuation())); + vec.emplace_back(CString("Numeric"), JSTaggedValue(GetNumeric())); JSObject::DumpForSnapshot(vec); } -void JSPluralRules::DumpForSnapshot(std::vector> &vec) const +void JSPluralRules::DumpForSnapshot(std::vector &vec) const { // please update the NUM_OF_ITEMS if you change the items below constexpr int16_t NUM_OF_ITEMS = 10; vec.reserve(vec.size() + NUM_OF_ITEMS); - vec.emplace_back("Locale", GetLocale()); - vec.emplace_back("MinimumIntegerDigits", GetMinimumIntegerDigits()); - vec.emplace_back("MinimumFractionDigits", GetMinimumFractionDigits()); - vec.emplace_back("MaximumFractionDigits", GetMaximumFractionDigits()); - vec.emplace_back("MinimumSignificantDigits", GetMinimumSignificantDigits()); - vec.emplace_back("MaximumSignificantDigits", GetMaximumSignificantDigits()); - vec.emplace_back("RoundingType", JSTaggedValue(static_cast(GetRoundingType()))); - vec.emplace_back("IcuPR", GetIcuPR()); - vec.emplace_back("IcuNF", GetIcuNF()); - vec.emplace_back("Type", JSTaggedValue(static_cast(GetType()))); + vec.emplace_back(CString("Locale"), GetLocale()); + vec.emplace_back(CString("MinimumIntegerDigits"), GetMinimumIntegerDigits()); + vec.emplace_back(CString("MinimumFractionDigits"), GetMinimumFractionDigits()); + vec.emplace_back(CString("MaximumFractionDigits"), GetMaximumFractionDigits()); + vec.emplace_back(CString("MinimumSignificantDigits"), GetMinimumSignificantDigits()); + vec.emplace_back(CString("MaximumSignificantDigits"), GetMaximumSignificantDigits()); + vec.emplace_back(CString("RoundingType"), JSTaggedValue(static_cast(GetRoundingType()))); + vec.emplace_back(CString("IcuPR"), GetIcuPR()); + vec.emplace_back(CString("IcuNF"), GetIcuNF()); + vec.emplace_back(CString("Type"), JSTaggedValue(static_cast(GetType()))); JSObject::DumpForSnapshot(vec); } -void JSDisplayNames::DumpForSnapshot(std::vector> &vec) const +void JSDisplayNames::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("Locale", GetLocale()); - vec.emplace_back("Type", JSTaggedValue(static_cast(GetType()))); - vec.emplace_back("Style", JSTaggedValue(static_cast(GetStyle()))); - vec.emplace_back("Fallback", JSTaggedValue(static_cast(GetFallback()))); - vec.emplace_back("IcuLDN", GetIcuLDN()); + vec.emplace_back(CString("Locale"), GetLocale()); + vec.emplace_back(CString("Type"), JSTaggedValue(static_cast(GetType()))); + vec.emplace_back(CString("Style"), JSTaggedValue(static_cast(GetStyle()))); + vec.emplace_back(CString("Fallback"), JSTaggedValue(static_cast(GetFallback()))); + vec.emplace_back(CString("IcuLDN"), GetIcuLDN()); JSObject::DumpForSnapshot(vec); } -void JSListFormat::DumpForSnapshot(std::vector> &vec) const +void JSListFormat::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("Locale", GetLocale()); - vec.emplace_back("Type", JSTaggedValue(static_cast(GetType()))); - vec.emplace_back("Style", JSTaggedValue(static_cast(GetStyle()))); - vec.emplace_back("IcuLF", GetIcuLF()); + vec.emplace_back(CString("Locale"), GetLocale()); + vec.emplace_back(CString("Type"), JSTaggedValue(static_cast(GetType()))); + vec.emplace_back(CString("Style"), JSTaggedValue(static_cast(GetStyle()))); + vec.emplace_back(CString("IcuLF"), GetIcuLF()); JSObject::DumpForSnapshot(vec); } #endif -void JSGeneratorObject::DumpForSnapshot(std::vector> &vec) const +void JSGeneratorObject::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("GeneratorContext", GetGeneratorContext()); - vec.emplace_back("ResumeResult", GetResumeResult()); - vec.emplace_back("GeneratorState", JSTaggedValue(static_cast(GetGeneratorState()))); - vec.emplace_back("ResumeMode", JSTaggedValue(static_cast(GetResumeMode()))); + vec.emplace_back(CString("GeneratorContext"), GetGeneratorContext()); + vec.emplace_back(CString("ResumeResult"), GetResumeResult()); + vec.emplace_back(CString("GeneratorState"), JSTaggedValue(static_cast(GetGeneratorState()))); + vec.emplace_back(CString("ResumeMode"), JSTaggedValue(static_cast(GetResumeMode()))); JSObject::DumpForSnapshot(vec); } -void JSAsyncGeneratorObject::DumpForSnapshot(std::vector> &vec) const +void JSAsyncGeneratorObject::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("GeneratorContext", GetGeneratorContext()); - vec.emplace_back("AsyncGeneratorQueue", GetAsyncGeneratorQueue()); - vec.emplace_back("GeneratorBrand", GetGeneratorBrand()); - vec.emplace_back("ResumeResult", GetResumeResult()); - vec.emplace_back("AsyncGeneratorState", JSTaggedValue(static_cast(GetAsyncGeneratorState()))); - vec.emplace_back("ResumeMode", JSTaggedValue(static_cast(GetResumeMode()))); + vec.emplace_back(CString("GeneratorContext"), GetGeneratorContext()); + vec.emplace_back(CString("AsyncGeneratorQueue"), GetAsyncGeneratorQueue()); + vec.emplace_back(CString("GeneratorBrand"), GetGeneratorBrand()); + vec.emplace_back(CString("ResumeResult"), GetResumeResult()); + vec.emplace_back(CString("AsyncGeneratorState"), JSTaggedValue(static_cast(GetAsyncGeneratorState()))); + vec.emplace_back(CString("ResumeMode"), JSTaggedValue(static_cast(GetResumeMode()))); JSObject::DumpForSnapshot(vec); } -void JSAsyncFuncObject::DumpForSnapshot(std::vector> &vec) const +void JSAsyncFuncObject::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("Promise", GetPromise()); + vec.emplace_back(CString("Promise"), GetPromise()); } -void GeneratorContext::DumpForSnapshot(std::vector> &vec) const +void GeneratorContext::DumpForSnapshot(std::vector &vec) const { // please update the NUM_OF_ITEMS if you change the items below constexpr int16_t NUM_OF_ITEMS = 8; vec.reserve(vec.size() + NUM_OF_ITEMS); - vec.emplace_back("RegsArray", GetRegsArray()); - vec.emplace_back("Method", GetMethod()); - vec.emplace_back("This", GetThis()); - vec.emplace_back("Acc", GetAcc()); - vec.emplace_back("GeneratorObject", GetGeneratorObject()); - vec.emplace_back("LexicalEnv", GetLexicalEnv()); - vec.emplace_back("NRegs", JSTaggedValue(GetNRegs())); - vec.emplace_back("BCOffset", JSTaggedValue(GetBCOffset())); + vec.emplace_back(CString("RegsArray"), GetRegsArray()); + vec.emplace_back(CString("Method"), GetMethod()); + vec.emplace_back(CString("This"), GetThis()); + vec.emplace_back(CString("Acc"), GetAcc()); + vec.emplace_back(CString("GeneratorObject"), GetGeneratorObject()); + vec.emplace_back(CString("LexicalEnv"), GetLexicalEnv()); + vec.emplace_back(CString("NRegs"), JSTaggedValue(GetNRegs())); + vec.emplace_back(CString("BCOffset"), JSTaggedValue(GetBCOffset())); +} + +void ProtoChangeMarker::DumpForSnapshot(std::vector &vec) const +{ + vec.emplace_back(CString("Promise"), JSTaggedValue(GetHasChanged())); +} + +void MarkerCell::DumpForSnapshot(std::vector &vec) const +{ + vec.emplace_back(CString("IsDetectorInvalid"), JSTaggedValue(GetIsDetectorInvalid())); } -void ProtoChangeMarker::DumpForSnapshot(std::vector> &vec) const +void ProtoChangeDetails::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("Promise", JSTaggedValue(GetHasChanged())); + vec.emplace_back(CString("ChangeListener"), GetChangeListener()); + vec.emplace_back(CString("RegisterIndex"), JSTaggedValue(GetRegisterIndex())); } -void ProtoChangeDetails::DumpForSnapshot(std::vector> &vec) const +void MachineCode::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("ChangeListener", GetChangeListener()); - vec.emplace_back("RegisterIndex", JSTaggedValue(GetRegisterIndex())); + vec.emplace_back(CString("InstructionSizeInBytes"), JSTaggedValue(GetInstructionSizeInBytes())); } -void MachineCode::DumpForSnapshot(std::vector> &vec) const +void TrackInfo::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("InstructionSizeInBytes", JSTaggedValue(GetInstructionSizeInBytes())); + vec.emplace_back("ElementsKind", JSTaggedValue(static_cast(GetElementsKind()))); } -void ClassInfoExtractor::DumpForSnapshot(std::vector> &vec) const +void ClassInfoExtractor::DumpForSnapshot(std::vector &vec) const { // please update the NUM_OF_ITEMS if you change the items below constexpr int16_t NUM_OF_ITEMS = 6; vec.reserve(vec.size() + NUM_OF_ITEMS); - vec.emplace_back("NonStaticKeys", GetNonStaticKeys()); - vec.emplace_back("NonStaticProperties", GetNonStaticProperties()); - vec.emplace_back("NonStaticElements", GetNonStaticElements()); - vec.emplace_back("StaticKeys", GetStaticKeys()); - vec.emplace_back("StaticProperties", GetStaticProperties()); - vec.emplace_back("StaticElements", GetStaticElements()); + vec.emplace_back(CString("NonStaticKeys"), GetNonStaticKeys()); + vec.emplace_back(CString("NonStaticProperties"), GetNonStaticProperties()); + vec.emplace_back(CString("NonStaticElements"), GetNonStaticElements()); + vec.emplace_back(CString("StaticKeys"), GetStaticKeys()); + vec.emplace_back(CString("StaticProperties"), GetStaticProperties()); + vec.emplace_back(CString("StaticElements"), GetStaticElements()); } -void TSObjectType::DumpForSnapshot(std::vector> &vec) const +void TSObjectType::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("ObjLayoutInfo", GetObjLayoutInfo()); - vec.emplace_back("IndexSigns", GetIndexSigns()); + vec.emplace_back(CString("ObjLayoutInfo"), GetObjLayoutInfo()); + vec.emplace_back(CString("IndexSigns"), GetIndexSigns()); } -void TSClassType::DumpForSnapshot(std::vector> &vec) const +void TSClassType::DumpForSnapshot(std::vector &vec) const { // please update the NUM_OF_ITEMS if you change the items below constexpr int16_t NUM_OF_ITEMS = 5; vec.reserve(vec.size() + NUM_OF_ITEMS); - vec.emplace_back("InstanceType", GetInstanceType()); - vec.emplace_back("ConstructorType", GetConstructorType()); - vec.emplace_back("PrototypeType", GetPrototypeType()); - vec.emplace_back("ExtensionGT", JSTaggedValue(GetExtensionGT().GetType())); - vec.emplace_back("HasLinked", JSTaggedValue(GetHasLinked())); + vec.emplace_back(CString("InstanceType"), GetInstanceType()); + vec.emplace_back(CString("ConstructorType"), GetConstructorType()); + vec.emplace_back(CString("PrototypeType"), GetPrototypeType()); + vec.emplace_back(CString("ExtensionGT"), JSTaggedValue(GetExtensionGT().GetType())); + vec.emplace_back(CString("HasLinked"), JSTaggedValue(GetHasLinked())); } -void TSInterfaceType::DumpForSnapshot(std::vector> &vec) const +void TSInterfaceType::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("Fields", GetFields()); - vec.emplace_back("Extends", GetExtends()); + vec.emplace_back(CString("Fields"), GetFields()); + vec.emplace_back(CString("Extends"), GetExtends()); } -void TSClassInstanceType::DumpForSnapshot(std::vector> &vec) const +void TSClassInstanceType::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("ClassGT", JSTaggedValue(GetClassGT().GetType())); + vec.emplace_back(CString("ClassGT"), JSTaggedValue(GetClassGT().GetType())); } -void TSUnionType::DumpForSnapshot(std::vector> &vec) const +void TSUnionType::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("ComponentTypes", GetComponents()); + vec.emplace_back(CString("ComponentTypes"), GetComponents()); } -void TSFunctionType::DumpForSnapshot(std::vector> &vec) const +void TSFunctionType::DumpForSnapshot(std::vector &vec) const { // please update the NUM_OF_ITEMS if you change the items below constexpr int16_t NUM_OF_ITEMS = 5; vec.reserve(vec.size() + NUM_OF_ITEMS); - vec.emplace_back("Name", GetName()); - vec.emplace_back("ParameterTypes", GetParameterTypes()); - vec.emplace_back("ReturnGT", JSTaggedValue(GetReturnGT().GetType())); - vec.emplace_back("ThisGT", JSTaggedValue(GetThisGT().GetType())); - vec.emplace_back("BitFiled", JSTaggedValue(GetBitField())); + vec.emplace_back(CString("Name"), GetName()); + vec.emplace_back(CString("ParameterTypes"), GetParameterTypes()); + vec.emplace_back(CString("ReturnGT"), JSTaggedValue(GetReturnGT().GetType())); + vec.emplace_back(CString("ThisGT"), JSTaggedValue(GetThisGT().GetType())); + vec.emplace_back(CString("BitFiled"), JSTaggedValue(GetBitField())); } -void TSArrayType::DumpForSnapshot(std::vector> &vec) const +void TSArrayType::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("ParameterTypeRef", JSTaggedValue(GetElementGT().GetType())); + vec.emplace_back(CString("ParameterTypeRef"), JSTaggedValue(GetElementGT().GetType())); } -void TSIteratorInstanceType::DumpForSnapshot(std::vector> &vec) const +void TSIteratorInstanceType::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("kindGT", JSTaggedValue(GetKindGT().GetType())); - vec.emplace_back("elementGT", JSTaggedValue(GetElementGT().GetType())); + vec.emplace_back(CString("kindGT"), JSTaggedValue(GetKindGT().GetType())); + vec.emplace_back(CString("elementGT"), JSTaggedValue(GetElementGT().GetType())); } -void TSNamespaceType::DumpForSnapshot(std::vector> &vec) const +void TSNamespaceType::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("PropertyType", GetPropertyType()); + vec.emplace_back(CString("PropertyType"), GetPropertyType()); } -void SourceTextModule::DumpForSnapshot(std::vector> &vec) const +void SourceTextModule::DumpForSnapshot(std::vector &vec) const { // please update the NUM_OF_ITEMS if you change the items below constexpr int16_t NUM_OF_ITEMS = 14; vec.reserve(vec.size() + NUM_OF_ITEMS); - vec.emplace_back("Environment", GetEnvironment()); - vec.emplace_back("Namespace", GetNamespace()); - vec.emplace_back("EcmaModuleFilename", GetEcmaModuleFilename()); - vec.emplace_back("EcmaModuleRecordName", GetEcmaModuleRecordName()); - vec.emplace_back("RequestedModules", GetRequestedModules()); - vec.emplace_back("ImportEntries", GetImportEntries()); - vec.emplace_back("LocalExportEntries", GetLocalExportEntries()); - vec.emplace_back("IndirectExportEntries", GetIndirectExportEntries()); - vec.emplace_back("StarExportEntries", GetStarExportEntries()); - vec.emplace_back("Status", JSTaggedValue(static_cast(GetStatus()))); - vec.emplace_back("EvaluationError", JSTaggedValue(GetEvaluationError())); - vec.emplace_back("DFSIndex", JSTaggedValue(GetDFSIndex())); - vec.emplace_back("DFSAncestorIndex", JSTaggedValue(GetDFSAncestorIndex())); - vec.emplace_back("NameDictionary", GetNameDictionary()); + vec.emplace_back(CString("Environment"), GetEnvironment()); + vec.emplace_back(CString("Namespace"), GetNamespace()); + vec.emplace_back(CString("EcmaModuleFilename"), GetEcmaModuleFilename()); + vec.emplace_back(CString("EcmaModuleRecordName"), GetEcmaModuleRecordName()); + vec.emplace_back(CString("RequestedModules"), GetRequestedModules()); + vec.emplace_back(CString("ImportEntries"), GetImportEntries()); + vec.emplace_back(CString("LocalExportEntries"), GetLocalExportEntries()); + vec.emplace_back(CString("IndirectExportEntries"), GetIndirectExportEntries()); + vec.emplace_back(CString("StarExportEntries"), GetStarExportEntries()); + vec.emplace_back(CString("Status"), JSTaggedValue(static_cast(GetStatus()))); + vec.emplace_back(CString("EvaluationError"), JSTaggedValue(GetEvaluationError())); + vec.emplace_back(CString("DFSIndex"), JSTaggedValue(GetDFSIndex())); + vec.emplace_back(CString("DFSAncestorIndex"), JSTaggedValue(GetDFSAncestorIndex())); + vec.emplace_back(CString("NameDictionary"), GetNameDictionary()); } -void ImportEntry::DumpForSnapshot(std::vector> &vec) const +void ImportEntry::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("ModuleRequest", GetModuleRequest()); - vec.emplace_back("ImportName", GetImportName()); - vec.emplace_back("LocalName", GetLocalName()); + vec.emplace_back(CString("ModuleRequest"), GetModuleRequest()); + vec.emplace_back(CString("ImportName"), GetImportName()); + vec.emplace_back(CString("LocalName"), GetLocalName()); } -void LocalExportEntry::DumpForSnapshot(std::vector> &vec) const +void LocalExportEntry::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("ExportName", GetExportName()); - vec.emplace_back("LocalName", GetLocalName()); + vec.emplace_back(CString("ExportName"), GetExportName()); + vec.emplace_back(CString("LocalName"), GetLocalName()); } -void IndirectExportEntry::DumpForSnapshot(std::vector> &vec) const +void IndirectExportEntry::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("ExportName", GetExportName()); - vec.emplace_back("ModuleRequest", GetModuleRequest()); - vec.emplace_back("ImportName", GetImportName()); + vec.emplace_back(CString("ExportName"), GetExportName()); + vec.emplace_back(CString("ModuleRequest"), GetModuleRequest()); + vec.emplace_back(CString("ImportName"), GetImportName()); } -void StarExportEntry::DumpForSnapshot(std::vector> &vec) const +void StarExportEntry::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("ModuleRequest", GetModuleRequest()); + vec.emplace_back(CString("ModuleRequest"), GetModuleRequest()); } -void ResolvedBinding::DumpForSnapshot(std::vector> &vec) const +void ResolvedBinding::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("Module", GetModule()); - vec.emplace_back("BindingName", GetBindingName()); + vec.emplace_back(CString("Module"), GetModule()); + vec.emplace_back(CString("BindingName"), GetBindingName()); } -void ResolvedIndexBinding::DumpForSnapshot(std::vector> &vec) const +void ResolvedIndexBinding::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("Module", GetModule()); - vec.emplace_back("Index", JSTaggedValue(GetIndex())); + vec.emplace_back(CString("Module"), GetModule()); + vec.emplace_back(CString("Index"), JSTaggedValue(GetIndex())); } -void ModuleNamespace::DumpForSnapshot(std::vector> &vec) const +void ModuleNamespace::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("Module", GetModule()); - vec.emplace_back("Exports", GetExports()); + vec.emplace_back(CString("Module"), GetModule()); + vec.emplace_back(CString("Exports"), GetExports()); } -void CjsModule::DumpForSnapshot(std::vector> &vec) const +void CjsModule::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("Id", GetId()); - vec.emplace_back("Path", GetPath()); - vec.emplace_back("Exports", GetExports()); - vec.emplace_back("Filename", GetFilename()); + vec.emplace_back(CString("Id"), GetId()); + vec.emplace_back(CString("Path"), GetPath()); + vec.emplace_back(CString("Exports"), GetExports()); + vec.emplace_back(CString("Filename"), GetFilename()); } -void CjsExports::DumpForSnapshot(std::vector> &vec) const +void CjsExports::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("Exports", GetExports()); + vec.emplace_back(CString("Exports"), GetExports()); } -void CjsRequire::DumpForSnapshot(std::vector> &vec) const +void CjsRequire::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("Cache", GetCache()); - vec.emplace_back("Parent", GetParent()); + vec.emplace_back(CString("Cache"), GetCache()); + vec.emplace_back(CString("Parent"), GetParent()); } -void ClassLiteral::DumpForSnapshot(std::vector> &vec) const +void ClassLiteral::DumpForSnapshot(std::vector &vec) const { - vec.emplace_back("Array", GetArray()); - vec.emplace_back("IsAOTUsed", GetIsAOTUsed()); + vec.emplace_back(CString("Array"), GetArray()); + vec.emplace_back(CString("IsAOTUsed"), JSTaggedValue(GetIsAOTUsed())); } } // namespace panda::ecmascript diff --git a/ecmascript/ecma_context.cpp b/ecmascript/ecma_context.cpp index 23dcdcdce65c9fc20b79e56c726d3760b141c2f7..1103fb139e66a1c6d6c27f1ee49d749a28b04151 100644 --- a/ecmascript/ecma_context.cpp +++ b/ecmascript/ecma_context.cpp @@ -32,6 +32,7 @@ #include "ecmascript/jspandafile/program_object.h" #include "ecmascript/js_function.h" #include "ecmascript/js_thread.h" +#include "ecmascript/module/module_path_helper.h" #include "ecmascript/object_factory.h" #include "ecmascript/pgo_profiler/pgo_profiler_manager.h" #include "ecmascript/require/js_cjs_module_cache.h" @@ -80,22 +81,31 @@ bool EcmaContext::Initialize() propertiesCache_ = new PropertiesCache(); regExpParserCache_ = new RegExpParserCache(); + JSHandle hClassHandle = factory_->InitClassClass(); + JSHandle globalEnvClass = factory_->NewEcmaHClass( - JSHClass::Cast(vm_->GetHClassClass().GetTaggedObject()), + *hClassHandle, GlobalEnv::SIZE, JSType::GLOBAL_ENV); + thread_->SetGlobalConst(&globalConst_); + globalConst_.Init(thread_, *hClassHandle); + auto arrayHClassIndexMaps = Elements::InitializeHClassMap(); + thread_->SetArrayHClassIndexMap(arrayHClassIndexMaps); JSHandle globalEnv = factory_->NewGlobalEnv(*globalEnvClass); globalEnv->Init(thread_); globalEnv_ = globalEnv.GetTaggedValue(); Builtins builtins; - builtins.Initialize(globalEnv, thread_); + bool builtinsLazyEnabled = vm_->GetJSOptions().IsWorker() && vm_->GetJSOptions().GetEnableBuiltinsLazy(); + builtins.Initialize(globalEnv, thread_, builtinsLazyEnabled); SetupRegExpResultCache(); + SetupRegExpGlobalResult(); microJobQueue_ = factory_->NewMicroJobQueue().GetTaggedValue(); moduleManager_ = new ModuleManager(vm_); tsManager_ = new TSManager(vm_); optCodeProfiler_ = new OptCodeProfiler(); + initialized_ = true; return true; } @@ -202,6 +212,12 @@ EcmaContext::~EcmaContext() delete propertiesCache_; propertiesCache_ = nullptr; } + // clear join stack + joinStack_.clear(); + + for (auto v : stringifyCache_) { + v.clear(); + } } JSTaggedValue EcmaContext::InvokeEcmaAotEntrypoint(JSHandle mainFunc, JSHandle &thisArg, @@ -241,36 +257,43 @@ Expected EcmaContext::InvokeEcmaEntrypoint(const JSPandaFil jsPandaFile->GetJSPandaFileDesc(), entryPoint); JSHandle func(thread_, program->GetMainFunction()); + JSHandle method(thread_, func->GetMethod()); JSHandle global = GlobalEnv::Cast(globalEnv_.GetTaggedObject())->GetJSGlobalObject(); JSHandle undefined = thread_->GlobalConstants()->GetHandledUndefined(); - if (jsPandaFile->IsModule(thread_, entryPoint.data())) { + CString entry = entryPoint.data(); + JSRecordInfo recordInfo; + bool hasRecord = jsPandaFile->CheckAndGetRecordInfo(entry, recordInfo); + if (!hasRecord) { + CString msg = "cannot find record '" + entry + "', please check the request path."; + LOG_FULL(ERROR) << msg; + THROW_REFERENCE_ERROR_AND_RETURN(thread_, msg.c_str(), Unexpected(false)); + } + if (jsPandaFile->IsModule(recordInfo)) { global = undefined; CString moduleName = jsPandaFile->GetJSPandaFileDesc(); if (!jsPandaFile->IsBundlePack()) { - moduleName = entryPoint.data(); + moduleName = entry; } JSHandle module = moduleManager_->HostGetImportedModule(moduleName); - func->SetModule(thread_, module); + method->SetModule(thread_, module); } else { // if it is Cjs at present, the module slot of the function is not used. We borrow it to store the recordName, // which can avoid the problem of larger memory caused by the new slot - JSHandle recordName = factory_->NewFromUtf8(entryPoint.data()); - func->SetModule(thread_, recordName); + JSHandle recordName = factory_->NewFromUtf8(entry); + method->SetModule(thread_, recordName); } vm_->CheckStartCpuProfiler(); JSTaggedValue result; - if (jsPandaFile->IsCjs(thread_, entryPoint.data())) { - if (!thread_->HasPendingException()) { - CJSExecution(func, global, jsPandaFile, entryPoint); - } + if (jsPandaFile->IsCjs(recordInfo)) { + CJSExecution(func, global, jsPandaFile, entryPoint); } else { - if (aotFileManager_->IsLoadMain(jsPandaFile, entryPoint.data())) { + if (aotFileManager_->IsLoadMain(jsPandaFile, entry)) { EcmaRuntimeStatScope runtimeStatScope(vm_); result = InvokeEcmaAotEntrypoint(func, global, jsPandaFile, entryPoint); } else { if (thread_->IsPGOProfilerEnable()) { - vm_->GetPGOProfiler()->ProfileCall(func.GetTaggedType()); + vm_->GetPGOProfiler()->ProfileCall(func.GetTaggedType(), pgo::SampleMode::HOTNESS_MODE); } EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread_, JSHandle(func), global, undefined, 0); @@ -299,16 +322,18 @@ void EcmaContext::CJSExecution(JSHandle &func, JSHandle filename(thread_, JSTaggedValue::Undefined()); JSMutableHandle dirname(thread_, JSTaggedValue::Undefined()); if (jsPandaFile->IsBundlePack()) { - PathHelper::ResolveCurrentPath(thread_, dirname, filename, jsPandaFile); + ModulePathHelper::ResolveCurrentPath(thread_, dirname, filename, jsPandaFile); } else { filename.Update(func->GetModule()); ASSERT(filename->IsString()); - dirname.Update(PathHelper::ResolveDirPath(thread_, filename)); + CString fullName = ConvertToString(filename.GetTaggedValue()); + dirname.Update(PathHelper::ResolveDirPath(thread_, fullName)); } CJSInfo cjsInfo(module, require, exports, filename, dirname); RequireManager::InitializeCommonJS(thread_, cjsInfo); if (aotFileManager_->IsLoadMain(jsPandaFile, entryPoint.data())) { EcmaRuntimeStatScope runtimeStateScope(vm_); + isAotEntry_ = true; InvokeEcmaAotEntrypoint(func, thisArg, jsPandaFile, entryPoint, &cjsInfo); } else { // Execute main function @@ -381,6 +406,15 @@ JSHandle EcmaContext::FindOrCreateConstPool(const JSPandaFile *jsP panda_file::IndexAccessor indexAccessor(*jsPandaFile->GetPandaFile(), id); int32_t index = static_cast(indexAccessor.GetHeaderIndex()); JSTaggedValue constpool = FindConstpool(jsPandaFile, index); + // In the taskpool thread, there is a case where the Function object is serialized before InitForCurrentThread. + // A constpool is created when a Function is serialized. Slowpath, the default deserialized constpool, + // string is non-lazy load mode. A hole is returned if you access the constpool of the serialized Function + if (constpool.IsHole() && ecmascript::AnFileDataManager::GetInstance()->IsEnable()) { + bool result = GetAOTFileManager()->LoadAiFile(jsPandaFile); + if (result) { + constpool = FindConstpool(jsPandaFile, index); + } + } if (constpool.IsHole()) { JSHandle newConstpool = ConstantPool::CreateConstPool(vm_, jsPandaFile, id); AddConstpool(jsPandaFile, newConstpool.GetTaggedValue(), index); @@ -503,7 +537,7 @@ void EcmaContext::HandleUncaughtException(JSTaggedValue exception) // if caught exceptionHandle type is JSError thread_->ClearException(); if (exceptionHandle->IsJSError()) { - PrintJSErrorInfo(exceptionHandle); + PrintJSErrorInfo(thread_, exceptionHandle); return; } JSHandle result = JSTaggedValue::ToString(thread_, exceptionHandle); @@ -511,14 +545,33 @@ void EcmaContext::HandleUncaughtException(JSTaggedValue exception) LOG_NO_TAG(ERROR) << string; } -void EcmaContext::PrintJSErrorInfo(const JSHandle &exceptionInfo) -{ - JSHandle nameKey = thread_->GlobalConstants()->GetHandledNameString(); - JSHandle name(JSObject::GetProperty(thread_, exceptionInfo, nameKey).GetValue()); - JSHandle msgKey = thread_->GlobalConstants()->GetHandledMessageString(); - JSHandle msg(JSObject::GetProperty(thread_, exceptionInfo, msgKey).GetValue()); - JSHandle stackKey = thread_->GlobalConstants()->GetHandledStackString(); - JSHandle stack(JSObject::GetProperty(thread_, exceptionInfo, stackKey).GetValue()); +// static +void EcmaContext::PrintJSErrorInfo(JSThread *thread, const JSHandle &exceptionInfo) +{ + JSHandle nameKey = thread->GlobalConstants()->GetHandledNameString(); + JSHandle nameValue = JSObject::GetProperty(thread, exceptionInfo, nameKey).GetValue(); + JSHandle name = JSTaggedValue::ToString(thread, nameValue); + // JSTaggedValue::ToString may cause exception. In this case, do not return, use "" instead. + if (thread->HasPendingException()) { + thread->ClearException(); + name = thread->GetEcmaVM()->GetFactory()->NewFromStdString(""); + } + JSHandle msgKey = thread->GlobalConstants()->GetHandledMessageString(); + JSHandle msgValue = JSObject::GetProperty(thread, exceptionInfo, msgKey).GetValue(); + JSHandle msg = JSTaggedValue::ToString(thread, msgValue); + // JSTaggedValue::ToString may cause exception. In this case, do not return, use "" instead. + if (thread->HasPendingException()) { + thread->ClearException(); + msg = thread->GetEcmaVM()->GetFactory()->NewFromStdString(""); + } + JSHandle stackKey = thread->GlobalConstants()->GetHandledStackString(); + JSHandle stackValue = JSObject::GetProperty(thread, exceptionInfo, stackKey).GetValue(); + JSHandle stack = JSTaggedValue::ToString(thread, stackValue); + // JSTaggedValue::ToString may cause exception. In this case, do not return, use "" instead. + if (thread->HasPendingException()) { + thread->ClearException(); + stack = thread->GetEcmaVM()->GetFactory()->NewFromStdString(""); + } CString nameBuffer = ConvertToString(*name); CString msgBuffer = ConvertToString(*msg); @@ -526,6 +579,14 @@ void EcmaContext::PrintJSErrorInfo(const JSHandle &exceptionInfo) LOG_NO_TAG(ERROR) << nameBuffer << ": " << msgBuffer << "\n" << stackBuffer; } +bool EcmaContext::HasPendingJob() +{ + if (isProcessingPendingJob_) { + return true; + } + return job::MicroJobQueue::HasPendingJob(thread_, GetMicroJobQueue()); +} + bool EcmaContext::ExecutePromisePendingJob() { if (isProcessingPendingJob_) { @@ -576,9 +637,7 @@ JSHandle EcmaContext::GetMicroJobQueue() const void EcmaContext::MountContext(JSThread *thread) { - EcmaContext *context = EcmaContext::Create(thread); - thread->PushContext(context); - context->Initialize(); + EcmaContext *context = EcmaContext::CreateAndInitialize(thread); thread->SwitchCurrentContext(context); } @@ -589,15 +648,41 @@ void EcmaContext::UnmountContext(JSThread *thread) Destroy(context); } +EcmaContext *EcmaContext::CreateAndInitialize(JSThread *thread) +{ + EcmaContext *context = EcmaContext::Create(thread); + thread->PushContext(context); + context->Initialize(); + return context; +} + +void EcmaContext::CheckAndDestroy(JSThread *thread, EcmaContext *context) +{ + if (thread->EraseContext(context)) { + Destroy(context); + return; + } + LOG_ECMA(FATAL) << "CheckAndDestroy a nonexistent context."; +} + void EcmaContext::SetupRegExpResultCache() { regexpCache_ = builtins::RegExpExecResultCache::CreateCacheTable(thread_); } +void EcmaContext::SetupRegExpGlobalResult() +{ + regexpGlobal_ = builtins::RegExpGlobalResult::CreateGloablResultTable(thread_); +} + void EcmaContext::Iterate(const RootVisitor &v, const RootRangeVisitor &rv) { + // visit global Constant + globalConst_.VisitRangeSlot(rv); + v(Root::ROOT_VM, ObjectSlot(reinterpret_cast(&globalEnv_))); v(Root::ROOT_VM, ObjectSlot(reinterpret_cast(®expCache_))); + v(Root::ROOT_VM, ObjectSlot(reinterpret_cast(®expGlobal_))); v(Root::ROOT_VM, ObjectSlot(reinterpret_cast(µJobQueue_))); if (moduleManager_) { moduleManager_->Iterate(v); @@ -621,6 +706,11 @@ void EcmaContext::Iterate(const RootVisitor &v, const RootRangeVisitor &rv) rv(ecmascript::Root::ROOT_HANDLE, ObjectSlot(ToUintPtr(start)), ObjectSlot(ToUintPtr(end))); } } + + if (!joinStack_.empty()) { + rv(Root::ROOT_VM, ObjectSlot(ToUintPtr(&joinStack_.front())), + ObjectSlot(ToUintPtr(&joinStack_.back()) + JSTaggedValue::TaggedTypeSize())); + } } size_t EcmaContext::IterateHandle(const RootRangeVisitor &rangeVisitor) @@ -725,4 +815,56 @@ void EcmaContext::DumpAOTInfo() const { aotFileManager_->DumpAOTInfo(); } + +bool EcmaContext::JoinStackPushFastPath(JSHandle receiver) +{ + if (JSTaggedValue::SameValue(joinStack_[0], JSTaggedValue::Hole())) { + joinStack_[0] = receiver.GetTaggedValue(); + return true; + } + return JoinStackPush(receiver); +} + +bool EcmaContext::JoinStackPush(JSHandle receiver) +{ + uint32_t capacity = joinStack_.size(); + JSTaggedValue receiverValue = receiver.GetTaggedValue(); + for (size_t i = 0; i < capacity; ++i) { + if (JSTaggedValue::SameValue(joinStack_[i], JSTaggedValue::Hole())) { + joinStack_[i] = receiverValue; + return true; + } + if (JSTaggedValue::SameValue(joinStack_[i], receiverValue)) { + return false; + } + } + joinStack_.emplace_back(receiverValue); + return true; +} + +void EcmaContext::JoinStackPopFastPath(JSHandle receiver) +{ + uint32_t length = joinStack_.size(); + if (JSTaggedValue::SameValue(joinStack_[0], receiver.GetTaggedValue()) && length == MIN_JOIN_STACK_SIZE) { + joinStack_[0] = JSTaggedValue::Hole(); + } else { + JoinStackPop(receiver); + } +} + +void EcmaContext::JoinStackPop(JSHandle receiver) +{ + uint32_t length = joinStack_.size(); + for (size_t i = 0; i < length; ++i) { + if (JSTaggedValue::SameValue(joinStack_[i], receiver.GetTaggedValue())) { + if (i == 0 && length > MIN_JOIN_STACK_SIZE) { + joinStack_ = {JSTaggedValue::Hole(), JSTaggedValue::Hole()}; + break; + } else { + joinStack_[i] = JSTaggedValue::Hole(); + break; + } + } + } +} } // namespace panda::ecmascript diff --git a/ecmascript/ecma_context.h b/ecmascript/ecma_context.h index ebb2879c8b3478bd144bcd1e1bef4435a376d1c6..8ed73e9e9eeca206dee3371c6917541838d80716 100644 --- a/ecmascript/ecma_context.h +++ b/ecmascript/ecma_context.h @@ -85,8 +85,10 @@ using PromiseRejectCallback = void (*)(void* info); using IcuDeleteEntry = void(*)(void *pointer, void *data); class EcmaContext { public: - static EcmaContext *Create(JSThread *thread); + static EcmaContext *CreateAndInitialize(JSThread *thread); + static void CheckAndDestroy(JSThread *thread, EcmaContext *context); + static EcmaContext *Create(JSThread *thread); static bool Destroy(EcmaContext *context); EcmaContext(JSThread *thread); @@ -99,6 +101,8 @@ public: bool Initialize(); + bool HasPendingJob(); + bool ExecutePromisePendingJob(); static EcmaContext *ConstCast(const EcmaContext *context) @@ -106,6 +110,11 @@ public: return const_cast(context); } + bool IsInitialized() const + { + return initialized_; + } + ModuleManager *GetModuleManager() const { return moduleManager_; @@ -168,6 +177,18 @@ public: return reinterpret_cast(®expCache_); } + void SetupRegExpGlobalResult(); + + JSHandle GetRegExpGlobalResult() const + { + return JSHandle(reinterpret_cast(®expGlobal_)); + } + + void SetRegExpGlobalResult(JSTaggedValue newResult) + { + regexpGlobal_ = newResult; + } + WaiterListNode *GetWaiterListNode() { return &waiterListNode_; @@ -211,7 +232,7 @@ public: JSHandle GetMicroJobQueue() const; - void PrintJSErrorInfo(const JSHandle &exceptionInfo); + static void PrintJSErrorInfo(JSThread *thread, const JSHandle &exceptionInfo); void Iterate(const RootVisitor &v, const RootRangeVisitor &rv); static void MountContext(JSThread *thread); static void UnmountContext(JSThread *thread); @@ -377,6 +398,30 @@ public: return propertiesCache_; } void ClearBufferData(); + const GlobalEnvConstants *GlobalConstants() const + { + return &globalConst_; + } + + bool JoinStackPushFastPath(JSHandle receiver); + bool JoinStackPush(JSHandle receiver); + void JoinStackPopFastPath(JSHandle receiver); + void JoinStackPop(JSHandle receiver); + + void SetJsonStringifyCache(size_t index, CVector> &value) + { + stringifyCache_[index] = value; + } + + CVector> GetJsonStringifyCache(size_t index) + { + return stringifyCache_[index]; + } + + bool IsAotEntry() + { + return isAotEntry_; + } private: void CJSExecution(JSHandle &func, JSHandle &thisArg, @@ -395,13 +440,14 @@ private: bool isUncaughtExceptionRegistered_ {false}; bool isProcessingPendingJob_ {false}; - + bool initialized_ {false}; ObjectFactory *factory_ {nullptr}; // VM execution states. RegExpParserCache *regExpParserCache_ {nullptr}; JSTaggedValue globalEnv_ {JSTaggedValue::Hole()}; JSTaggedValue regexpCache_ {JSTaggedValue::Hole()}; + JSTaggedValue regexpGlobal_ {JSTaggedValue::Hole()}; JSTaggedValue microJobQueue_ {JSTaggedValue::Hole()}; EcmaRuntimeStat *runtimeStat_ {nullptr}; @@ -453,8 +499,15 @@ private: JSTaggedType *frameBase_ {nullptr}; uint64_t stackStart_ {0}; uint64_t stackLimit_ {0}; - PropertiesCache *propertiesCache_ {nullptr}; + GlobalEnvConstants globalConst_; + // Join Stack + static constexpr uint32_t MIN_JOIN_STACK_SIZE = 2; + CVector joinStack_ {JSTaggedValue::Hole(), JSTaggedValue::Hole()}; + // json stringify cache + static constexpr uint32_t STRINGIFY_CACHE_SIZE = 64; + std::array>, STRINGIFY_CACHE_SIZE> stringifyCache_ {}; + bool isAotEntry_ { false }; friend class EcmaHandleScope; friend class JSPandaFileExecutor; diff --git a/ecmascript/ecma_handle_scope.cpp b/ecmascript/ecma_handle_scope.cpp index 63ba4dcb3bf393cff6f2c81d4b20c351b1cce859..a022ce52d29beb574f208dff4c25ccf5f57c630a 100644 --- a/ecmascript/ecma_handle_scope.cpp +++ b/ecmascript/ecma_handle_scope.cpp @@ -25,9 +25,9 @@ EcmaHandleScope::EcmaHandleScope(JSThread *thread) : thread_(thread) prevEnd_ = context->handleScopeStorageEnd_; prevHandleStorageIndex_ = context->currentHandleStorageIndex_; #ifdef ECMASCRIPT_ENABLE_HANDLE_LEAK_CHECK - thread_->HandleScopeCountAdd(); - prevHandleScope_ = thread->GetLastHandleScope(); - thread_->SetLastHandleScope(this); + context->HandleScopeCountAdd(); + prevHandleScope_ = context->GetLastHandleScope(); + context->SetLastHandleScope(this); #endif } @@ -65,14 +65,18 @@ uintptr_t EcmaHandleScope::NewHandle(JSThread *thread, JSTaggedType value) LOG_ECMA(INFO) << stack.str(); } } -#endif -#if ECMASCRIPT_ENABLE_NEW_HANDLE_CHECK - thread->CheckJSTaggedType(value); #endif auto result = context->handleScopeStorageNext_; if (result == context->handleScopeStorageEnd_) { result = reinterpret_cast(context->ExpandHandleStorage()); } +#if ECMASCRIPT_ENABLE_NEW_HANDLE_CHECK + thread->CheckJSTaggedType(value); + if (result == nullptr) { + LOG_ECMA(ERROR) << "result is nullptr, New handle fail!"; + return nullptr; + } +#endif // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) context->handleScopeStorageNext_ = result + 1; *result = value; diff --git a/ecmascript/ecma_macros.h b/ecmascript/ecma_macros.h index 2a8554b7221e645ecaa8c3f2e9a73f872d0af08a..d9a256d0acccfb9e66d3ea19ed0749d2cc9d6030 100644 --- a/ecmascript/ecma_macros.h +++ b/ecmascript/ecma_macros.h @@ -51,9 +51,9 @@ // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define SET_VALUE_WITH_BARRIER(thread, addr, offset, value) \ if ((value).IsHeapObject()) { \ - Barriers::SetObject(thread, addr, offset, (value).GetRawData()); \ + Barriers::SetObject(thread, addr, offset, (value).GetRawData()); \ } else { \ - Barriers::SetPrimitive(addr, offset, (value).GetRawData()); \ + Barriers::SetPrimitive(addr, offset, (value).GetRawData()); \ } // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) @@ -67,31 +67,31 @@ { \ /* Note: We can't statically decide the element type is a primitive or heap object, especially for */ \ /* dynamically-typed languages like JavaScript. So we simply skip the read-barrier. */ \ - return JSTaggedValue(Barriers::GetValue(this, offset)); \ + return JSTaggedValue(Barriers::GetValue(this, offset)); \ } \ template \ void Set##name(const JSThread *thread, JSHandle value, BarrierMode mode = WRITE_BARRIER) \ { \ if (mode == WRITE_BARRIER) { \ if (value.GetTaggedValue().IsHeapObject()) { \ - Barriers::SetObject(thread, this, offset, value.GetTaggedValue().GetRawData()); \ + Barriers::SetObject(thread, this, offset, value.GetTaggedValue().GetRawData()); \ } else { \ - Barriers::SetPrimitive(this, offset, value.GetTaggedValue().GetRawData()); \ + Barriers::SetPrimitive(this, offset, value.GetTaggedValue().GetRawData()); \ } \ } else { \ - Barriers::SetPrimitive(this, offset, value.GetTaggedValue().GetRawData()); \ + Barriers::SetPrimitive(this, offset, value.GetTaggedValue().GetRawData()); \ } \ } \ void Set##name(const JSThread *thread, JSTaggedValue value, BarrierMode mode = WRITE_BARRIER) \ { \ if (mode == WRITE_BARRIER) { \ if (value.IsHeapObject()) { \ - Barriers::SetObject(thread, this, offset, value.GetRawData()); \ + Barriers::SetObject(thread, this, offset, value.GetRawData()); \ } else { \ - Barriers::SetPrimitive(this, offset, value.GetRawData()); \ + Barriers::SetPrimitive(this, offset, value.GetRawData()); \ } \ } else { \ - Barriers::SetPrimitive(this, offset, value.GetRawData()); \ + Barriers::SetPrimitive(this, offset, value.GetRawData()); \ } \ } @@ -105,11 +105,11 @@ static constexpr size_t endOffset = (offset) + sizeof(sizeType); \ inline void Set##name(type value) \ { \ - Barriers::SetPrimitive(this, offset, value); \ + Barriers::SetPrimitive(this, offset, value); \ } \ inline type Get##name() const \ { \ - return Barriers::GetValue(this, offset); \ + return Barriers::GetValue(this, offset); \ } // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) @@ -173,11 +173,11 @@ } // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define RETURN_IF_ABRUPT_COMPLETION(thread) \ - do { \ +#define RETURN_IF_ABRUPT_COMPLETION(thread) \ + do { \ if ((thread)->HasPendingException()) { \ - return; \ - } \ + return; \ + } \ } while (false) // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) @@ -196,6 +196,16 @@ } \ } while (false) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, value) \ + do { \ + if ((thread)->HasPendingException()) { \ + auto ecmaContext = thread->GetCurrentEcmaContext(); \ + ecmaContext->JoinStackPopFastPath(value); \ + return JSTaggedValue::Exception(); \ + } \ + } while (false) + // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define RETURN_HANDLE_IF_ABRUPT_COMPLETION(type, thread) \ do { \ @@ -353,6 +363,16 @@ #define THROW_OOM_ERROR(thread, message) \ THROW_ERROR(thread, ErrorType::OOM_ERROR, message) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define RETURN_STACK_BEFORE_THROW_IF_ASM(thread) \ + do { \ + if ((thread)->IsAsmInterpreter()) { \ + FrameIterator it(const_cast((thread)->GetCurrentSPFrame()), (thread)); \ + it.Advance(); \ + (thread)->SetCurrentSPFrame(it.GetSp()); \ + } \ + } while (false) + // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define RETURN_REJECT_PROMISE_IF_ABRUPT(thread, value, capability) \ do { \ @@ -412,7 +432,7 @@ { \ Dump(std::cout); \ } \ - void DumpForSnapshot(std::vector> &vec) const; + void DumpForSnapshot(std::vector &vec) const; #endif // defined(__cplusplus) @@ -425,47 +445,50 @@ } // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define DECL_VISIT_ARRAY(BEGIN_OFFSET, LENGTH) \ - void VisitRangeSlot(const EcmaObjectRangeVisitor &visitor) \ - { \ - size_t endOffset = (BEGIN_OFFSET) + (LENGTH) * JSTaggedValue::TaggedTypeSize(); \ - visitor(this, ObjectSlot(ToUintPtr(this) + (BEGIN_OFFSET)), ObjectSlot(ToUintPtr(this) + endOffset), false); \ +#define DECL_VISIT_ARRAY(BEGIN_OFFSET, LENGTH) \ + void VisitRangeSlot(const EcmaObjectRangeVisitor &visitor) \ + { \ + size_t endOffset = (BEGIN_OFFSET) + (LENGTH) * JSTaggedValue::TaggedTypeSize(); \ + visitor(this, ObjectSlot(ToUintPtr(this) + (BEGIN_OFFSET)), \ + ObjectSlot(ToUintPtr(this) + endOffset), VisitObjectArea::NORMAL); \ } // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define DECL_VISIT_OBJECT(BEGIN_OFFSET, END_OFFSET) \ - void VisitRangeSlot(const EcmaObjectRangeVisitor &visitor) \ - { \ - visitor(this, ObjectSlot(ToUintPtr(this) + (BEGIN_OFFSET)), \ - ObjectSlot(ToUintPtr(this) + (END_OFFSET)), false); \ +#define DECL_VISIT_OBJECT(BEGIN_OFFSET, END_OFFSET) \ + void VisitRangeSlot(const EcmaObjectRangeVisitor &visitor) \ + { \ + visitor(this, ObjectSlot(ToUintPtr(this) + (BEGIN_OFFSET)), \ + ObjectSlot(ToUintPtr(this) + (END_OFFSET)), VisitObjectArea::NORMAL); \ } // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define DECL_VISIT_NATIVE_FIELD(BEGIN_OFFSET, END_OFFSET) \ - void VisitRangeSlotForNative(const EcmaObjectRangeVisitor &visitor) \ - { \ - visitor(this, ObjectSlot(ToUintPtr(this) + (BEGIN_OFFSET)), ObjectSlot(ToUintPtr(this) + (END_OFFSET)), true); \ +#define DECL_VISIT_NATIVE_FIELD(BEGIN_OFFSET, END_OFFSET) \ + void VisitRangeSlotForNative(const EcmaObjectRangeVisitor &visitor) \ + { \ + visitor(this, ObjectSlot(ToUintPtr(this) + (BEGIN_OFFSET)), \ + ObjectSlot(ToUintPtr(this) + (END_OFFSET)), VisitObjectArea::NATIVE_POINTER); \ } // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define DECL_VISIT_OBJECT_FOR_JS_OBJECT(PARENTCLASS, BEGIN_OFFSET, END_OFFSET) \ - void VisitRangeSlot(const EcmaObjectRangeVisitor &visitor) \ - { \ - VisitObjects(visitor); \ - /* visit in object fields */ \ - auto objSize = this->GetClass()->GetObjectSize(); \ - if (objSize > SIZE) { \ - visitor(this, ObjectSlot(ToUintPtr(this) + SIZE), ObjectSlot(ToUintPtr(this) + objSize), false); \ - } \ - } \ - void VisitObjects(const EcmaObjectRangeVisitor &visitor) \ - { \ - PARENTCLASS::VisitObjects(visitor); \ - if ((BEGIN_OFFSET) == (END_OFFSET)) { \ - return; \ - } \ - visitor(this, ObjectSlot(ToUintPtr(this) + (BEGIN_OFFSET)), \ - ObjectSlot(ToUintPtr(this) + (END_OFFSET)), false); \ +#define DECL_VISIT_OBJECT_FOR_JS_OBJECT(PARENTCLASS, BEGIN_OFFSET, END_OFFSET) \ + void VisitRangeSlot(const EcmaObjectRangeVisitor &visitor) \ + { \ + VisitObjects(visitor); \ + /* visit in object fields */ \ + auto objSize = this->GetClass()->GetObjectSize(); \ + if (objSize > SIZE) { \ + visitor(this, ObjectSlot(ToUintPtr(this) + SIZE), \ + ObjectSlot(ToUintPtr(this) + objSize), VisitObjectArea::IN_OBJECT); \ + } \ + } \ + void VisitObjects(const EcmaObjectRangeVisitor &visitor) \ + { \ + PARENTCLASS::VisitObjects(visitor); \ + if ((BEGIN_OFFSET) == (END_OFFSET)) { \ + return; \ + } \ + visitor(this, ObjectSlot(ToUintPtr(this) + (BEGIN_OFFSET)), \ + ObjectSlot(ToUintPtr(this) + (END_OFFSET)), VisitObjectArea::NORMAL); \ } #if ECMASCRIPT_ENABLE_CAST_CHECK @@ -530,8 +553,8 @@ LOG_FULL(FATAL) << __func__ << ":" << __LINE__ << " begin: " << (begin) << " end: " << (end); \ } -#define CHECK_JS_THREAD(vm) \ - if (!(vm)->GetJSThread()->IsCrossThreadExecutionEnable()) { \ +#define CHECK_JS_THREAD(vm) \ + if (!(vm)->GetJSThread()->IsCrossThreadExecutionEnable()) { \ ASSERT((vm)->GetJSThread()->GetThreadId() == JSThread::GetCurrentThreadId()); \ } diff --git a/ecmascript/ecma_param_configuration.h b/ecmascript/ecma_param_configuration.h index 38a38b97d614695f6c240d2d8c11bf0e826b6d85..826c961aea8f8eebded594e09c717b6296410d08 100644 --- a/ecmascript/ecma_param_configuration.h +++ b/ecmascript/ecma_param_configuration.h @@ -56,12 +56,13 @@ public: defaultSnapshotSpaceSize_ = 512_KB; defaultMachineCodeSpaceSize_ = 2_MB; semiSpaceTriggerConcurrentMark_ = 1_MB; - semiSpaceOvershootSize_ = 2_MB; + semiSpaceStepOvershootSize_ = 2_MB; + oldSpaceOvershootSize_ = 4_MB; outOfMemoryOvershootSize_ = 2_MB; minAllocLimitGrowingStep_ = 2_MB; minGrowingStep_ = 4_MB; maxStackSize_ = 128_KB; - maxJSSerializerSize_ = 16_MB; + maxJSSerializerSize_ = 8_MB; } else if (maxHeapSize_ < HIGH_MEMORY) { // 128_MB ~ 256_MB minSemiSpaceSize_ = 2_MB; maxSemiSpaceSize_ = 8_MB; @@ -70,26 +71,28 @@ public: defaultSnapshotSpaceSize_ = 512_KB; defaultMachineCodeSpaceSize_ = 2_MB; semiSpaceTriggerConcurrentMark_ = 1.5_MB; - semiSpaceOvershootSize_ = 2_MB; + semiSpaceStepOvershootSize_ = 2_MB; + oldSpaceOvershootSize_ = 8_MB; outOfMemoryOvershootSize_ = 2_MB; minAllocLimitGrowingStep_ = 4_MB; minGrowingStep_ = 8_MB; maxStackSize_ = 128_KB; - maxJSSerializerSize_ = 32_MB; + maxJSSerializerSize_ = 16_MB; } else { // 256_MB ~ 384_MB minSemiSpaceSize_ = 2_MB; maxSemiSpaceSize_ = 16_MB; defaultReadOnlySpaceSize_ = 256_KB; - defaultNonMovableSpaceSize_ = 10_MB; + defaultNonMovableSpaceSize_ = 18_MB; defaultSnapshotSpaceSize_ = 4_MB; defaultMachineCodeSpaceSize_ = 8_MB; semiSpaceTriggerConcurrentMark_ = 1.5_MB; - semiSpaceOvershootSize_ = 2_MB; + semiSpaceStepOvershootSize_ = 2_MB; + oldSpaceOvershootSize_ = 8_MB; outOfMemoryOvershootSize_ = 2_MB; minAllocLimitGrowingStep_ = 8_MB; minGrowingStep_ = 16_MB; maxStackSize_ = 128_KB; - maxJSSerializerSize_ = 32_MB; + maxJSSerializerSize_ = 16_MB; } } @@ -133,9 +136,14 @@ public: return semiSpaceTriggerConcurrentMark_; } - size_t GetSemiSpaceOvershootSize() const + size_t GetSemiSpaceStepOvershootSize() const + { + return semiSpaceStepOvershootSize_; + } + + size_t GetOldSpaceOvershootSize() const { - return semiSpaceOvershootSize_; + return oldSpaceOvershootSize_; } size_t GetOutOfMemoryOvershootSize() const @@ -188,7 +196,8 @@ private: size_t defaultSnapshotSpaceSize_ {0}; size_t defaultMachineCodeSpaceSize_ {0}; size_t semiSpaceTriggerConcurrentMark_ {0}; - size_t semiSpaceOvershootSize_ {0}; + size_t semiSpaceStepOvershootSize_ {0}; + size_t oldSpaceOvershootSize_ {0}; size_t outOfMemoryOvershootSize_ {0}; size_t minAllocLimitGrowingStep_ {0}; size_t minGrowingStep_ {0}; diff --git a/ecmascript/ecma_runtime_call_info.h b/ecmascript/ecma_runtime_call_info.h index 7a41f9bdbadb946d630de3561ddd17bb7e18c03e..8e41e5ca474a2d400715ed79a1257e56b58452dc 100644 --- a/ecmascript/ecma_runtime_call_info.h +++ b/ecmascript/ecma_runtime_call_info.h @@ -156,9 +156,9 @@ public: } } - inline void SetCallArg(int32_t argsLength, const TaggedArray* args) + inline void SetCallArg(uint32_t argsLength, const TaggedArray* args) { - for (int32_t i = 0; i < argsLength; i++) { + for (uint32_t i = 0; i < argsLength; i++) { SetCallArg(i, args->Get(GetThread(), i)); } } diff --git a/ecmascript/ecma_string-inl.h b/ecmascript/ecma_string-inl.h index 6d5d7b74894b9a7b2c408e6fe617ef3d92d111c1..1742f99426d90b46c37cbe89fdf502b8b20cb8e5 100644 --- a/ecmascript/ecma_string-inl.h +++ b/ecmascript/ecma_string-inl.h @@ -139,6 +139,13 @@ inline EcmaString *EcmaString::CreateLineStringWithSpaceType(const EcmaVM *vm, s return string; } +inline SlicedString *EcmaString::CreateSlicedString(const EcmaVM *vm, MemSpaceType type) +{ + auto slicedString = SlicedString::Cast(vm->GetFactory()->AllocSlicedStringObject(type)); + slicedString->SetRawHashcode(0); + return slicedString; +} + inline EcmaString *EcmaString::CreateConstantString(const EcmaVM *vm, const uint8_t *utf8Data, size_t length, bool compressed, MemSpaceType type, uint32_t idOffset) { @@ -154,6 +161,7 @@ inline EcmaString *EcmaString::CreateConstantString(const EcmaVM *vm, const uint inline EcmaString *EcmaString::CreateTreeString(const EcmaVM *vm, const JSHandle &left, const JSHandle &right, uint32_t length, bool compressed) { + ECMA_STRING_CHECK_LENGTH_AND_TRHOW(vm, length); auto thread = vm->GetJSThread(); auto string = TreeEcmaString::Cast(vm->GetFactory()->AllocTreeStringObject()); string->SetLength(length, compressed); @@ -167,36 +175,38 @@ inline EcmaString *EcmaString::CreateTreeString(const EcmaVM *vm, EcmaString *EcmaString::FastSubUtf8String(const EcmaVM *vm, const JSHandle &src, uint32_t start, uint32_t length) { - ASSERT(src->IsLineOrConstantString()); - auto string = CreateLineString(vm, length, true); + JSHandle string(vm->GetJSThread(), CreateLineString(vm, length, true)); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + FlatStringInfo srcFlat = FlattenAllString(vm, src); Span dst(string->GetDataUtf8Writable(), length); - Span source(src->GetDataUtf8() + start, length); + Span source(srcFlat.GetDataUtf8() + start, length); EcmaString::MemCopyChars(dst, length, source, length); - ASSERT_PRINT(CanBeCompressed(string), "canBeCompresse does not match the real value!"); - return string; + ASSERT_PRINT(CanBeCompressed(*string), "canBeCompresse does not match the real value!"); + return *string; } /* static */ EcmaString *EcmaString::FastSubUtf16String(const EcmaVM *vm, const JSHandle &src, uint32_t start, uint32_t length) { - ASSERT(src->IsLineOrConstantString()); - bool canBeCompressed = CanBeCompressed(src->GetDataUtf16() + start, length); - auto string = CreateLineString(vm, length, canBeCompressed); + FlatStringInfo srcFlat = FlattenAllString(vm, src); + bool canBeCompressed = CanBeCompressed(srcFlat.GetDataUtf16() + start, length); + JSHandle string(vm->GetJSThread(), CreateLineString(vm, length, canBeCompressed)); + // maybe happen GC,so get srcFlat again + srcFlat = FlattenAllString(vm, src); if (canBeCompressed) { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - CopyChars(string->GetDataUtf8Writable(), src->GetDataUtf16() + start, length); + CopyChars(string->GetDataUtf8Writable(), srcFlat.GetDataUtf16() + start, length); } else { uint32_t len = length * (sizeof(uint16_t) / sizeof(uint8_t)); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) Span dst(string->GetDataUtf16Writable(), length); - Span source(src->GetDataUtf16() + start, length); + Span source(srcFlat.GetDataUtf16() + start, length); EcmaString::MemCopyChars(dst, len, source, len); } - ASSERT_PRINT(canBeCompressed == CanBeCompressed(string), "canBeCompresse does not match the real value!"); - return string; + ASSERT_PRINT(canBeCompressed == CanBeCompressed(*string), "canBeCompresse does not match the real value!"); + return *string; } inline uint16_t *EcmaString::GetData() const @@ -208,10 +218,10 @@ inline uint16_t *EcmaString::GetData() const inline const uint8_t *EcmaString::GetDataUtf8() const { ASSERT_PRINT(IsUtf8(), "EcmaString: Read data as utf8 for utf16 string"); - if (IsConstantString()) { - return ConstantString::Cast(this)->GetConstantData(); + if (IsLineString()) { + return reinterpret_cast(GetData()); } - return reinterpret_cast(GetData()); + return ConstantString::Cast(this)->GetConstantData(); } inline const uint16_t *EcmaString::GetDataUtf16() const @@ -237,11 +247,12 @@ inline uint16_t *EcmaString::GetDataUtf16Writable() inline size_t EcmaString::GetUtf8Length(bool modify) const { - ASSERT(IsLineOrConstantString()); if (!IsUtf16()) { return GetLength() + 1; // add place for zero in the end } - return base::utf_helper::Utf16ToUtf8Size(GetData(), GetLength(), modify); + CVector tmpBuf; + const uint16_t *data = GetUtf16DataFlat(this, tmpBuf); + return base::utf_helper::Utf16ToUtf8Size(data, GetLength(), modify); } template @@ -253,12 +264,18 @@ inline uint16_t EcmaString::At(int32_t index) const return 0; } } - if (IsLineString()) { - return LineEcmaString::Cast(this)->Get(index); - } else if (IsConstantString()) { - return ConstantString::Cast(this)->Get(index); - } else { - return TreeEcmaString::Cast(this)->Get(index); + switch (GetStringType()) { + case JSType::LINE_STRING: + return LineEcmaString::Cast(this)->Get(index); + case JSType::CONSTANT_STRING: + return ConstantString::Cast(this)->Get(index); + case JSType::SLICED_STRING: + return SlicedString::Cast(this)->Get(index); + case JSType::TREE_STRING: + return TreeEcmaString::Cast(this)->Get(index); + default: + LOG_ECMA(FATAL) << "this branch is unreachable"; + UNREACHABLE(); } } @@ -336,6 +353,15 @@ void EcmaString::WriteToFlat(EcmaString *src, Char *buf, uint32_t maxLength) } continue; } + case JSType::SLICED_STRING: { + EcmaString *parent = EcmaString::Cast(SlicedString::Cast(src)->GetParent()); + if (src->IsUtf8()) { + CopyChars(buf, parent->GetDataUtf8() + SlicedString::Cast(src)->GetStartIndex(), length); + } else { + CopyChars(buf, parent->GetDataUtf16() + SlicedString::Cast(src)->GetStartIndex(), length); + } + return; + } default: LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); @@ -343,6 +369,21 @@ void EcmaString::WriteToFlat(EcmaString *src, Char *buf, uint32_t maxLength) } } +inline const uint8_t *FlatStringInfo::GetDataUtf8() const +{ + return string_->GetDataUtf8() + startIndex_; +} + +inline const uint16_t *FlatStringInfo::GetDataUtf16() const +{ + return string_->GetDataUtf16() + startIndex_; +} + +inline uint8_t *FlatStringInfo::GetDataUtf8Writable() const +{ + return string_->GetDataUtf8Writable() + startIndex_; +} + inline const uint8_t *EcmaStringAccessor::GetDataUtf8() { return string_->GetDataUtf8(); diff --git a/ecmascript/ecma_string.cpp b/ecmascript/ecma_string.cpp index c793807354d1e72c61dc00b8b765bc51adeb305f..66f190bfae3954288b49d5cf6f1a0cf6045a334b 100644 --- a/ecmascript/ecma_string.cpp +++ b/ecmascript/ecma_string.cpp @@ -19,35 +19,44 @@ #include "ecmascript/mem/c_containers.h" namespace panda::ecmascript { -static constexpr int SMALL_STRING_SIZE = 128; EcmaString *EcmaString::Concat(const EcmaVM *vm, - const JSHandle &left, const JSHandle &right) + const JSHandle &left, const JSHandle &right, MemSpaceType type) { // allocator may trig gc and move src, need to hold it EcmaString *strLeft = *left; EcmaString *strRight = *right; uint32_t leftLength = strLeft->GetLength(); - if (leftLength == 0) { - return strRight; - } uint32_t rightLength = strRight->GetLength(); - if (rightLength == 0) { - return strLeft; - } - uint32_t newLength = leftLength + rightLength; if (newLength == 0) { return vm->GetFactory()->GetEmptyString().GetObject(); } - bool compressed = (strLeft->IsUtf8() && strRight->IsUtf8()); - + if (leftLength == 0) { + if (type == MemSpaceType::OLD_SPACE) { + Region *objectRegion = Region::ObjectAddressToRange(reinterpret_cast(*right)); + if (objectRegion->InYoungSpace()) { + return CopyStringToOldSpace(vm, right, rightLength, strRight->IsUtf8()); + } + } + return strRight; + } + if (rightLength == 0) { + if (type == MemSpaceType::OLD_SPACE) { + Region *objectRegion = Region::ObjectAddressToRange(reinterpret_cast(*left)); + if (objectRegion->InYoungSpace()) { + return CopyStringToOldSpace(vm, left, leftLength, strLeft->IsUtf8()); + } + } + return strLeft; + } // if the result string is small, make a LineString + bool compressed = (strLeft->IsUtf8() && strRight->IsUtf8()); if (newLength < TreeEcmaString::MIN_TREE_ECMASTRING_LENGTH) { ASSERT(strLeft->IsLineOrConstantString()); ASSERT(strRight->IsLineOrConstantString()); - auto newString = CreateLineString(vm, newLength, compressed); + auto newString = CreateLineStringWithSpaceType(vm, newLength, compressed, type); // retrieve strings after gc strLeft = *left; strRight = *right; @@ -84,6 +93,35 @@ EcmaString *EcmaString::Concat(const EcmaVM *vm, return CreateTreeString(vm, left, right, newLength, compressed); } +/* static */ +EcmaString *EcmaString::CopyStringToOldSpace(const EcmaVM *vm, const JSHandle &original, + uint32_t length, bool compressed) +{ + if (original->IsConstantString()) { + return CreateConstantString(vm, original->GetDataUtf8(), length, MemSpaceType::OLD_SPACE); + } + JSHandle newString(vm->GetJSThread(), + CreateLineStringWithSpaceType(vm, length, compressed, MemSpaceType::OLD_SPACE)); + auto strOrigin = FlattenAllString(vm, original); + if (compressed) { + // copy + Span sp(newString->GetDataUtf8Writable(), length); + Span srcSp(strOrigin.GetDataUtf8(), length); + EcmaString::MemCopyChars(sp, length, srcSp, length); + } else { + // copy left part + Span sp(newString->GetDataUtf16Writable(), length); + if (strOrigin.IsUtf8()) { + EcmaString::CopyChars(sp.data(), strOrigin.GetDataUtf8(), length); + } else { + Span srcSp(strOrigin.GetDataUtf16(), length); + EcmaString::MemCopyChars(sp, length << 1U, srcSp, length << 1U); + } + } + ASSERT_PRINT(compressed == CanBeCompressed(*newString), "compressed does not match the real value!"); + return *newString; +} + /* static */ EcmaString *EcmaString::FastSubString(const EcmaVM *vm, const JSHandle &src, uint32_t start, uint32_t length) @@ -95,11 +133,26 @@ EcmaString *EcmaString::FastSubString(const EcmaVM *vm, if (start == 0 && length == src->GetLength()) { return *src; } - auto srcFlat = JSHandle(vm->GetJSThread(), Flatten(vm, src)); - if (srcFlat->IsUtf8()) { - return FastSubUtf8String(vm, srcFlat, start, length); + if (src->IsUtf8()) { + return FastSubUtf8String(vm, src, start, length); + } + return FastSubUtf16String(vm, src, start, length); +} + +/* static */ +EcmaString *EcmaString::GetSlicedString(const EcmaVM *vm, + const JSHandle &src, uint32_t start, uint32_t length) +{ + ASSERT((start + length) <= src->GetLength()); + if (start == 0 && length == src->GetLength()) { + return *src; } - return FastSubUtf16String(vm, srcFlat, start, length); + JSHandle slicedString(vm->GetJSThread(), CreateSlicedString(vm)); + FlatStringInfo srcFlat = FlattenAllString(vm, src); + slicedString->SetLength(length, srcFlat.GetString()->IsUtf8()); + slicedString->SetParent(vm->GetJSThread(), JSTaggedValue(srcFlat.GetString())); + slicedString->SetStartIndex(start + srcFlat.GetStartIndex()); + return *slicedString; } void EcmaString::WriteData(EcmaString *src, uint32_t start, uint32_t destSize, uint32_t length) @@ -153,38 +206,38 @@ int32_t EcmaString::Compare(const EcmaVM *vm, const JSHandle &left, if (*left == *right) { return 0; } - auto leftFlat = JSHandle(vm->GetJSThread(), Flatten(vm, left)); - auto rightFlat = JSHandle(vm->GetJSThread(), Flatten(vm, right)); - EcmaString *lhs = *leftFlat; - EcmaString *rhs = *rightFlat; - int32_t lhsCount = static_cast(lhs->GetLength()); - int32_t rhsCount = static_cast(rhs->GetLength()); + FlatStringInfo lhs = FlattenAllString(vm, left); + JSHandle string(vm->GetJSThread(), lhs.GetString()); + FlatStringInfo rhs = FlattenAllString(vm, right); + lhs.SetString(*string); + int32_t lhsCount = static_cast(lhs.GetLength()); + int32_t rhsCount = static_cast(rhs.GetLength()); int32_t countDiff = lhsCount - rhsCount; int32_t minCount = (countDiff < 0) ? lhsCount : rhsCount; - if (!lhs->IsUtf16() && !rhs->IsUtf16()) { - Span lhsSp(lhs->GetDataUtf8(), lhsCount); - Span rhsSp(rhs->GetDataUtf8(), rhsCount); + if (!lhs.IsUtf16() && !rhs.IsUtf16()) { + Span lhsSp(lhs.GetDataUtf8(), lhsCount); + Span rhsSp(rhs.GetDataUtf8(), rhsCount); int32_t charDiff = CompareStringSpan(lhsSp, rhsSp, minCount); if (charDiff != 0) { return charDiff; } - } else if (!lhs->IsUtf16()) { - Span lhsSp(lhs->GetDataUtf8(), lhsCount); - Span rhsSp(rhs->GetDataUtf16(), rhsCount); + } else if (!lhs.IsUtf16()) { + Span lhsSp(lhs.GetDataUtf8(), lhsCount); + Span rhsSp(rhs.GetDataUtf16(), rhsCount); int32_t charDiff = CompareStringSpan(lhsSp, rhsSp, minCount); if (charDiff != 0) { return charDiff; } - } else if (!rhs->IsUtf16()) { - Span lhsSp(lhs->GetDataUtf16(), rhsCount); - Span rhsSp(rhs->GetDataUtf8(), lhsCount); + } else if (!rhs.IsUtf16()) { + Span lhsSp(lhs.GetDataUtf16(), rhsCount); + Span rhsSp(rhs.GetDataUtf8(), lhsCount); int32_t charDiff = CompareStringSpan(lhsSp, rhsSp, minCount); if (charDiff != 0) { return charDiff; } } else { - Span lhsSp(lhs->GetDataUtf16(), lhsCount); - Span rhsSp(rhs->GetDataUtf16(), rhsCount); + Span lhsSp(lhs.GetDataUtf16(), lhsCount); + Span rhsSp(rhs.GetDataUtf16(), rhsCount); int32_t charDiff = CompareStringSpan(lhsSp, rhsSp, minCount); if (charDiff != 0) { return charDiff; @@ -250,13 +303,13 @@ int32_t EcmaString::LastIndexOf(Span &lhsSp, Span &rhsSp, in int32_t EcmaString::IndexOf(const EcmaVM *vm, const JSHandle &receiver, const JSHandle &search, int pos) { - EcmaString *lhs = *receiver; - EcmaString *rhs = *search; - if (lhs == nullptr || rhs == nullptr) { + EcmaString *lhstring = *receiver; + EcmaString *rhstring = *search; + if (lhstring == nullptr || rhstring == nullptr) { return -1; } - int32_t lhsCount = static_cast(lhs->GetLength()); - int32_t rhsCount = static_cast(rhs->GetLength()); + int32_t lhsCount = static_cast(lhstring->GetLength()); + int32_t rhsCount = static_cast(rhstring->GetLength()); if (pos > lhsCount) { return -1; @@ -279,24 +332,24 @@ int32_t EcmaString::IndexOf(const EcmaVM *vm, return -1; } - auto receiverFlat = JSHandle(vm->GetJSThread(), Flatten(vm, receiver)); - auto searchFlat = JSHandle(vm->GetJSThread(), Flatten(vm, search)); - lhs = *receiverFlat; - rhs = *searchFlat; + FlatStringInfo lhs = FlattenAllString(vm, receiver); + JSHandle string(vm->GetJSThread(), lhs.GetString()); + FlatStringInfo rhs = FlattenAllString(vm, search); + lhs.SetString(*string); - if (rhs->IsUtf8() && lhs->IsUtf8()) { - Span lhsSp(lhs->GetDataUtf8(), lhsCount); - Span rhsSp(rhs->GetDataUtf8(), rhsCount); + if (rhs.IsUtf8() && lhs.IsUtf8()) { + Span lhsSp(lhs.GetDataUtf8(), lhsCount); + Span rhsSp(rhs.GetDataUtf8(), rhsCount); return EcmaString::IndexOf(lhsSp, rhsSp, pos, max); - } else if (rhs->IsUtf16() && lhs->IsUtf16()) { // NOLINT(readability-else-after-return) - Span lhsSp(lhs->GetDataUtf16(), lhsCount); - Span rhsSp(rhs->GetDataUtf16(), rhsCount); + } else if (rhs.IsUtf16() && lhs.IsUtf16()) { // NOLINT(readability-else-after-return) + Span lhsSp(lhs.GetDataUtf16(), lhsCount); + Span rhsSp(rhs.GetDataUtf16(), rhsCount); return EcmaString::IndexOf(lhsSp, rhsSp, pos, max); - } else if (rhs->IsUtf16()) { + } else if (rhs.IsUtf16()) { return -1; } else { // NOLINT(readability-else-after-return) - Span lhsSp(lhs->GetDataUtf16(), lhsCount); - Span rhsSp(rhs->GetDataUtf8(), rhsCount); + Span lhsSp(lhs.GetDataUtf16(), lhsCount); + Span rhsSp(rhs.GetDataUtf8(), rhsCount); return EcmaString::IndexOf(lhsSp, rhsSp, pos, max); } } @@ -304,14 +357,14 @@ int32_t EcmaString::IndexOf(const EcmaVM *vm, int32_t EcmaString::LastIndexOf(const EcmaVM *vm, const JSHandle &receiver, const JSHandle &search, int pos) { - EcmaString *lhs = *receiver; - EcmaString *rhs = *search; - if (lhs == nullptr || rhs == nullptr) { + EcmaString *lhstring = *receiver; + EcmaString *rhstring = *search; + if (lhstring == nullptr || rhstring == nullptr) { return -1; } - int32_t lhsCount = static_cast(lhs->GetLength()); - int32_t rhsCount = static_cast(rhs->GetLength()); + int32_t lhsCount = static_cast(lhstring->GetLength()); + int32_t rhsCount = static_cast(rhstring->GetLength()); if (lhsCount < rhsCount) { return -1; } @@ -332,24 +385,23 @@ int32_t EcmaString::LastIndexOf(const EcmaVM *vm, return pos; } - auto receiverFlat = JSHandle(vm->GetJSThread(), Flatten(vm, receiver)); - auto searchFlat = JSHandle(vm->GetJSThread(), Flatten(vm, search)); - lhs = *receiverFlat; - rhs = *searchFlat; - - if (rhs->IsUtf8() && lhs->IsUtf8()) { - Span lhsSp(lhs->GetDataUtf8(), lhsCount); - Span rhsSp(rhs->GetDataUtf8(), rhsCount); + FlatStringInfo lhs = FlattenAllString(vm, receiver); + JSHandle string(vm->GetJSThread(), lhs.GetString()); + FlatStringInfo rhs = FlattenAllString(vm, search); + lhs.SetString(*string); + if (rhs.IsUtf8() && lhs.IsUtf8()) { + Span lhsSp(lhs.GetDataUtf8(), lhsCount); + Span rhsSp(rhs.GetDataUtf8(), rhsCount); return EcmaString::LastIndexOf(lhsSp, rhsSp, pos); - } else if (rhs->IsUtf16() && lhs->IsUtf16()) { // NOLINT(readability-else-after-return) - Span lhsSp(lhs->GetDataUtf16(), lhsCount); - Span rhsSp(rhs->GetDataUtf16(), rhsCount); + } else if (rhs.IsUtf16() && lhs.IsUtf16()) { // NOLINT(readability-else-after-return) + Span lhsSp(lhs.GetDataUtf16(), lhsCount); + Span rhsSp(rhs.GetDataUtf16(), rhsCount); return EcmaString::LastIndexOf(lhsSp, rhsSp, pos); - } else if (rhs->IsUtf16()) { + } else if (rhs.IsUtf16()) { return -1; } else { // NOLINT(readability-else-after-return) - Span lhsSp(lhs->GetDataUtf16(), lhsCount); - Span rhsSp(rhs->GetDataUtf8(), rhsCount); + Span lhsSp(lhs.GetDataUtf16(), lhsCount); + Span rhsSp(rhs.GetDataUtf8(), rhsCount); return EcmaString::LastIndexOf(lhsSp, rhsSp, pos); } } @@ -413,52 +465,85 @@ bool EcmaString::CanBeCompressed(const uint16_t *utf16Data, uint32_t utf16Len) bool EcmaString::EqualToSplicedString(const EcmaString *str1, const EcmaString *str2) { - ASSERT(IsLineOrConstantString()); - ASSERT(str1->IsLineOrConstantString() && str2->IsLineOrConstantString()); + ASSERT(NotTreeString()); + ASSERT(str1->NotTreeString() && str2->NotTreeString()); if (GetLength() != str1->GetLength() + str2->GetLength()) { return false; } if (IsUtf16()) { - if (str1->IsUtf8() && str2->IsUtf8()) { - return false; - } - if (EcmaString::StringsAreEqualUtf16(str1, GetDataUtf16(), str1->GetLength())) { - return EcmaString::StringsAreEqualUtf16(str2, GetDataUtf16() + str1->GetLength(), str2->GetLength()); + CVector buf; + const uint16_t *data = EcmaString::GetUtf16DataFlat(this, buf); + if (EcmaString::StringsAreEqualUtf16(str1, data, str1->GetLength())) { + return EcmaString::StringsAreEqualUtf16(str2, data + str1->GetLength(), str2->GetLength()); } } else { - if (str1->IsUtf16() || str2->IsUtf16()) { - return false; - } - Span concatData(GetDataUtf8(), str1->GetLength()); - Span data1(str1->GetDataUtf8(), str1->GetLength()); - if (EcmaString::StringsAreEquals(concatData, data1)) { - concatData = Span(GetDataUtf8() + str1->GetLength(), str2->GetLength()); - Span data2(str2->GetDataUtf8(), str2->GetLength()); - return EcmaString::StringsAreEquals(concatData, data2); + CVector buf; + const uint8_t *data = EcmaString::GetUtf8DataFlat(this, buf); + if (EcmaString::StringIsEqualUint8Data(str1, data, str1->GetLength(), this->IsUtf8())) { + return EcmaString::StringIsEqualUint8Data(str2, data + str1->GetLength(), + str2->GetLength(), this->IsUtf8()); } } return false; } /* static */ -bool EcmaString::StringsAreEqualSameUtfEncoding(EcmaString *str1, EcmaString *str2) -{ - if (str1->IsUtf16()) { - CVector buf1; - CVector buf2; - const uint16_t *data1 = EcmaString::GetUtf16DataFlat(str1, buf1); - const uint16_t *data2 = EcmaString::GetUtf16DataFlat(str2, buf2); - Span sp1(data1, str1->GetLength()); - Span sp2(data2, str2->GetLength()); - return EcmaString::StringsAreEquals(sp1, sp2); - } else { // NOLINT(readability-else-after-return) - CVector buf1; - CVector buf2; - const uint8_t *data1 = EcmaString::GetUtf8DataFlat(str1, buf1); - const uint8_t *data2 = EcmaString::GetUtf8DataFlat(str2, buf2); - Span sp1(data1, str1->GetLength()); - Span sp2(data2, str2->GetLength()); - return EcmaString::StringsAreEquals(sp1, sp2); +bool EcmaString::StringsAreEqualDiffUtfEncoding(EcmaString *left, EcmaString *right) +{ + CVector bufLeftUft16; + CVector bufRightUft16; + CVector bufLeftUft8; + CVector bufRightUft8; + int32_t lhsCount = static_cast(left->GetLength()); + int32_t rhsCount = static_cast(right->GetLength()); + if (!left->IsUtf16() && !right->IsUtf16()) { + const uint8_t *data1 = EcmaString::GetUtf8DataFlat(left, bufLeftUft8); + const uint8_t *data2 = EcmaString::GetUtf8DataFlat(right, bufRightUft8); + Span lhsSp(data1, lhsCount); + Span rhsSp(data2, rhsCount); + return EcmaString::StringsAreEquals(lhsSp, rhsSp); + } else if (!left->IsUtf16()) { + const uint8_t *data1 = EcmaString::GetUtf8DataFlat(left, bufLeftUft8); + const uint16_t *data2 = EcmaString::GetUtf16DataFlat(right, bufRightUft16); + Span lhsSp(data1, lhsCount); + Span rhsSp(data2, rhsCount); + return EcmaString::StringsAreEquals(lhsSp, rhsSp); + } else if (!right->IsUtf16()) { + const uint16_t *data1 = EcmaString::GetUtf16DataFlat(left, bufLeftUft16); + const uint8_t *data2 = EcmaString::GetUtf8DataFlat(right, bufRightUft8); + Span lhsSp(data1, lhsCount); + Span rhsSp(data2, rhsCount); + return EcmaString::StringsAreEquals(lhsSp, rhsSp); + } else { + const uint16_t *data1 = EcmaString::GetUtf16DataFlat(left, bufLeftUft16); + const uint16_t *data2 = EcmaString::GetUtf16DataFlat(right, bufRightUft16); + Span lhsSp(data1, lhsCount); + Span rhsSp(data2, rhsCount); + return EcmaString::StringsAreEquals(lhsSp, rhsSp); + } +} + +/* static */ +bool EcmaString::StringsAreEqualDiffUtfEncoding(const FlatStringInfo &left, const FlatStringInfo &right) +{ + int32_t lhsCount = static_cast(left.GetLength()); + int32_t rhsCount = static_cast(right.GetLength()); + if (!left.IsUtf16() && !right.IsUtf16()) { + Span lhsSp(left.GetDataUtf8(), lhsCount); + Span rhsSp(right.GetDataUtf8(), rhsCount); + return EcmaString::StringsAreEquals(lhsSp, rhsSp); + } else if (!left.IsUtf16()) { + Span lhsSp(left.GetDataUtf8(), lhsCount); + Span rhsSp(right.GetDataUtf16(), rhsCount); + return EcmaString::StringsAreEquals(lhsSp, rhsSp); + } else if (!right.IsUtf16()) { + Span lhsSp(left.GetDataUtf16(), rhsCount); + Span rhsSp(right.GetDataUtf8(), lhsCount); + return EcmaString::StringsAreEquals(lhsSp, rhsSp); + } else { + Span lhsSp(left.GetDataUtf16(), lhsCount); + Span rhsSp(right.GetDataUtf16(), rhsCount); + return EcmaString::StringsAreEquals(lhsSp, rhsSp); } } @@ -467,9 +552,6 @@ bool EcmaString::StringsAreEqual(const EcmaVM *vm, const JSHandle &s if (str1 == str2) { return true; } - if (str1->IsUtf16() != str2->IsUtf16()) { - return false; - } uint32_t str1Len = str1->GetLength(); if (str1Len != str2->GetLength()) { return false; @@ -485,10 +567,11 @@ bool EcmaString::StringsAreEqual(const EcmaVM *vm, const JSHandle &s return false; } } - - auto str1Flat = JSHandle(vm->GetJSThread(), Flatten(vm, str1)); - auto str2Flat = JSHandle(vm->GetJSThread(), Flatten(vm, str2)); - return StringsAreEqualSameUtfEncoding(*str1Flat, *str2Flat); + FlatStringInfo str1Flat = FlattenAllString(vm, str1); + JSHandle string(vm->GetJSThread(), str1Flat.GetString()); + FlatStringInfo str2Flat = FlattenAllString(vm, str2); + str1Flat.SetString(*string); + return StringsAreEqualDiffUtfEncoding(str1Flat, str2Flat); } /* static */ @@ -497,9 +580,6 @@ bool EcmaString::StringsAreEqual(EcmaString *str1, EcmaString *str2) if (str1 == str2) { return true; } - if (str1->IsUtf16() != str2->IsUtf16()) { - return false; - } uint32_t str1Len = str1->GetLength(); if (str1Len != str2->GetLength()) { return false; @@ -515,29 +595,29 @@ bool EcmaString::StringsAreEqual(EcmaString *str1, EcmaString *str2) return false; } } - return StringsAreEqualSameUtfEncoding(str1, str2); + return StringsAreEqualDiffUtfEncoding(str1, str2); } /* static */ -bool EcmaString::StringsAreEqualUtf8(const EcmaString *str1, const uint8_t *utf8Data, uint32_t utf8Len, - bool canBeCompress) +bool EcmaString::StringIsEqualUint8Data(const EcmaString *str1, const uint8_t *dataAddr, uint32_t dataLen, + bool canBeCompressToUtf8) { - if (canBeCompress != str1->IsUtf8()) { + if (!str1->IsSlicedString() && canBeCompressToUtf8 != str1->IsUtf8()) { return false; } - if (canBeCompress && str1->GetLength() != utf8Len) { + if (canBeCompressToUtf8 && str1->GetLength() != dataLen) { return false; } - if (canBeCompress) { + if (str1->IsUtf8()) { CVector buf; - Span data1(EcmaString::GetUtf8DataFlat(str1, buf), utf8Len); - Span data2(utf8Data, utf8Len); + Span data1(EcmaString::GetUtf8DataFlat(str1, buf), dataLen); + Span data2(dataAddr, dataLen); return EcmaString::StringsAreEquals(data1, data2); } CVector buf; uint32_t length = str1->GetLength(); const uint16_t *data = EcmaString::GetUtf16DataFlat(str1, buf); - return IsUtf8EqualsUtf16(utf8Data, utf8Len, data, length); + return IsUtf8EqualsUtf16(dataAddr, dataLen, data, length); } /* static */ @@ -559,23 +639,6 @@ bool EcmaString::StringsAreEqualUtf16(const EcmaString *str1, const uint16_t *ut } } -/* static */ -template -bool EcmaString::StringsAreEquals(Span &str1, Span &str2) -{ - ASSERT(str1.Size() <= str2.Size()); - size_t size = str1.Size(); - if (size < SMALL_STRING_SIZE) { - for (size_t i = 0; i < size; i++) { - if (str1[i] != str2[i]) { - return false; - } - } - return true; - } - return !memcmp(str1.data(), str2.data(), size); -} - template bool EcmaString::MemCopyChars(Span &dst, size_t dstMax, Span &src, size_t count) { @@ -588,18 +651,6 @@ bool EcmaString::MemCopyChars(Span &dst, size_t dstMax, Span &src, s return true; } -template -static uint32_t ComputeHashForData(const T *data, size_t size, uint32_t hashSeed) -{ - uint32_t hash = hashSeed; - Span sp(data, size); - for (auto c : sp) { - constexpr size_t SHIFT = 5; - hash = (hash << SHIFT) - hash + c; - } - return hash; -} - uint32_t EcmaString::ComputeHashcode(uint32_t hashSeed) const { uint32_t hash; @@ -643,17 +694,27 @@ uint32_t EcmaString::ComputeHashcodeUtf16(const uint16_t *utf16Data, uint32_t le bool EcmaString::IsUtf8EqualsUtf16(const uint8_t *utf8Data, size_t utf8Len, const uint16_t *utf16Data, uint32_t utf16Len) { - // length is one more than compared utf16Data, don't need convert all utf8Data to utf16Data - uint32_t utf8ConvertLength = utf16Len + 1; - CVector tmpBuffer(utf8ConvertLength); - auto len = base::utf_helper::ConvertRegionUtf8ToUtf16(utf8Data, tmpBuffer.data(), utf8Len, utf8ConvertLength, 0); - if (len != utf16Len) { - return false; + size_t utf8Pos = 0; + size_t utf16Pos = 0; + while (utf8Pos < utf8Len) { + auto [pair, nbytes] = utf::ConvertMUtf8ToUtf16Pair(utf8Data, utf8Len - utf8Pos); + auto [pHigh, pLow] = utf::SplitUtf16Pair(pair); + utf8Data += nbytes; + utf8Pos += nbytes; + if (pHigh != 0) { + if (utf16Pos >= utf16Len - 1 || *utf16Data != pHigh) { + return false; + } + ++utf16Pos; + ++utf16Data; + } + if (utf16Pos >= utf16Len || *utf16Data != pLow) { + return false; + } + ++utf16Pos; + ++utf16Data; } - - Span data1(tmpBuffer.data(), len); - Span data2(utf16Data, utf16Len); - return EcmaString::StringsAreEquals(data1, data2); + return true; } bool EcmaString::ToElementIndex(uint32_t *index) @@ -674,24 +735,64 @@ bool EcmaString::ToElementIndex(uint32_t *index) *index = 0; return len == 1; } - if (c > '0' && c <= '9') { - n = c - '0'; - for (uint32_t i = 1; i < len; i++) { - c = data[i]; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - if (c < '0' || c > '9') { - return false; - } - // NOLINTNEXTLINE(readability-magic-numbers) - n = n * 10 + (c - '0'); // 10: decimal factor - } - if (n < JSObject::MAX_ELEMENT_INDEX) { - *index = n; - return true; - } + uint32_t loopStart = 0; + if (ToUInt64FromLoopStart(&n, loopStart, data) && n < JSObject::MAX_ELEMENT_INDEX) { + *index = n; + return true; + } + return false; +} + +bool EcmaString::ToInt(int32_t *index) +{ + uint32_t len = GetLength(); + if (UNLIKELY(len == 0 || len > MAX_ELEMENT_INDEX_LEN)) { // NOLINTNEXTLINEreadability-magic-numbers) + return false; + } + if (UNLIKELY(IsUtf16())) { + return false; + } + bool negative = false; + CVector buf; + const uint8_t *data = EcmaString::GetUtf8DataFlat(this, buf); + uint32_t c = data[0]; + uint32_t loopStart = 0; + uint64_t n = 0; + if (c == '0') { + *index = 0; + return len == 1; + } + if(c == '-' && len > 1){ + negative = true; + loopStart = 1; + } + + if (ToUInt64FromLoopStart(&n, loopStart, data) && n < JSObject::MAX_ELEMENT_INDEX) { + *index = negative ? -n : n; + return true; } return false; } +bool EcmaString::ToUInt64FromLoopStart(uint64_t *index, uint32_t loopStart, const uint8_t *data) +{ + uint64_t n = 0; + uint32_t len = GetLength(); + if (UNLIKELY(loopStart >= len)) { + return false; + } + for (uint32_t i = loopStart; i < len; i++) { + uint32_t c = data[i]; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (c < '0' || c > '9') { + return false; + } + // NOLINTNEXTLINE(readability-magic-numbers) + n = n * 10 + (c - '0'); // 10: decimal factor + } + *index = n; + return true; +} + bool EcmaString::ToTypedArrayIndex(uint32_t *index) { uint32_t len = GetLength(); @@ -743,10 +844,10 @@ EcmaString *EcmaString::TrimBody(const JSThread *thread, const JSHandleGetLength(); int32_t start = 0; - int32_t end = srcLen - 1; + int32_t end = static_cast(srcLen) - 1; if (mode == TrimMode::TRIM || mode == TrimMode::TRIM_START) { - start = base::StringHelper::GetStart(data, srcLen); + start = static_cast(base::StringHelper::GetStart(data, srcLen)); } if (mode == TrimMode::TRIM || mode == TrimMode::TRIM_END) { end = base::StringHelper::GetEnd(data, start, srcLen); @@ -758,27 +859,27 @@ EcmaString *EcmaString::TrimBody(const JSThread *thread, const JSHandle &src) { - auto srcFlat = JSHandle(vm->GetJSThread(), Flatten(vm, src)); - uint32_t srcLength = srcFlat->GetLength(); + auto srcFlat = FlattenAllString(vm, src); + uint32_t srcLength = srcFlat.GetLength(); auto factory = vm->GetFactory(); - if (srcFlat->IsUtf16()) { - std::u16string u16str = base::StringHelper::Utf16ToU16String(srcFlat->GetDataUtf16(), srcLength); + if (srcFlat.IsUtf16()) { + std::u16string u16str = base::StringHelper::Utf16ToU16String(srcFlat.GetDataUtf16(), srcLength); std::string res = base::StringHelper::ToLower(u16str); return *(factory->NewFromStdString(res)); } else { - return ConvertUtf8ToLowerOrUpper(vm, srcFlat, true); + return ConvertUtf8ToLowerOrUpper(vm, src, true); } } /* static */ EcmaString *EcmaString::TryToLower(const EcmaVM *vm, const JSHandle &src) { - auto srcFlat = JSHandle(vm->GetJSThread(), Flatten(vm, src)); - uint32_t srcLength = srcFlat->GetLength(); + auto srcFlat = FlattenAllString(vm, src); + uint32_t srcLength = srcFlat.GetLength(); const char start = 'A'; const char end = 'Z'; uint32_t upperIndex = srcLength; - Span data(srcFlat->GetDataUtf8Writable(), srcLength); + Span data(srcFlat.GetDataUtf8Writable(), srcLength); for (uint32_t index = 0; index < srcLength; ++index) { if (base::StringHelper::Utf8CharInRange(data[index], start, end)) { upperIndex = index; @@ -788,18 +889,19 @@ EcmaString *EcmaString::TryToLower(const EcmaVM *vm, const JSHandle if (upperIndex == srcLength) { return *src; } - return ConvertUtf8ToLowerOrUpper(vm, srcFlat, true, upperIndex); + return ConvertUtf8ToLowerOrUpper(vm, src, true, upperIndex); } /* static */ -EcmaString *EcmaString::ConvertUtf8ToLowerOrUpper(const EcmaVM *vm, const JSHandle &srcFlat, +EcmaString *EcmaString::ConvertUtf8ToLowerOrUpper(const EcmaVM *vm, const JSHandle &src, bool toLower, uint32_t startIndex) { const char start = toLower ? 'A' : 'a'; const char end = toLower ? 'Z' : 'z'; - uint32_t srcLength = srcFlat->GetLength(); - auto newString = CreateLineString(vm, srcLength, true); - Span data(srcFlat->GetDataUtf8Writable(), srcLength); + uint32_t srcLength = src->GetLength(); + JSHandle newString(vm->GetJSThread(), CreateLineString(vm, srcLength, true)); + auto srcFlat = FlattenAllString(vm, src); + Span data(srcFlat.GetDataUtf8Writable(), srcLength); auto newStringPtr = newString->GetDataUtf8Writable(); if (startIndex > 0) { if (memcpy_s(newStringPtr, startIndex * sizeof(uint8_t), data.data(), startIndex * sizeof(uint8_t)) != EOK) { @@ -814,21 +916,21 @@ EcmaString *EcmaString::ConvertUtf8ToLowerOrUpper(const EcmaVM *vm, const JSHand *(newStringPtr + index) = data[index]; } } - return newString; + return *newString; } /* static */ EcmaString *EcmaString::ToUpper(const EcmaVM *vm, const JSHandle &src) { - auto srcFlat = JSHandle(vm->GetJSThread(), Flatten(vm, src)); - uint32_t srcLength = srcFlat->GetLength(); + FlatStringInfo srcFlat = FlattenAllString(vm, src); + uint32_t srcLength = srcFlat.GetLength(); auto factory = vm->GetFactory(); - if (srcFlat->IsUtf16()) { - std::u16string u16str = base::StringHelper::Utf16ToU16String(srcFlat->GetDataUtf16(), srcLength); + if (srcFlat.IsUtf16()) { + std::u16string u16str = base::StringHelper::Utf16ToU16String(srcFlat.GetDataUtf16(), srcLength); std::string res = base::StringHelper::ToUpper(u16str); return *(factory->NewFromStdString(res)); } else { - return ConvertUtf8ToLowerOrUpper(vm, srcFlat, false); + return ConvertUtf8ToLowerOrUpper(vm, src, false); } } @@ -836,8 +938,8 @@ EcmaString *EcmaString::ToUpper(const EcmaVM *vm, const JSHandle &sr EcmaString *EcmaString::ToLocaleLower(const EcmaVM *vm, const JSHandle &src, const icu::Locale &locale) { auto factory = vm->GetFactory(); - auto srcFlat = JSHandle(vm->GetJSThread(), Flatten(vm, src)); - std::u16string utf16 = srcFlat->ToU16String(); + FlatStringInfo srcFlat = FlattenAllString(vm, src); + std::u16string utf16 = srcFlat.ToU16String(); std::string res = base::StringHelper::ToLocaleLower(utf16, locale); return *(factory->NewFromStdString(res)); } @@ -846,63 +948,87 @@ EcmaString *EcmaString::ToLocaleLower(const EcmaVM *vm, const JSHandle &src, const icu::Locale &locale) { auto factory = vm->GetFactory(); - auto srcFlat = JSHandle(vm->GetJSThread(), Flatten(vm, src)); - std::u16string utf16 = srcFlat->ToU16String(); + FlatStringInfo srcFlat = FlattenAllString(vm, src); + std::u16string utf16 = srcFlat.ToU16String(); std::string res = base::StringHelper::ToLocaleUpper(utf16, locale); return *(factory->NewFromStdString(res)); } EcmaString *EcmaString::Trim(const JSThread *thread, const JSHandle &src, TrimMode mode) { - auto srcFlat = JSHandle(thread, Flatten(thread->GetEcmaVM(), src)); - uint32_t srcLen = srcFlat->GetLength(); + FlatStringInfo srcFlat = FlattenAllString(thread->GetEcmaVM(), src); + uint32_t srcLen = srcFlat.GetLength(); if (UNLIKELY(srcLen == 0)) { return EcmaString::Cast(thread->GlobalConstants()->GetEmptyString().GetTaggedObject()); } - if (srcFlat->IsUtf8()) { - Span data(srcFlat->GetDataUtf8(), srcLen); - return TrimBody(thread, srcFlat, data, mode); + if (srcFlat.IsUtf8()) { + Span data(srcFlat.GetDataUtf8(), srcLen); + return TrimBody(thread, src, data, mode); } else { - Span data(srcFlat->GetDataUtf16(), srcLen); - return TrimBody(thread, srcFlat, data, mode); + Span data(srcFlat.GetDataUtf16(), srcLen); + return TrimBody(thread, src, data, mode); } } -EcmaString *EcmaString::SlowFlatten(const EcmaVM *vm, const JSHandle &string) +EcmaString *EcmaString::SlowFlatten(const EcmaVM *vm, const JSHandle &string, MemSpaceType type) { + ASSERT(string->IsTreeString() || string->IsSlicedString()); auto thread = vm->GetJSThread(); - ASSERT(EcmaString::Cast(string->GetSecond())->GetLength() != 0); - uint32_t length = string->GetLength(); EcmaString *result = nullptr; if (string->IsUtf8()) { - result = CreateLineString(vm, length, true); + result = CreateLineStringWithSpaceType(vm, length, true, type); WriteToFlat(*string, result->GetDataUtf8Writable(), length); } else { - result = CreateLineString(vm, length, false); + result = CreateLineStringWithSpaceType(vm, length, false, type); WriteToFlat(*string, result->GetDataUtf16Writable(), length); } - string->SetFirst(thread, JSTaggedValue(result)); - string->SetSecond(thread, JSTaggedValue(*vm->GetFactory()->GetEmptyString())); + if (string->IsTreeString()) { + JSHandle tree(string); + ASSERT(EcmaString::Cast(tree->GetSecond())->GetLength() != 0); + tree->SetFirst(thread, JSTaggedValue(result)); + tree->SetSecond(thread, JSTaggedValue(*vm->GetFactory()->GetEmptyString())); + } return result; } -EcmaString *EcmaString::Flatten(const EcmaVM *vm, const JSHandle &string) +EcmaString *EcmaString::Flatten(const EcmaVM *vm, const JSHandle &string, MemSpaceType type) { EcmaString *s = *string; - if (s->IsLineOrConstantString()) { + if (s->IsLineOrConstantString() || s->IsSlicedString()) { return s; } if (s->IsTreeString()) { JSHandle tree = JSHandle::Cast(string); if (!tree->IsFlat()) { - return SlowFlatten(vm, tree); + return SlowFlatten(vm, string, type); } s = EcmaString::Cast(tree->GetFirst()); } return s; } +FlatStringInfo EcmaString::FlattenAllString(const EcmaVM *vm, const JSHandle &string, MemSpaceType type) +{ + EcmaString *s = *string; + uint32_t startIndex = 0; + if (s->IsLineOrConstantString()) { + return FlatStringInfo(s, startIndex, s->GetLength()); + } + if (string->IsTreeString()) { + JSHandle tree = JSHandle::Cast(string); + if (!tree->IsFlat()) { + s = SlowFlatten(vm, string, type); + } else { + s = EcmaString::Cast(tree->GetFirst()); + } + } else if (string->IsSlicedString()) { + s = EcmaString::Cast(SlicedString::Cast(*string)->GetParent()); + startIndex = SlicedString::Cast(*string)->GetStartIndex(); + } + return FlatStringInfo(s, startIndex, string->GetLength()); +} + EcmaString *EcmaString::FlattenNoGC(const EcmaVM *vm, EcmaString *string) { DISALLOW_GARBAGE_COLLECTION; @@ -927,6 +1053,18 @@ EcmaString *EcmaString::FlattenNoGC(const EcmaVM *vm, EcmaString *string) tree->SetSecond(vm->GetJSThread(), JSTaggedValue(*vm->GetFactory()->GetEmptyString())); return result; } + } else if (string->IsSlicedString()) { + SlicedString *str = SlicedString::Cast(string); + uint32_t length = str->GetLength(); + EcmaString *result = nullptr; + if (str->IsUtf8()) { + result = CreateLineStringNoGC(vm, length, true); + WriteToFlat(str, result->GetDataUtf8Writable(), length); + } else { + result = CreateLineStringNoGC(vm, length, false); + WriteToFlat(str, result->GetDataUtf16Writable(), length); + } + return result; } return string; } @@ -944,6 +1082,9 @@ const uint8_t *EcmaString::GetUtf8DataFlat(const EcmaString *src, CVectorIsSlicedString()) { + SlicedString *str = SlicedString::Cast(string); + return EcmaString::Cast(str->GetParent())->GetDataUtf8() + str->GetStartIndex(); } return string->GetDataUtf8(); } @@ -961,10 +1102,27 @@ const uint16_t *EcmaString::GetUtf16DataFlat(const EcmaString *src, CVectorIsSlicedString()) { + SlicedString *str = SlicedString::Cast(string); + return EcmaString::Cast(str->GetParent())->GetDataUtf16() + str->GetStartIndex(); } return string->GetDataUtf16(); } +std::u16string FlatStringInfo::ToU16String(uint32_t len) +{ + uint32_t length = len > 0 ? len : GetLength(); + std::u16string result; + if (IsUtf16()) { + const uint16_t *data = this->GetDataUtf16(); + result = base::StringHelper::Utf16ToU16String(data, length); + } else { + const uint8_t *data = this->GetDataUtf8(); + result = base::StringHelper::Utf8ToU16String(data, length); + } + return result; +} + EcmaStringAccessor::EcmaStringAccessor(EcmaString *string) { ASSERT(string != nullptr); diff --git a/ecmascript/ecma_string.h b/ecmascript/ecma_string.h index de7ae40102888fc4034c8b1899a97fd4e7386d98..c0ead4b9472cb80a7766e803770ca195581a68b2 100644 --- a/ecmascript/ecma_string.h +++ b/ecmascript/ecma_string.h @@ -21,6 +21,7 @@ #include #include "ecmascript/base/utf_helper.h" +#include "ecmascript/common.h" #include "ecmascript/ecma_macros.h" #include "ecmascript/js_hclass.h" #include "ecmascript/js_tagged_value.h" @@ -41,6 +42,14 @@ class EcmaVM; class LineEcmaString; class ConstantString; class TreeEcmaString; +class SlicedString; +class FlatStringInfo; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ECMA_STRING_CHECK_LENGTH_AND_TRHOW(vm, length) \ + if ((length) >= MAX_STRING_LENGTH) { \ + THROW_RANGE_ERROR_AND_RETURN((vm)->GetJSThread(), "Invalid string length", nullptr); \ + } class EcmaString : public TaggedObject { public: @@ -48,6 +57,8 @@ public: static constexpr uint32_t STRING_COMPRESSED_BIT = 0x1; static constexpr uint32_t STRING_INTERN_BIT = 0x2; + static constexpr size_t MAX_STRING_LENGTH = 0x40000000U; // 30 bits for string length, 2 bits for special meaning + static constexpr uint32_t STRING_LENGTH_SHIFT_COUNT = 2U; static constexpr size_t MIX_LENGTH_OFFSET = TaggedObjectSize(); // In last bit of mix_length we store if this string is compressed or not. @@ -70,6 +81,11 @@ private: friend class LineEcmaString; friend class ConstantString; friend class TreeEcmaString; + friend class SlicedString; + friend class FlatStringInfo; + friend class NameDictionary; + + static constexpr int SMALL_STRING_SIZE = 128; static EcmaString *CreateEmptyString(const EcmaVM *vm); static EcmaString *CreateFromUtf8(const EcmaVM *vm, const uint8_t *utf8Data, uint32_t utf8Len, @@ -77,6 +93,7 @@ private: uint32_t idOffset = 0); static EcmaString *CreateFromUtf16(const EcmaVM *vm, const uint16_t *utf16Data, uint32_t utf16Len, bool canBeCompress, MemSpaceType type = MemSpaceType::SEMI_SPACE); + static SlicedString *CreateSlicedString(const EcmaVM *vm, MemSpaceType type = MemSpaceType::SEMI_SPACE); static EcmaString *CreateLineString(const EcmaVM *vm, size_t length, bool compressed); static EcmaString *CreateLineStringNoGC(const EcmaVM *vm, size_t length, bool compressed); static EcmaString *CreateLineStringWithSpaceType(const EcmaVM *vm, @@ -85,10 +102,14 @@ private: const JSHandle &left, const JSHandle &right, uint32_t length, bool compressed); static EcmaString *CreateConstantString(const EcmaVM *vm, const uint8_t *utf8Data, size_t length, bool compressed, MemSpaceType type = MemSpaceType::SEMI_SPACE, uint32_t idOffset = 0); - static EcmaString *Concat(const EcmaVM *vm, - const JSHandle &left, const JSHandle &right); + static EcmaString *Concat(const EcmaVM *vm, const JSHandle &left, + const JSHandle &right, MemSpaceType type = MemSpaceType::SEMI_SPACE); + static EcmaString *CopyStringToOldSpace(const EcmaVM *vm, const JSHandle &original, + uint32_t length, bool compressed); static EcmaString *FastSubString(const EcmaVM *vm, const JSHandle &src, uint32_t start, uint32_t length); + static EcmaString *GetSlicedString(const EcmaVM *vm, + const JSHandle &src, uint32_t start, uint32_t length); // require src is LineString // not change src data structure static inline EcmaString *FastSubUtf8String(const EcmaVM *vm, @@ -119,14 +140,14 @@ private: uint32_t GetLength() const { - return GetMixLength() >> 2U; + return GetMixLength() >> STRING_LENGTH_SHIFT_COUNT; } void SetLength(uint32_t length, bool compressed = false) { - ASSERT(length < 0x40000000U); + ASSERT(length < MAX_STRING_LENGTH); // Use 0u for compressed/utf8 expression - SetMixLength((length << 2U) | (compressed ? STRING_COMPRESSED : STRING_UNCOMPRESSED)); + SetMixLength((length << STRING_LENGTH_SHIFT_COUNT) | (compressed ? STRING_COMPRESSED : STRING_UNCOMPRESSED)); } inline size_t GetUtf8Length(bool modify = true) const; @@ -185,9 +206,34 @@ private: // can change left and right data structure static int32_t Compare(const EcmaVM *vm, const JSHandle &left, const JSHandle &right); - template // Check that two spans are equal. Should have the same length. - static bool StringsAreEquals(Span &str1, Span &str2); + /* static */ + template + static bool StringsAreEquals(Span &str1, Span &str2) + { + ASSERT(str1.Size() <= str2.Size()); + size_t size = str1.Size(); + if (!std::is_same_v) { + for (size_t i = 0; i < size; i++) { + auto left = static_cast(str1[i]); + auto right = static_cast(str2[i]); + if (left != right) { + return false; + } + } + return true; + } + if (size < SMALL_STRING_SIZE) { + for (size_t i = 0; i < size; i++) { + if (str1[i] != str2[i]) { + return false; + } + } + return true; + } + return memcmp(str1.data(), str2.data(), size * sizeof(T)) == 0; + } + // Converts utf8Data to utf16 and compare it with given utf16_data. static bool IsUtf8EqualsUtf16(const uint8_t *utf8Data, size_t utf8Len, const uint16_t *utf16Data, uint32_t utf16Len); @@ -198,12 +244,13 @@ private: // Compares strings by bytes, It doesn't check canonical unicode equivalence. static bool StringsAreEqual(EcmaString *str1, EcmaString *str2); // Two strings have the same type of utf encoding format. - static bool StringsAreEqualSameUtfEncoding(EcmaString *str1, EcmaString *str2); + static bool StringsAreEqualDiffUtfEncoding(EcmaString *str1, EcmaString *str2); + static bool StringsAreEqualDiffUtfEncoding(const FlatStringInfo &str1, const FlatStringInfo &str2); // Compares strings by bytes, It doesn't check canonical unicode equivalence. // not change str1 data structure. // if str1 is not flat, this func has low efficiency. - static bool StringsAreEqualUtf8(const EcmaString *str1, const uint8_t *utf8Data, uint32_t utf8Len, - bool canBeCompress); + static bool StringIsEqualUint8Data(const EcmaString *str1, const uint8_t *dataAddr, uint32_t dataLen, + bool canBeCompress); // Compares strings by bytes, It doesn't check canonical unicode equivalence. // not change str1 data structure. // if str1 is not flat, this func has low efficiency. @@ -419,6 +466,10 @@ private: bool ToElementIndex(uint32_t *index); + bool ToInt(int32_t *index); + + bool ToUInt64FromLoopStart(uint64_t *index, uint32_t loopStart, const uint8_t *data); + bool ToTypedArrayIndex(uint32_t *index); template @@ -447,6 +498,18 @@ private: template static bool MemCopyChars(Span &dst, size_t dstMax, Span &src, size_t count); + template + static uint32_t ComputeHashForData(const T *data, size_t size, uint32_t hashSeed) + { + uint32_t hash = hashSeed; + Span sp(data, size); + for (auto c : sp) { + constexpr size_t SHIFT = 5; + hash = (hash << SHIFT) - hash + c; + } + return hash; + } + static bool IsASCIICharacter(uint16_t data) { // \0 is not considered ASCII in Ecma-Modified-UTF8 [only modify '\u0000'] @@ -469,10 +532,18 @@ private: { return GetClass()->IsConstantString(); } + bool IsSlicedString() const + { + return GetClass()->IsSlicedString(); + } bool IsTreeString() const { return GetClass()->IsTreeString(); } + bool NotTreeString() const + { + return !IsTreeString(); + } bool IsLineOrConstantString() const { auto hclass = GetClass(); @@ -494,9 +565,13 @@ private: static const uint16_t *GetUtf16DataFlat(const EcmaString *src, CVector &buf); // string must be not flat - static EcmaString *SlowFlatten(const EcmaVM *vm, const JSHandle &string); + static EcmaString *SlowFlatten(const EcmaVM *vm, const JSHandle &string, MemSpaceType type); + + static EcmaString *Flatten(const EcmaVM *vm, const JSHandle &string, + MemSpaceType type = MemSpaceType::SEMI_SPACE); - static EcmaString *Flatten(const EcmaVM *vm, const JSHandle &string); + static FlatStringInfo FlattenAllString(const EcmaVM *vm, const JSHandle &string, + MemSpaceType type = MemSpaceType::SEMI_SPACE); static EcmaString *FlattenNoGC(const EcmaVM *vm, EcmaString *string); @@ -625,6 +700,57 @@ public: } }; +// The substrings of another string use SlicedString to describe. +class SlicedString : public EcmaString { +public: + static constexpr uint32_t MIN_SLICED_ECMASTRING_LENGTH = 13; + static constexpr size_t PARENT_OFFSET = EcmaString::SIZE; + ACCESSORS(Parent, PARENT_OFFSET, STARTINDEX_OFFSET); + ACCESSORS_PRIMITIVE_FIELD(StartIndex, uint32_t, STARTINDEX_OFFSET, SIZE); + DECL_VISIT_OBJECT(PARENT_OFFSET, STARTINDEX_OFFSET); + + CAST_CHECK(SlicedString, IsSlicedString); +private: + friend class EcmaString; + static SlicedString *Cast(EcmaString *str) + { + return static_cast(str); + } + + static SlicedString *Cast(const EcmaString *str) + { + return SlicedString::Cast(const_cast(str)); + } + + static size_t ObjectSize() + { + return SlicedString::SIZE; + } + + // Minimum length for a sliced string + template + uint16_t Get(int32_t index) const + { + int32_t length = static_cast(GetLength()); + if (verify) { + if ((index < 0) || (index >= length)) { + return 0; + } + } + EcmaString *parent = EcmaString::Cast(GetParent()); + if (parent->IsLineString()) { + if (parent->IsUtf8()) { + Span sp(parent->GetDataUtf8() + GetStartIndex(), length); + return sp[index]; + } + Span sp(parent->GetDataUtf16() + GetStartIndex(), length); + return sp[index]; + } + Span sp(ConstantString::Cast(parent)->GetConstantData() + GetStartIndex(), length); + return sp[index]; + } +}; + class TreeEcmaString : public EcmaString { public: // Minimum length for a tree string @@ -683,7 +809,52 @@ public: } } UNREACHABLE(); - } + } +}; + +class FlatStringInfo { +public: + FlatStringInfo(EcmaString *string, uint32_t startIndex, uint32_t length) : string_(string), + startIndex_(startIndex), + length_(length) {} + bool IsUtf8() const + { + return string_->IsUtf8(); + } + + bool IsUtf16() const + { + return string_->IsUtf16(); + } + + EcmaString *GetString() const + { + return string_; + } + + void SetString(EcmaString *string) + { + string_ = string; + } + + uint32_t GetStartIndex() const + { + return startIndex_; + } + + uint32_t GetLength() const + { + return length_; + } + + const uint8_t *GetDataUtf8() const; + const uint16_t *GetDataUtf16() const; + uint8_t *GetDataUtf8Writable() const; + std::u16string ToU16String(uint32_t len = 0); +private: + EcmaString *string_ {nullptr}; + uint32_t startIndex_ {0}; + uint32_t length_ {0}; }; // if you want to use functions of EcmaString, please not use directly, @@ -728,10 +899,16 @@ public: return EcmaString::CreateFromUtf16(vm, utf16Data, utf16Len, canBeCompress, type); } - static EcmaString *Concat(const EcmaVM *vm, - const JSHandle &str1Handle, const JSHandle &str2Handle) + static EcmaString *Concat(const EcmaVM *vm, const JSHandle &str1Handle, + const JSHandle &str2Handle, MemSpaceType type = MemSpaceType::SEMI_SPACE) { - return EcmaString::Concat(vm, str1Handle, str2Handle); + return EcmaString::Concat(vm, str1Handle, str2Handle, type); + } + + static EcmaString *CopyStringToOldSpace(const EcmaVM *vm, const JSHandle &original, + uint32_t length, bool compressed) + { + return EcmaString::CopyStringToOldSpace(vm, original, length, compressed); } // can change src data structure @@ -741,6 +918,13 @@ public: return EcmaString::FastSubString(vm, src, start, length); } + // get + static EcmaString *GetSlicedString(const EcmaVM *vm, + const JSHandle &src, uint32_t start, uint32_t length) + { + return EcmaString::GetSlicedString(vm, src, start, length); + } + bool IsUtf8() const { return string_->IsUtf8(); @@ -932,17 +1116,17 @@ public: // not change str1 and str2 data structure. // if str1 or str2 is not flat, this func has low efficiency. - static bool StringsAreEqualSameUtfEncoding(EcmaString *str1, EcmaString *str2) + static bool StringsAreEqualDiffUtfEncoding(EcmaString *str1, EcmaString *str2) { - return EcmaString::StringsAreEqualSameUtfEncoding(str1, str2); + return EcmaString::StringsAreEqualDiffUtfEncoding(str1, str2); } // not change str1 data structure. // if str1 is not flat, this func has low efficiency. - static bool StringsAreEqualUtf8(const EcmaString *str1, const uint8_t *utf8Data, uint32_t utf8Len, - bool canBeCompress) + static bool StringIsEqualUint8Data(const EcmaString *str1, const uint8_t *dataAddr, uint32_t dataLen, + bool canBeCompress) { - return EcmaString::StringsAreEqualUtf8(str1, utf8Data, utf8Len, canBeCompress); + return EcmaString::StringIsEqualUint8Data(str1, dataAddr, dataLen, canBeCompress); } // not change str1 data structure. @@ -983,6 +1167,13 @@ public: return string_->ToElementIndex(index); } + // not change string data structure. + // if string is not flat, this func has low efficiency. + bool ToInt(int32_t *index) + { + return string_->ToInt(index); + } + // not change string data structure. // if string is not flat, this func has low efficiency. bool ToTypedArrayIndex(uint32_t *index) @@ -1046,14 +1237,27 @@ public: return string_->IsTreeString(); } - static EcmaString *Flatten(const EcmaVM *vm, const JSHandle &string) + bool NotTreeString() const + { + return string_->NotTreeString(); + } + + static EcmaString *Flatten(const EcmaVM *vm, const JSHandle &string, + MemSpaceType type = MemSpaceType::SEMI_SPACE) + { + return EcmaString::Flatten(vm, string, type); + } + + static FlatStringInfo FlattenAllString(const EcmaVM *vm, const JSHandle &string, + MemSpaceType type = MemSpaceType::SEMI_SPACE) { - return EcmaString::Flatten(vm, string); + return EcmaString::FlattenAllString(vm, string, type); } - static EcmaString *SlowFlatten(const EcmaVM *vm, const JSHandle &string) + static EcmaString *SlowFlatten(const EcmaVM *vm, const JSHandle &string, + MemSpaceType type = MemSpaceType::SEMI_SPACE) { - return EcmaString::SlowFlatten(vm, string); + return EcmaString::SlowFlatten(vm, string, type); } static EcmaString *FlattenNoGC(const EcmaVM *vm, EcmaString *string) diff --git a/ecmascript/ecma_string_table.cpp b/ecmascript/ecma_string_table.cpp index 523507768aef09307c12a0fb7de8f279b3a0208f..f0f677a57267aa8de2f15e5f484271456904799f 100644 --- a/ecmascript/ecma_string_table.cpp +++ b/ecmascript/ecma_string_table.cpp @@ -27,8 +27,8 @@ EcmaStringTable::EcmaStringTable(const EcmaVM *vm) : vm_(vm) {} EcmaString *EcmaStringTable::GetString(const JSHandle &firstString, const JSHandle &secondString) const { - ASSERT(EcmaStringAccessor(firstString).IsLineOrConstantString()); - ASSERT(EcmaStringAccessor(secondString).IsLineOrConstantString()); + ASSERT(EcmaStringAccessor(firstString).NotTreeString()); + ASSERT(EcmaStringAccessor(secondString).NotTreeString()); uint32_t hashCode = EcmaStringAccessor(firstString).GetHashcode(); hashCode = EcmaStringAccessor(secondString).ComputeHashcode(hashCode); auto range = table_.equal_range(hashCode); @@ -47,7 +47,7 @@ EcmaString *EcmaStringTable::GetString(const uint8_t *utf8Data, uint32_t utf8Len auto range = table_.equal_range(hashCode); for (auto item = range.first; item != range.second; ++item) { auto foundString = item->second; - if (EcmaStringAccessor::StringsAreEqualUtf8(foundString, utf8Data, utf8Len, canBeCompress)) { + if (EcmaStringAccessor::StringIsEqualUint8Data(foundString, utf8Data, utf8Len, canBeCompress)) { return foundString; } } @@ -69,7 +69,7 @@ EcmaString *EcmaStringTable::GetString(const uint16_t *utf16Data, uint32_t utf16 EcmaString *EcmaStringTable::GetString(EcmaString *string) const { - ASSERT(EcmaStringAccessor(string).IsLineOrConstantString()); + ASSERT(EcmaStringAccessor(string).NotTreeString()); auto hashcode = EcmaStringAccessor(string).GetHashcode(); auto range = table_.equal_range(hashcode); for (auto item = range.first; item != range.second; ++item) { @@ -86,8 +86,11 @@ void EcmaStringTable::InternString(EcmaString *string) if (EcmaStringAccessor(string).IsInternString()) { return; } - ASSERT(EcmaStringAccessor(string).IsLineOrConstantString()); - table_.emplace(EcmaStringAccessor(string).GetHashcode(), string); + // Strings in string table should not be in the young space. + ASSERT(!Region::ObjectAddressToRange(reinterpret_cast(string))->InYoungSpace()); + ASSERT(EcmaStringAccessor(string).NotTreeString()); + auto hashcode = EcmaStringAccessor(string).GetHashcode(); + table_.emplace(hashcode, string); EcmaStringAccessor(string).SetInternString(); } @@ -105,8 +108,9 @@ EcmaString *EcmaStringTable::GetOrInternString(const JSHandle &first if (concatString != nullptr) { return concatString; } - JSHandle concatHandle(vm_->GetJSThread(), EcmaStringAccessor::Concat(vm_, firstFlat, secondFlat)); - concatString = EcmaStringAccessor::Flatten(vm_, concatHandle); + JSHandle concatHandle(vm_->GetJSThread(), + EcmaStringAccessor::Concat(vm_, firstFlat, secondFlat, MemSpaceType::OLD_SPACE)); + concatString = EcmaStringAccessor::Flatten(vm_, concatHandle, MemSpaceType::OLD_SPACE); InternString(concatString); return concatString; } @@ -118,7 +122,7 @@ EcmaString *EcmaStringTable::GetOrInternString(const uint8_t *utf8Data, uint32_t return result; } - result = EcmaStringAccessor::CreateFromUtf8(vm_, utf8Data, utf8Len, canBeCompress); + result = EcmaStringAccessor::CreateFromUtf8(vm_, utf8Data, utf8Len, canBeCompress, MemSpaceType::OLD_SPACE); InternString(result); return result; } @@ -129,7 +133,12 @@ EcmaString *EcmaStringTable::GetOrInternString(const uint8_t *utf8Data, uint32_t */ EcmaString *EcmaStringTable::CreateAndInternStringNonMovable(const uint8_t *utf8Data, uint32_t utf8Len) { - EcmaString *result = EcmaStringAccessor::CreateFromUtf8(vm_, utf8Data, utf8Len, true, MemSpaceType::NON_MOVABLE); + EcmaString *result = GetString(utf8Data, utf8Len, true); + if (result != nullptr) { + return result; + } + + result = EcmaStringAccessor::CreateFromUtf8(vm_, utf8Data, utf8Len, true, MemSpaceType::NON_MOVABLE); InternString(result); return result; } @@ -141,7 +150,7 @@ EcmaString *EcmaStringTable::GetOrInternString(const uint16_t *utf16Data, uint32 return result; } - result = EcmaStringAccessor::CreateFromUtf16(vm_, utf16Data, utf16Len, canBeCompress); + result = EcmaStringAccessor::CreateFromUtf16(vm_, utf16Data, utf16Len, canBeCompress, MemSpaceType::OLD_SPACE); InternString(result); return result; } @@ -153,7 +162,7 @@ EcmaString *EcmaStringTable::GetOrInternString(EcmaString *string) } JSHandle strHandle(vm_->GetJSThread(), string); // may gc - auto strFlat = EcmaStringAccessor::Flatten(vm_, strHandle); + auto strFlat = EcmaStringAccessor::Flatten(vm_, strHandle, MemSpaceType::OLD_SPACE); if (EcmaStringAccessor(strFlat).IsInternString()) { return strFlat; } @@ -161,6 +170,15 @@ EcmaString *EcmaStringTable::GetOrInternString(EcmaString *string) if (result != nullptr) { return result; } + + if (EcmaStringAccessor(strFlat).NotTreeString()) { + Region *objectRegion = Region::ObjectAddressToRange(reinterpret_cast(strFlat)); + if (objectRegion->InYoungSpace()) { + JSHandle resultHandle(vm_->GetJSThread(), strFlat); + strFlat = EcmaStringAccessor::CopyStringToOldSpace(vm_, + resultHandle, EcmaStringAccessor(strFlat).GetLength(), EcmaStringAccessor(strFlat).IsUtf8()); + } + } InternString(strFlat); return strFlat; } @@ -173,6 +191,7 @@ EcmaString *EcmaStringTable::GetOrInternStringWithSpaceType(const uint8_t *utf8D if (result != nullptr) { return result; } + type = type == MemSpaceType::NON_MOVABLE ? MemSpaceType::NON_MOVABLE : MemSpaceType::OLD_SPACE; if (canBeCompress) { // Constant string will be created in this branch. result = EcmaStringAccessor::CreateFromUtf8(vm_, utf8Data, utf8Len, canBeCompress, type, isConstantString, @@ -191,7 +210,7 @@ EcmaString *EcmaStringTable::GetOrInternStringWithSpaceType(const uint16_t *utf1 if (result != nullptr) { return result; } - + type = type == MemSpaceType::NON_MOVABLE ? MemSpaceType::NON_MOVABLE : MemSpaceType::OLD_SPACE; result = EcmaStringAccessor::CreateFromUtf16(vm_, utf16Data, utf16Len, canBeCompress, type); InternString(result); return result; @@ -200,8 +219,10 @@ EcmaString *EcmaStringTable::GetOrInternStringWithSpaceType(const uint16_t *utf1 void EcmaStringTable::SweepWeakReference(const WeakRootVisitor &visitor) { for (auto it = table_.begin(); it != table_.end();) { + // Strings in string table should not be in the young space. Only old gc will sweep string table. auto *object = it->second; auto fwd = visitor(object); + ASSERT(!Region::ObjectAddressToRange(object)->InYoungSpace()); if (fwd == nullptr) { LOG_ECMA(VERBOSE) << "StringTable: delete string " << std::hex << object; table_.erase(it++); @@ -219,7 +240,7 @@ bool EcmaStringTable::CheckStringTableValidity() { for (auto itemOuter = table_.begin(); itemOuter != table_.end(); ++itemOuter) { auto outerString = itemOuter->second; - if (!EcmaStringAccessor(outerString).IsLineOrConstantString()) { + if (!EcmaStringAccessor(outerString).NotTreeString()) { return false; } int counter = 0; diff --git a/ecmascript/ecma_vm.cpp b/ecmascript/ecma_vm.cpp index 2abd331a1c71b5e2009bbc694abdd91db09a1fae..35eca1f39bf838f84a7ed884025a7ea43652e5dc 100644 --- a/ecmascript/ecma_vm.cpp +++ b/ecmascript/ecma_vm.cpp @@ -18,6 +18,7 @@ #include "ecmascript/base/string_helper.h" #include "ecmascript/builtins/builtins.h" #include "ecmascript/builtins/builtins_ark_tools.h" +#include "ecmascript/log.h" #ifdef ARK_SUPPORT_INTL #include "ecmascript/builtins/builtins_collator.h" #include "ecmascript/builtins/builtins_date_time_format.h" @@ -72,7 +73,6 @@ #include "ecmascript/mem/visitor.h" #include "ecmascript/module/js_module_manager.h" #include "ecmascript/module/module_data_extractor.h" -#include "ecmascript/napi/include/dfx_jsnapi.h" #include "ecmascript/object_factory.h" #include "ecmascript/patch/quick_fix_manager.h" #include "ecmascript/pgo_profiler/pgo_profiler_manager.h" @@ -91,6 +91,7 @@ namespace panda::ecmascript { using RandomGenerator = base::RandomGenerator; +using PGOProfilerManager = pgo::PGOProfilerManager; AOTFileManager *JsStackInfo::loader = nullptr; /* static */ EcmaVM *EcmaVM::Create(const JSRuntimeOptions &options, EcmaParamConfiguration &config) @@ -108,7 +109,9 @@ EcmaVM *EcmaVM::Create(const JSRuntimeOptions &options, EcmaParamConfiguration & auto jsThread = JSThread::Create(vm); vm->thread_ = jsThread; vm->Initialize(); - JsStackInfo::loader = vm->GetJSThread()->GetCurrentEcmaContext()->GetAOTFileManager(); + if (JsStackInfo::loader == nullptr) { + JsStackInfo::loader = vm->GetJSThread()->GetCurrentEcmaContext()->GetAOTFileManager(); + } #if defined(__aarch64__) && !defined(PANDA_TARGET_MACOS) && !defined(PANDA_TARGET_IOS) if (SetThreadInfoCallback != nullptr) { SetThreadInfoCallback(CrashCallback); @@ -142,6 +145,9 @@ void EcmaVM::PostFork() heap_->SetHeapMode(HeapMode::SHARE); GetAssociatedJSThread()->SetThreadId(); heap_->EnableParallelGC(); +#ifdef ENABLE_POSTFORK_FORCEEXPAND + heap_->NotifyPostFork(); +#endif } EcmaVM::EcmaVM(JSRuntimeOptions options, EcmaParamConfiguration config) @@ -204,9 +210,6 @@ bool EcmaVM::Initialize() auto context = new EcmaContext(thread_); thread_->PushContext(context); [[maybe_unused]] EcmaHandleScope scope(thread_); - JSHandle hClassHandle = factory_->InitClassClass(); - hClassClass_ = hClassHandle.GetTaggedValue(); - thread_->InitGlobalConst(*hClassHandle); context->Initialize(); thread_->SetGlueGlobalEnv(reinterpret_cast(context->GetGlobalEnv().GetTaggedType())); thread_->SetGlobalObject(GetGlobalEnv()->GetGlobalObject()); @@ -232,7 +235,13 @@ EcmaVM::~EcmaVM() { initialized_ = false; #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER) - DFXJSNApi::StopCpuProfilerForFile(this); + if (thread_->isProfiling_) { + DFXJSNApi::StopCpuProfilerForFile(this); + DFXJSNApi::StopCpuProfilerForInfo(this); + } +#endif +#if defined(ECMASCRIPT_SUPPORT_HEAPPROFILER) + DeleteHeapProfile(); #endif heap_->WaitAllTasksFinished(); Taskpool::GetCurrentTaskpool()->Destroy(thread_->GetThreadId()); @@ -263,6 +272,10 @@ EcmaVM::~EcmaVM() gcStats_ = nullptr; } + if (JsStackInfo::loader == GetJSThread()->GetCurrentEcmaContext()->GetAOTFileManager()) { + JsStackInfo::loader = nullptr; + } + if (heap_ != nullptr) { heap_->Destroy(); delete heap_; @@ -371,32 +384,25 @@ JSHandle EcmaVM::GetEcmaUncaughtException() const void EcmaVM::PrintJSErrorInfo(const JSHandle &exceptionInfo) const { - JSHandle nameKey = thread_->GlobalConstants()->GetHandledNameString(); - JSHandle name(JSObject::GetProperty(thread_, exceptionInfo, nameKey).GetValue()); - JSHandle msgKey = thread_->GlobalConstants()->GetHandledMessageString(); - JSHandle msg(JSObject::GetProperty(thread_, exceptionInfo, msgKey).GetValue()); - JSHandle stackKey = thread_->GlobalConstants()->GetHandledStackString(); - JSHandle stack(JSObject::GetProperty(thread_, exceptionInfo, stackKey).GetValue()); - - CString nameBuffer = ConvertToString(*name); - CString msgBuffer = ConvertToString(*msg); - CString stackBuffer = ConvertToString(*stack); - LOG_NO_TAG(ERROR) << nameBuffer << ": " << msgBuffer << "\n" << stackBuffer; + EcmaContext::PrintJSErrorInfo(thread_, exceptionInfo); } void EcmaVM::ProcessNativeDelete(const WeakRootVisitor &visitor) { - auto iter = nativePointerList_.begin(); - while (iter != nativePointerList_.end()) { - JSNativePointer *object = *iter; - auto fwd = visitor(reinterpret_cast(object)); - if (fwd == nullptr) { - object->Destroy(); - iter = nativePointerList_.erase(iter); - } else { - ++iter; + if (!heap_->IsYoungGC()) { + auto iter = nativePointerList_.begin(); + while (iter != nativePointerList_.end()) { + JSNativePointer *object = *iter; + auto fwd = visitor(reinterpret_cast(object)); + if (fwd == nullptr) { + object->Destroy(); + iter = nativePointerList_.erase(iter); + } else { + ++iter; + } } } + thread_->GetCurrentEcmaContext()->ProcessNativeDelete(visitor); } @@ -405,34 +411,37 @@ void EcmaVM::ProcessReferences(const WeakRootVisitor &visitor) if (thread_->GetCurrentEcmaContext()->GetRegExpParserCache() != nullptr) { thread_->GetCurrentEcmaContext()->GetRegExpParserCache()->Clear(); } - heap_->ResetNativeBindingSize(); - // array buffer - auto iter = nativePointerList_.begin(); - while (iter != nativePointerList_.end()) { - JSNativePointer *object = *iter; - auto fwd = visitor(reinterpret_cast(object)); - if (fwd == nullptr) { - object->Destroy(); - iter = nativePointerList_.erase(iter); - continue; - } - heap_->IncreaseNativeBindingSize(JSNativePointer::Cast(fwd)); - if (fwd != reinterpret_cast(object)) { - *iter = JSNativePointer::Cast(fwd); + if (!heap_->IsYoungGC()) { + heap_->ResetNativeBindingSize(); + // array buffer + auto iter = nativePointerList_.begin(); + while (iter != nativePointerList_.end()) { + JSNativePointer *object = *iter; + auto fwd = visitor(reinterpret_cast(object)); + if (fwd == nullptr) { + object->Destroy(); + iter = nativePointerList_.erase(iter); + continue; + } + heap_->IncreaseNativeBindingSize(JSNativePointer::Cast(fwd)); + if (fwd != reinterpret_cast(object)) { + *iter = JSNativePointer::Cast(fwd); + } + ++iter; } - ++iter; } thread_->GetCurrentEcmaContext()->ProcessReferences(visitor); + GetPGOProfiler()->ProcessReferences(visitor); } -void EcmaVM::PushToNativePointerList(JSNativePointer *array) +void EcmaVM::PushToNativePointerList(JSNativePointer *pointer) { - nativePointerList_.emplace_back(array); + nativePointerList_.emplace_back(pointer); } -void EcmaVM::RemoveFromNativePointerList(JSNativePointer *array) +void EcmaVM::RemoveFromNativePointerList(JSNativePointer *pointer) { - auto iter = std::find(nativePointerList_.begin(), nativePointerList_.end(), array); + auto iter = std::find(nativePointerList_.begin(), nativePointerList_.end(), pointer); if (iter != nativePointerList_.end()) { JSNativePointer *object = *iter; object->Destroy(); @@ -440,6 +449,25 @@ void EcmaVM::RemoveFromNativePointerList(JSNativePointer *array) } } +void EcmaVM::PushToDeregisterModuleList(CString module) +{ + deregisterModuleList_.emplace_back(module); +} + +void EcmaVM::RemoveFromDeregisterModuleList(CString module) +{ + auto iter = std::find(deregisterModuleList_.begin(), deregisterModuleList_.end(), module); + if (iter != deregisterModuleList_.end()) { + deregisterModuleList_.erase(iter); + } +} + +bool EcmaVM::ContainInDeregisterModuleList(CString module) +{ + return (std::find(deregisterModuleList_.begin(), deregisterModuleList_.end(), module) + != deregisterModuleList_.end()); +} + void EcmaVM::ClearBufferData() { for (auto iter : nativePointerList_) { @@ -449,6 +477,7 @@ void EcmaVM::ClearBufferData() thread_->GetCurrentEcmaContext()->ClearBufferData(); internalNativeMethods_.clear(); workerList_.clear(); + deregisterModuleList_.clear(); } void EcmaVM::CollectGarbage(TriggerGCType gcType, GCReason reason) const @@ -456,16 +485,6 @@ void EcmaVM::CollectGarbage(TriggerGCType gcType, GCReason reason) const heap_->CollectGarbage(gcType, reason); } -void EcmaVM::StartHeapTracking(HeapTracker *tracker) -{ - heap_->StartHeapTracking(tracker); -} - -void EcmaVM::StopHeapTracking() -{ - heap_->StopHeapTracking(); -} - void EcmaVM::Iterate(const RootVisitor &v, const RootRangeVisitor &rv) { rv(Root::ROOT_VM, ObjectSlot(ToUintPtr(&internalNativeMethods_.front())), @@ -473,6 +492,7 @@ void EcmaVM::Iterate(const RootVisitor &v, const RootRangeVisitor &rv) if (!WIN_OR_MAC_OR_IOS_PLATFORM) { snapshotEnv_->Iterate(v); } + pgoProfiler_->Iterate(v); } #if defined(ECMASCRIPT_SUPPORT_HEAPPROFILER) @@ -485,6 +505,14 @@ void EcmaVM::DeleteHeapProfile() heapProfile_ = nullptr; } +HeapProfilerInterface *EcmaVM::GetHeapProfile() +{ + if (heapProfile_ != nullptr) { + return heapProfile_; + } + return nullptr; +} + HeapProfilerInterface *EcmaVM::GetOrNewHeapProfile() { if (heapProfile_ != nullptr) { @@ -494,6 +522,16 @@ HeapProfilerInterface *EcmaVM::GetOrNewHeapProfile() ASSERT(heapProfile_ != nullptr); return heapProfile_; } + +void EcmaVM::StartHeapTracking() +{ + heap_->StartHeapTracking(); +} + +void EcmaVM::StopHeapTracking() +{ + heap_->StopHeapTracking(); +} #endif // NOLINTNEXTLINE(modernize-avoid-c-arrays) @@ -594,4 +632,80 @@ void EcmaVM::DumpCallTimeInfo() callTimer_->PrintAllStats(); } } + +void EcmaVM::WorkersetInfo(EcmaVM *workerVm) +{ + LockHolder lock(mutex_); + auto thread = workerVm->GetJSThread(); + if (thread != nullptr) { + auto tid = thread->GetThreadId(); + if (tid != 0) { + workerList_.emplace(tid, workerVm); + } + } +} + +EcmaVM *EcmaVM::GetWorkerVm(uint32_t tid) +{ + LockHolder lock(mutex_); + EcmaVM *workerVm = nullptr; + if (!workerList_.empty()) { + auto iter = workerList_.find(tid); + if (iter != workerList_.end()) { + workerVm = iter->second; + } + } + return workerVm; +} + +bool EcmaVM::DeleteWorker(EcmaVM *workerVm) +{ + LockHolder lock(mutex_); + auto thread = workerVm->GetJSThread(); + if (thread != nullptr) { + auto tid = thread->GetThreadId(); + if (tid == 0) { + return false; + } + auto iter = workerList_.find(tid); + if (iter != workerList_.end()) { + workerList_.erase(iter); + return true; + } + return false; + } + return false; +} + +bool EcmaVM::SuspendWorkerVm(uint32_t tid) +{ + LockHolder lock(mutex_); + if (!workerList_.empty()) { + auto iter = workerList_.find(tid); + if (iter != workerList_.end()) { + return DFXJSNApi::SuspendVM(iter->second); + } + } + return false; +} + +void EcmaVM::ResumeWorkerVm(uint32_t tid) +{ + LockHolder lock(mutex_); + if (!workerList_.empty()) { + auto iter = workerList_.find(tid); + if (iter != workerList_.end()) { + DFXJSNApi::ResumeVM(iter->second); + } + } +} + +bool EcmaVM::RequestAot(const std::string &bundleName, const std::string &moduleName, RequestAotMode triggerMode) const +{ + if (requestAotCallback_ == nullptr) { + LOG_ECMA(ERROR) << "Trigger aot failed. callback is null."; + return false; + } + return (requestAotCallback_(bundleName, moduleName, static_cast(triggerMode)) == 0); +} } // namespace panda::ecmascript diff --git a/ecmascript/ecma_vm.h b/ecmascript/ecma_vm.h index 0648f8a11c1e0ff928938cb31aae051555a9b4d3..fd3e8cd3a5595f2ae8ccee1236a6966262386343 100644 --- a/ecmascript/ecma_vm.h +++ b/ecmascript/ecma_vm.h @@ -16,6 +16,7 @@ #ifndef ECMASCRIPT_ECMA_VM_H #define ECMASCRIPT_ECMA_VM_H +#include #include #include "ecmascript/base/config.h" @@ -23,10 +24,13 @@ #include "ecmascript/ecma_context.h" #include "ecmascript/js_runtime_options.h" #include "ecmascript/js_thread.h" +#include "ecmascript/log_wrapper.h" #include "ecmascript/mem/c_containers.h" #include "ecmascript/mem/c_string.h" #include "ecmascript/mem/gc_stats.h" +#include "ecmascript/napi/include/dfx_jsnapi.h" #include "ecmascript/napi/include/jsnapi.h" +#include "ecmascript/pgo_profiler/pgo_profiler.h" #include "ecmascript/taskpool/taskpool.h" namespace panda { @@ -55,9 +59,10 @@ class EcmaStringTable; class SnapshotEnv; class SnapshotSerialize; class SnapshotProcessor; -class PGOProfiler; +using PGOProfiler = pgo::PGOProfiler; #if !WIN_OR_MAC_OR_IOS_PLATFORM class HeapProfilerInterface; +class HeapProfiler; #endif namespace job { class MicroJobQueue; @@ -82,10 +87,11 @@ class FunctionCallTimer; class EcmaStringTable; using NativePtrGetter = void* (*)(void* info); - -using ResolvePathCallback = std::function; -using ResolveBufferCallback = std::function(std::string dirPath)>; - +using SourceMapTranslateCallback = std::function; +using ResolveBufferCallback = std::function; +using UnloadNativeModuleCallback = std::function; +using RequestAotCallback = + std::function; class EcmaVM { public: static EcmaVM *Create(const JSRuntimeOptions &options, EcmaParamConfiguration &config); @@ -179,9 +185,11 @@ public: return icEnabled_; } - void PushToNativePointerList(JSNativePointer *array); - void RemoveFromNativePointerList(JSNativePointer *array); - + void PushToNativePointerList(JSNativePointer *pointer); + void RemoveFromNativePointerList(JSNativePointer *pointer); + void PushToDeregisterModuleList(CString module); + void RemoveFromDeregisterModuleList(CString module); + bool ContainInDeregisterModuleList(CString module); JSHandle GetAndClearEcmaUncaughtException() const; JSHandle GetEcmaUncaughtException() const; bool IsOptionalLogEnabled() const @@ -197,10 +205,6 @@ public: } void CollectGarbage(TriggerGCType gcType, GCReason reason = GCReason::OTHER) const; - void StartHeapTracking(HeapTracker *tracker); - - void StopHeapTracking(); - NativeAreaAllocator *GetNativeAreaAllocator() const { return nativeAreaAllocator_.get(); @@ -243,6 +247,16 @@ public: return nativePtrGetter_; } + void SetSourceMapTranslateCallback(SourceMapTranslateCallback cb) + { + sourceMapTranslateCallback_ = cb; + } + + SourceMapTranslateCallback GetSourceMapTranslateCallback() const + { + return sourceMapTranslateCallback_; + } + size_t GetNativePointerListSize() { return nativePointerList_.size(); @@ -258,62 +272,46 @@ public: return resolveBufferCallback_; } - void SetConcurrentCallback(ConcurrentCallback callback, void *data) + bool RequestAot(const std::string &bundleName, const std::string &moduleName, RequestAotMode triggerMode) const; + + void SetRequestAotCallback(const RequestAotCallback &cb) { - concurrentCallback_ = callback; - concurrentData_ = data; + requestAotCallback_ = cb; } - void TriggerConcurrentCallback(JSTaggedValue result, JSTaggedValue hint); + void SetUnloadNativeModuleCallback(const UnloadNativeModuleCallback &cb) + { + unloadNativeModuleCallback_ = cb; + } - void WorkersetInfo(EcmaVM *hostVm, EcmaVM *workerVm) + UnloadNativeModuleCallback GetUnloadNativeModuleCallback() const { - os::memory::LockHolder lock(mutex_); - auto thread = workerVm->GetJSThread(); - if (thread != nullptr && hostVm != nullptr) { - auto tid = thread->GetThreadId(); - if (tid != 0) { - workerList_.emplace(tid, workerVm); - } - } + return unloadNativeModuleCallback_; } - EcmaVM *GetWorkerVm(uint32_t tid) const + void SetConcurrentCallback(ConcurrentCallback callback, void *data) { - EcmaVM *workerVm = nullptr; - if (!workerList_.empty()) { - auto iter = workerList_.find(tid); - if (iter != workerList_.end()) { - workerVm = iter->second; - } - } - return workerVm; - } - - bool DeleteWorker(EcmaVM *hostVm, EcmaVM *workerVm) - { - os::memory::LockHolder lock(mutex_); - auto thread = workerVm->GetJSThread(); - if (hostVm != nullptr && thread != nullptr) { - auto tid = thread->GetThreadId(); - if (tid == 0) { - return false; - } - auto iter = workerList_.find(tid); - if (iter != workerList_.end()) { - workerList_.erase(iter); - return true; - } - return false; - } - return false; + concurrentCallback_ = callback; + concurrentData_ = data; } + void TriggerConcurrentCallback(JSTaggedValue result, JSTaggedValue hint); + + void WorkersetInfo(EcmaVM *workerVm); + + EcmaVM *GetWorkerVm(uint32_t tid); + + bool DeleteWorker(EcmaVM *workerVm); + + bool SuspendWorkerVm(uint32_t tid); + + void ResumeWorkerVm(uint32_t tid); + template void EnumerateWorkerVm(Callback cb) { // since there is a lock, so cannot mark function const - os::memory::LockHolder lock(mutex_); + LockHolder lock(mutex_); for (const auto &item : workerList_) { cb(item.second); } @@ -334,9 +332,40 @@ public: isBundlePack_ = value; } + void SetMockModuleList(const std::map &list) + { + for (auto it = list.begin(); it != list.end(); ++it) { + mockModuleList_.emplace(it->first.c_str(), it->second.c_str()); + } + } + + inline bool IsMockModule(const CString &moduleStr) const + { + if (mockModuleList_.empty()) { + return false; + } + auto it = mockModuleList_.find(moduleStr); + if (it == mockModuleList_.end()) { + return false; + } + return true; + } + + inline CString GetMockModule(const CString &module) const + { + auto it = mockModuleList_.find(module); + if (it == mockModuleList_.end()) { + LOG_ECMA(FATAL) << " Get Mock Module failed"; + } + return it->second; + } + #if defined(ECMASCRIPT_SUPPORT_HEAPPROFILER) void DeleteHeapProfile(); + HeapProfilerInterface *GetHeapProfile(); HeapProfilerInterface *GetOrNewHeapProfile(); + void StartHeapTracking(); + void StopHeapTracking(); #endif bool EnableReportModuleResolvingFailure() const @@ -417,10 +446,33 @@ public: ASSERT(stringTable_ != nullptr); return stringTable_; } - JSTaggedValue GetHClassClass() const + + void IncreaseCallDepth() { - return hClassClass_; + callDepth_++; } + + void DecreaseCallDepth() + { + ASSERT(callDepth_ > 0); + callDepth_--; + } + + bool IsTopLevelCallDepth() + { + return callDepth_ == 0; + } + + void SetProfilerState(bool state) + { + isProfiling_ = state; + } + + bool GetProfilerState() + { + return isProfiling_; + } + protected: void PrintJSErrorInfo(const JSHandle &exceptionInfo) const; @@ -451,7 +503,6 @@ private: CList nativePointerList_; // VM execution states. JSThread *thread_ {nullptr}; - JSTaggedValue hClassClass_ {JSTaggedValue::Hole()}; // VM resources. SnapshotEnv *snapshotEnv_ {nullptr}; @@ -466,14 +517,22 @@ private: CString assetPath_; CString bundleName_; CString moduleName_; - // Registered Callbacks + CList deregisterModuleList_; + CMap mockModuleList_; + NativePtrGetter nativePtrGetter_ {nullptr}; + SourceMapTranslateCallback sourceMapTranslateCallback_ {nullptr}; void *loop_ {nullptr}; - // CJS resolve path Callbacks - ResolvePathCallback resolvePathCallback_ {nullptr}; + // resolve path to get abc's buffer ResolveBufferCallback resolveBufferCallback_ {nullptr}; + // delete the native module and dlclose so from NativeModuleManager + UnloadNativeModuleCallback unloadNativeModuleCallback_ {nullptr}; + + // trigger local aot + RequestAotCallback requestAotCallback_ {nullptr}; + // Concurrent taskpool callback and data ConcurrentCallback concurrentCallback_ {nullptr}; void *concurrentData_ {nullptr}; @@ -494,6 +553,10 @@ private: // PGO Profiler PGOProfiler *pgoProfiler_ {nullptr}; + // c++ call js + size_t callDepth_ {0}; + + bool isProfiling_ {false}; friend class Snapshot; friend class SnapshotProcessor; @@ -503,7 +566,7 @@ private: friend class JSPandaFileExecutor; friend class EcmaContext; CMap workerList_ {}; - os::memory::Mutex mutex_; + Mutex mutex_; }; } // namespace ecmascript } // namespace panda diff --git a/ecmascript/elements.cpp b/ecmascript/elements.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6b27ab04b75af5d1ad8b9f468003b963f06b94d3 --- /dev/null +++ b/ecmascript/elements.cpp @@ -0,0 +1,120 @@ +/* + * 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/elements.h" +#include "ecmascript/global_env_constants.h" +#include "ecmascript/js_tagged_value-inl.h" + +namespace panda::ecmascript { +CMap Elements::InitializeHClassMap() +{ + CMap result; + result.emplace(ElementsKind::NONE, ConstantIndex::ELEMENT_NONE_HCLASS_INDEX); + result.emplace(ElementsKind::INT, ConstantIndex::ELEMENT_INT_HCLASS_INDEX); + result.emplace(ElementsKind::NUMBER, ConstantIndex::ELEMENT_NUMBER_HCLASS_INDEX); + result.emplace(ElementsKind::STRING, ConstantIndex::ELEMENT_STRING_HCLASS_INDEX); + result.emplace(ElementsKind::OBJECT, ConstantIndex::ELEMENT_OBJECT_HCLASS_INDEX); + result.emplace(ElementsKind::TAGGED, ConstantIndex::ELEMENT_TAGGED_HCLASS_INDEX); + result.emplace(ElementsKind::HOLE_INT, ConstantIndex::ELEMENT_HOLE_INT_HCLASS_INDEX); + result.emplace(ElementsKind::HOLE_NUMBER, ConstantIndex::ELEMENT_HOLE_NUMBER_HCLASS_INDEX); + result.emplace(ElementsKind::HOLE_STRING, ConstantIndex::ELEMENT_HOLE_STRING_HCLASS_INDEX); + result.emplace(ElementsKind::HOLE_OBJECT, ConstantIndex::ELEMENT_HOLE_OBJECT_HCLASS_INDEX); + result.emplace(ElementsKind::HOLE_TAGGED, ConstantIndex::ELEMENT_HOLE_TAGGED_HCLASS_INDEX); + return result; +} + +std::string Elements::GetString(ElementsKind kind) +{ + return std::to_string(static_cast(kind)); +} + +bool Elements::IsInt(ElementsKind kind) +{ + return kind == ElementsKind::INT; +} + +bool Elements::IsNumber(ElementsKind kind) +{ + return kind == ElementsKind::NUMBER; +} + +bool Elements::IsTagged(ElementsKind kind) +{ + return kind == ElementsKind::TAGGED; +} + +bool Elements::IsObject(ElementsKind kind) +{ + return kind == ElementsKind::OBJECT; +} + +bool Elements::IsHole(ElementsKind kind) +{ + static constexpr uint8_t EVEN_NUMBER = 2; + return static_cast(kind) % EVEN_NUMBER == 1; +} + +ElementsKind Elements::MergeElementsKind(ElementsKind curKind, ElementsKind newKind) +{ + auto result = ElementsKind(static_cast(curKind) | static_cast(newKind)); + ASSERT(result != ElementsKind::NONE); + result = FixElementsKind(result); + return result; +} + +ElementsKind Elements::FixElementsKind(ElementsKind oldKind) +{ + auto result = oldKind; + switch (result) { + case ElementsKind::NONE: + case ElementsKind::INT: + case ElementsKind::NUMBER: + case ElementsKind::STRING: + case ElementsKind::OBJECT: + case ElementsKind::HOLE_INT: + case ElementsKind::HOLE_NUMBER: + case ElementsKind::HOLE_STRING: + case ElementsKind::HOLE_OBJECT: + break; + default: + if (IsHole(result)) { + result = ElementsKind::HOLE_TAGGED; + } else { + result = ElementsKind::TAGGED; + } + break; + } + return result; +} + +ElementsKind Elements::ToElementsKind(JSTaggedValue value, ElementsKind kind) +{ + ElementsKind valueKind = ElementsKind::NONE; + if (value.IsInt()) { + valueKind = ElementsKind::INT; + } else if (value.IsDouble()) { + valueKind = ElementsKind::NUMBER; + } else if (value.IsString()) { + valueKind = ElementsKind::STRING; + } else if (value.IsHeapObject()) { + valueKind = ElementsKind::OBJECT; + } else if (value.IsHole()) { + valueKind = ElementsKind::HOLE; + } else { + valueKind = ElementsKind::TAGGED; + } + return MergeElementsKind(valueKind, kind); +} +} // namespace panda::ecmascript diff --git a/ecmascript/elements.h b/ecmascript/elements.h new file mode 100644 index 0000000000000000000000000000000000000000..0fc3a9d2dd34371a92aee57c911910ecbe474ac2 --- /dev/null +++ b/ecmascript/elements.h @@ -0,0 +1,70 @@ +/* + * 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_ELEMENTS_H +#define ECMASCRIPT_ELEMENTS_H + +#include "ecmascript/global_env_constants.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/mem/c_containers.h" + +namespace panda::ecmascript { +enum class ElementsKind : uint8_t { + NONE = 0x00UL, + HOLE = 0x01UL, + INT = 0x1UL << 1, // 2 + NUMBER = (0x1UL << 2) | INT, // 6 + STRING = 0x1UL << 3, // 8 + OBJECT = 0x1UL << 4, // 16 + TAGGED = 0x1EUL, // 30 + HOLE_INT = HOLE | INT, + HOLE_NUMBER = HOLE | NUMBER, + HOLE_STRING = HOLE | STRING, + HOLE_OBJECT = HOLE | OBJECT, + HOLE_TAGGED = HOLE | TAGGED, + GENERIC = HOLE_TAGGED, +}; + +class Elements { +public: + static CMap InitializeHClassMap(); + + static std::string GetString(ElementsKind kind); + static bool IsInt(ElementsKind kind); + static bool IsNumber(ElementsKind kind); + static bool IsTagged(ElementsKind kind); + static bool IsObject(ElementsKind kind); + static bool IsHole(ElementsKind kind); + static bool IsGeneric(ElementsKind kind) + { + return kind == ElementsKind::GENERIC; + } + + static bool IsNone(ElementsKind kind) + { + return kind == ElementsKind::NONE; + } + + static bool IsComplex(ElementsKind kind) + { + return IsNumber(kind) || IsTagged(kind); + } + + static ElementsKind MergeElementsKind(ElementsKind curKind, ElementsKind newKind); + static ElementsKind FixElementsKind(ElementsKind oldKind); + static ElementsKind ToElementsKind(JSTaggedValue value, ElementsKind kind); +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_ELEMENTS_H diff --git a/ecmascript/filter_helper.h b/ecmascript/filter_helper.h index f9f4f6da69dad4c24a020b66898546ea9667e931..f8f7e0b1c5c2e920832eca8b6322307ede66acca 100644 --- a/ecmascript/filter_helper.h +++ b/ecmascript/filter_helper.h @@ -16,8 +16,8 @@ #ifndef ECMASCRIPT_FILTER_HELPER_H #define ECMASCRIPT_FILTER_HELPER_H -#include "ecmascript/property_attributes.h" #include "ecmascript/object_operator.h" +#include "ecmascript/property_attributes.h" #define NATIVE_DEFAULT 0 #define NATIVE_WRITABLE 1 << 0 diff --git a/ecmascript/frames.cpp b/ecmascript/frames.cpp index f3d0014a690e8ba1d2e0c75b1b934646ede5064f..49cc99d0157176fa1e109d472533f866b4dcb2e0 100644 --- a/ecmascript/frames.cpp +++ b/ecmascript/frames.cpp @@ -103,7 +103,7 @@ JSTaggedValue FrameIterator::GetFunction() const return JSTaggedValue::Undefined(); } default: { - LOG_FULL(FATAL) << "frame type error!"; + LOG_FULL(FATAL) << "Unknown frame type: " << static_cast(type); UNREACHABLE(); } } @@ -388,12 +388,29 @@ uintptr_t FrameIterator::GetPrevFrameCallSiteSp() const break; } default: { - LOG_FULL(FATAL) << "frame type error!"; + LOG_FULL(FATAL) << "Unknown frame type: " << static_cast(type); } } return 0; } +std::map FrameIterator::GetInlinedMethodInfo() +{ + std::map inlineMethodInfos; + FrameType type = this->GetFrameType(); + switch (type) { + case FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME: + case FrameType::OPTIMIZED_JS_FUNCTION_FRAME: { + CollectMethodOffsetInfo(inlineMethodInfos); + break; + } + default: { + break; + } + } + return inlineMethodInfos; +} + uint32_t FrameIterator::GetBytecodeOffset() const { FrameType type = this->GetFrameType(); @@ -451,7 +468,7 @@ uintptr_t FrameIterator::GetPrevFrame() const break; } default: { - LOG_FULL(FATAL) << "frame type error!"; + LOG_FULL(FATAL) << "Unknown frame type: " << static_cast(type); } } return end; @@ -486,6 +503,11 @@ void FrameIterator::CollectPcOffsetInfo(ConstInfo &info) const arkStackMapParser_->GetConstInfo(optimizedReturnAddr_, info, stackMapAddr_); } +void FrameIterator::CollectMethodOffsetInfo(std::map &info) const +{ + arkStackMapParser_->GetMethodOffsetInfo(optimizedReturnAddr_, info, stackMapAddr_); +} + void FrameIterator::CollectArkDeopt(std::vector& deopts) const { arkStackMapParser_->GetArkDeopt(optimizedReturnAddr_, stackMapAddr_, deopts); diff --git a/ecmascript/frames.h b/ecmascript/frames.h index bb2a0f4df67168532df4a5878c374a1654bbb50f..4af8165f72b82a6d9d32c21ef7a0e3f0760d76a0 100644 --- a/ecmascript/frames.h +++ b/ecmascript/frames.h @@ -648,12 +648,12 @@ struct InterpretedFrameBase : public base::AlignedStruct(const_cast(sp)) - 1; } - static size_t GetTypeOffset(bool isArch32 = false) + static size_t GetPrevOffset(bool isArch32 = false) { return GetOffset(Index::PrevIndex)>(isArch32); } - static size_t GetPrevOffset(bool isArch32 = false) + static size_t GetTypeOffset(bool isArch32 = false) { return GetOffset(Index::TypeIndex)>(isArch32); } @@ -1625,6 +1625,7 @@ public: int ComputeDelta() const; template void Advance(); + std::map GetInlinedMethodInfo(); uint32_t GetBytecodeOffset() const; uintptr_t GetPrevFrameCallSiteSp() const; uintptr_t GetPrevFrame() const; @@ -1642,6 +1643,7 @@ public: } bool IteratorStackMap(const RootVisitor &visitor, const RootBaseAndDerivedVisitor &derivedVisitor) const; void CollectPcOffsetInfo(ConstInfo &info) const; + void CollectMethodOffsetInfo(std::map &info) const; void CollectArkDeopt(std::vector& deopts) const; std::tuple CalCallSiteInfo(uintptr_t retAddr) const; int GetCallSiteDelta(uintptr_t retAddr) const; diff --git a/ecmascript/global_dictionary-inl.h b/ecmascript/global_dictionary-inl.h index 44b6b06af3757b1eb2a5174baf219a207c4b1415..ee910a3a4c386730b270c5b88c964aeb2f6b41aa 100644 --- a/ecmascript/global_dictionary-inl.h +++ b/ecmascript/global_dictionary-inl.h @@ -205,11 +205,17 @@ void GlobalDictionary::InvalidatePropertyBox(JSThread *thread, const JSHandle &dictHandle, int entry, const JSHandle &oldValue) { + if (!dictHandle->IsValidateBox(entry)) { + return; + } ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); // Swap with a copy. JSHandle newBox = factory->NewPropertyBox(oldValue); PropertyBox *box = dictHandle->GetBox(entry); PropertyAttributes attr = dictHandle->GetAttributes(entry); + if (!attr.IsConfigurable() || box->GetValue().IsHole()) { + return; + } ASSERT_PRINT(attr.IsConfigurable(), "property must be configurable"); ASSERT_PRINT(!box->GetValue().IsHole(), "value must not be hole"); @@ -219,6 +225,12 @@ void GlobalDictionary::InvalidateAndReplaceEntry(JSThread *thread, const JSHandl dictHandle->UpdateValue(thread, entry, newBox.GetTaggedValue()); box->Clear(thread); } + +bool GlobalDictionary::IsValidateBox(int entry) const +{ + int index = GetEntryIndex(entry) + ENTRY_VALUE_INDEX; + return !Get(index).IsUndefined(); +} } // namespace ecmascript } // namespace panda diff --git a/ecmascript/global_dictionary.h b/ecmascript/global_dictionary.h index c913862092c90b3b113e48a7ea328b22a684f16f..ccfb0fc92580b23e9a6e4c3df03172abcf993451 100644 --- a/ecmascript/global_dictionary.h +++ b/ecmascript/global_dictionary.h @@ -54,6 +54,8 @@ public: inline static void InvalidateAndReplaceEntry(JSThread *thread, const JSHandle &dictHandle, int entry, const JSHandle &oldValue); + + inline bool IsValidateBox(int entry) const; inline PropertyBox *GetBox(int entry) const; diff --git a/ecmascript/global_env.cpp b/ecmascript/global_env.cpp index 8157dee2623c8c31f782bc4934c4a468e524ed2f..909a121dc1ada30c13d059c936f1bf7e6e2d3ff0 100644 --- a/ecmascript/global_env.cpp +++ b/ecmascript/global_env.cpp @@ -41,6 +41,7 @@ void GlobalEnv::Init(JSThread *thread) stringTable->InternEmptyString(EcmaString::Cast(emptyStr.GetTaggedObject())); SetTemplateMap(thread, TemplateMap::Create(thread)); SetObjectLiteralHClassCache(thread, JSTaggedValue::Hole()); + SetJsonObjectHclassCache(thread, JSTaggedValue::Hole()); } JSHandle GlobalEnv::GetSymbol(JSThread *thread, const JSHandle &string) { diff --git a/ecmascript/global_env.h b/ecmascript/global_env.h index c4c7d2f95086d9a729b4937f91747005659a3f26..a2f0a4107a82b29ad3d32e37f0ee938e159b4e0d 100644 --- a/ecmascript/global_env.h +++ b/ecmascript/global_env.h @@ -21,12 +21,13 @@ #include "ecmascript/lexical_env.h" #include "ecmascript/js_handle.h" #include "ecmascript/global_env_constants-inl.h" +#include "ecmascript/property_detector.h" namespace panda::ecmascript { class JSThread; // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define GLOBAL_ENV_FIELDS(V) \ +#define GLOBAL_ENV_COMMON_FIELDS(V) \ /* Function */ \ V(JSTaggedValue, ObjectFunction, OBJECT_FUNCTION_INDEX) \ V(JSTaggedValue, ObjectFunctionClass, OBJECT_FUNCTION_CLASS_INDEX) \ @@ -77,6 +78,8 @@ class JSThread; V(JSTaggedValue, DisplayNamesFunction, DISPLAY_NAMES_FUNCTION_INDEX) \ V(JSTaggedValue, ListFormatFunction, LIST_FORMAT_FUNCTION_INDEX) \ V(JSTaggedValue, RegExpFunction, REGEXP_FUNCTION_INDEX) \ + V(JSTaggedValue, RegExpExecFunction, REGEXP_EXEC_FUNCTION_INDEX) \ + V(JSTaggedValue, RegExpPrototype, REGEXP_PROTOTYPE_INDEX) \ V(JSTaggedValue, BuiltinsSetFunction, BUILTINS_SET_FUNCTION_INDEX) \ V(JSTaggedValue, SetPrototype, SET_PROTOTYPE_INDEX) \ V(JSTaggedValue, BuiltinsMapFunction, BUILTINS_MAP_FUNCTION_INDEX) \ @@ -90,6 +93,7 @@ class JSThread; V(JSTaggedValue, AtomicsFunction, ATOMICS_FUNCTION_INDEX) \ V(JSTaggedValue, JsonFunction, JSON_FUNCTION_INDEX) \ V(JSTaggedValue, StringFunction, STRING_FUNCTION_INDEX) \ + V(JSTaggedValue, StringPrototype, STRING_PROTOTYPE_INDEX) \ V(JSTaggedValue, ProxyFunction, PROXY_FUNCTION_INDEX) \ V(JSTaggedValue, GeneratorFunctionFunction, GENERATOR_FUNCTION_OFFSET) \ V(JSTaggedValue, GeneratorFunctionPrototype, GENERATOR_FUNCTION_PROTOTYPE_OFFSET) \ @@ -111,10 +115,8 @@ class JSThread; V(JSTaggedValue, AsyncIteratorSymbol, ASYNC_ITERATOR_SYMBOL_INDEX) \ V(JSTaggedValue, MatchSymbol, MATCH_SYMBOL_INDEX) \ V(JSTaggedValue, MatchAllSymbol, MATCH_All_SYMBOL_INDEX) \ - V(JSTaggedValue, ReplaceSymbol, REPLACE_SYMBOL_INDEX) \ V(JSTaggedValue, SearchSymbol, SEARCH_SYMBOL_INDEX) \ V(JSTaggedValue, SpeciesSymbol, SPECIES_SYMBOL_INDEX) \ - V(JSTaggedValue, SplitSymbol, SPLIT_SYMBOL_INDEX) \ V(JSTaggedValue, ToPrimitiveSymbol, TOPRIMITIVE_SYMBOL_INDEX) \ V(JSTaggedValue, UnscopablesSymbol, UNSCOPABLES_SYMBOL_INDEX) \ V(JSTaggedValue, HoleySymbol, HOLEY_SYMBOL_OFFSET) \ @@ -196,7 +198,18 @@ class JSThread; V(JSTaggedValue, CjsExportsFunction, CJS_EXPORTS_FUNCTION_INDEX) \ V(JSTaggedValue, CjsRequireFunction, CJS_REQUIRE_FUNCTION_INDEX) \ V(JSTaggedValue, GlobalPatch, GLOBAL_PATCH) \ - V(JSTaggedValue, ExportOfScript, DEFAULT_EXPORT_OF_SCRIPT) + V(JSTaggedValue, ExportOfScript, DEFAULT_EXPORT_OF_SCRIPT) \ + V(JSTaggedValue, JsonObjectHclassCache, JSON_OBJECT_HCLASS_CACHE) + +// Maintain the same order with DETECTOR_SYMBOL_LIST +#define GLOBAL_ENV_DETECTOR_SYMBOL_FIELDS(V) \ + V(JSTaggedValue, ReplaceSymbol, REPLACE_SYMBOL_INDEX) \ + V(JSTaggedValue, SplitSymbol, SPLIT_SYMBOL_INDEX) \ + +#define GLOBAL_ENV_FIELDS(V) \ + GLOBAL_ENV_COMMON_FIELDS(V) \ + GLOBAL_ENV_DETECTOR_SYMBOL_FIELDS(V) \ + GLOBAL_ENV_DETECTOR_FIELDS(V) \ class GlobalEnv : public TaggedObject { public: @@ -243,6 +256,23 @@ public: FINAL_INDEX }; + static constexpr uint8_t FIRST_DETECTOR_SYMBOL_INDEX = Field::REPLACE_SYMBOL_INDEX; + static constexpr uint8_t LAST_DETECTOR_SYMBOL_INDEX = Field::SPLIT_SYMBOL_INDEX; + + static inline uintptr_t GetFirstDetectorSymbolAddr(const GlobalEnv *env) + { + constexpr size_t offset = HEADER_SIZE + FIRST_DETECTOR_SYMBOL_INDEX * JSTaggedValue::TaggedTypeSize(); + uintptr_t addr = reinterpret_cast(env) + offset; + return *reinterpret_cast(addr); + } + + static uintptr_t GetLastDetectorSymbolAddr(const GlobalEnv *env) + { + constexpr size_t offset = HEADER_SIZE + LAST_DETECTOR_SYMBOL_INDEX * JSTaggedValue::TaggedTypeSize(); + uintptr_t addr = reinterpret_cast(env) + offset; + return *reinterpret_cast(addr); + } + // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define GLOBAL_ENV_FIELD_ACCESSORS(type, name, index) \ inline JSHandle Get##name() const \ diff --git a/ecmascript/global_env_constants-inl.h b/ecmascript/global_env_constants-inl.h index cc984f747d9fe88ac7ea4674c060f468901d9703..c708004eec86176393a39b36572e0e0fe8b1c2a3 100644 --- a/ecmascript/global_env_constants-inl.h +++ b/ecmascript/global_env_constants-inl.h @@ -74,6 +74,7 @@ inline uintptr_t GlobalEnvConstants::GetGlobalConstantAddr(ConstantIndex index) GLOBAL_ENV_CONSTANT_SPECIAL(DECL_GET_IMPL) // NOLINT(readability-const-return-type) GLOBAL_ENV_CONSTANT_CONSTANT(DECL_GET_IMPL) // NOLINT(readability-const-return-type) GLOBAL_ENV_CONSTANT_ACCESSOR(DECL_GET_IMPL) // NOLINT(readability-const-return-type) + GLOBAL_ENV_CACHES(DECL_GET_IMPL) // NOLINT(readability-const-return-type) #undef DECL_GET_IMPL // clang-format on } // namespace panda::ecmascript diff --git a/ecmascript/global_env_constants.cpp b/ecmascript/global_env_constants.cpp index a1087a0d1706cab77d8dc2bb018368897c91f980..392efd31a27363a378094091dd9883db6af4d4c5 100644 --- a/ecmascript/global_env_constants.cpp +++ b/ecmascript/global_env_constants.cpp @@ -18,6 +18,7 @@ #include "ecmascript/accessor_data.h" #include "ecmascript/builtins/builtins.h" #include "ecmascript/builtins/builtins_global.h" +#include "ecmascript/ecma_string-inl.h" #include "ecmascript/ecma_vm.h" #include "ecmascript/free_object.h" #include "ecmascript/global_env.h" @@ -67,6 +68,7 @@ #include "ecmascript/js_symbol.h" #include "ecmascript/js_tagged_value.h" #include "ecmascript/js_thread.h" +#include "ecmascript/marker_cell.h" #include "ecmascript/method.h" #include "ecmascript/module/js_module_source_text.h" #include "ecmascript/object_factory.h" @@ -79,6 +81,7 @@ void GlobalEnvConstants::Init(JSThread *thread, JSHClass *hClass) { InitRootsClass(thread, hClass); InitGlobalConstant(thread); + InitGlobalCaches(); } void GlobalEnvConstants::InitRootsClass(JSThread *thread, JSHClass *hClass) @@ -94,6 +97,8 @@ void GlobalEnvConstants::InitRootsClass(JSThread *thread, JSHClass *hClass) SetConstant(ConstantIndex::FREE_OBJECT_WITH_TWO_FIELD_CLASS_INDEX, factory->NewEcmaReadOnlyHClass(hClass, FreeObject::SIZE, JSType::FREE_OBJECT_WITH_TWO_FIELD)); SetConstant(ConstantIndex::LINE_STRING_CLASS_INDEX, factory->NewEcmaReadOnlyHClass(hClass, 0, JSType::LINE_STRING)); + SetConstant(ConstantIndex::SLICED_STRING_CLASS_INDEX, + factory->NewEcmaReadOnlyHClass(hClass, 0, JSType::SLICED_STRING)); SetConstant(ConstantIndex::CONSTANT_STRING_CLASS_INDEX, factory->NewEcmaReadOnlyHClass(hClass, 0, JSType::CONSTANT_STRING)); SetConstant(ConstantIndex::TREE_STRING_CLASS_INDEX, factory->NewEcmaReadOnlyHClass(hClass, 0, JSType::TREE_STRING)); @@ -103,6 +108,8 @@ void GlobalEnvConstants::InitRootsClass(JSThread *thread, JSHClass *hClass) factory->NewEcmaReadOnlyHClass(hClass, 0, JSType::BYTE_ARRAY)); SetConstant(ConstantIndex::CONSTANT_POOL_CLASS_INDEX, factory->NewEcmaReadOnlyHClass(hClass, 0, JSType::CONSTANT_POOL)); + SetConstant(ConstantIndex::PROFILE_TYPE_INFO_CLASS_INDEX, + factory->NewEcmaReadOnlyHClass(hClass, 0, JSType::PROFILE_TYPE_INFO)); SetConstant(ConstantIndex::AOT_LITERAL_INFO_CLASS_INDEX, factory->NewEcmaReadOnlyHClass(hClass, 0, JSType::AOT_LITERAL_INFO)); SetConstant(ConstantIndex::VTABLE_CLASS_INDEX, @@ -155,6 +162,10 @@ void GlobalEnvConstants::InitRootsClass(JSThread *thread, JSHClass *hClass) factory->NewEcmaReadOnlyHClass(hClass, ProtoChangeMarker::SIZE, JSType::PROTO_CHANGE_MARKER)); SetConstant(ConstantIndex::PROTO_CHANGE_DETAILS_CLASS_INDEX, factory->NewEcmaReadOnlyHClass(hClass, ProtoChangeDetails::SIZE, JSType::PROTOTYPE_INFO)); + SetConstant(ConstantIndex::MARKER_CELL_CLASS_INDEX, + factory->NewEcmaReadOnlyHClass(hClass, MarkerCell::SIZE, JSType::MARKER_CELL)); + SetConstant(ConstantIndex::TRACK_INFO_CLASS_INDEX, + factory->NewEcmaReadOnlyHClass(hClass, TrackInfo::SIZE, JSType::TRACK_INFO)); SetConstant(ConstantIndex::PROTOTYPE_HANDLER_CLASS_INDEX, factory->NewEcmaReadOnlyHClass(hClass, PrototypeHandler::SIZE, JSType::PROTOTYPE_HANDLER)); SetConstant(ConstantIndex::TRANSITION_HANDLER_CLASS_INDEX, @@ -384,6 +395,7 @@ void GlobalEnvConstants::InitGlobalConstant(JSThread *thread) SetConstant(ConstantIndex::TO_JSON_STRING_INDEX, factory->NewFromASCIINonMovable("toJSON")); SetConstant(ConstantIndex::GLOBAL_STRING_INDEX, factory->NewFromASCIINonMovable("global")); SetConstant(ConstantIndex::MESSAGE_STRING_INDEX, factory->NewFromASCIINonMovable("message")); + SetConstant(ConstantIndex::CAUSE_STRING_INDEX, factory->NewFromASCIINonMovable("cause")); SetConstant(ConstantIndex::ERROR_STRING_INDEX, factory->NewFromASCIINonMovable("Error")); SetConstant(ConstantIndex::ERRORS_STRING_INDEX, factory->NewFromASCII("errors")); SetConstant(ConstantIndex::AGGREGATE_ERROR_STRING_INDEX, factory->NewFromASCII("AggregateError")); @@ -536,6 +548,8 @@ void GlobalEnvConstants::InitGlobalConstant(JSThread *thread) SetConstant(ConstantIndex::GREGORY_INDEX, factory->NewFromASCIINonMovable("gregory")); SetConstant(ConstantIndex::ETHIOAA_INDEX, factory->NewFromASCIINonMovable("ethioaa")); SetConstant(ConstantIndex::STICKY_INDEX, factory->NewFromASCIINonMovable("sticky")); + SetConstant(ConstantIndex::HAS_INDICES_INDEX, factory->NewFromASCIINonMovable("hasIndices")); + SetConstant(ConstantIndex::INDICES_INDEX, factory->NewFromASCIINonMovable("indices")); SetConstant(ConstantIndex::U_INDEX, factory->NewFromASCIINonMovable("u")); SetConstant(ConstantIndex::INDEX_INDEX, factory->NewFromASCIINonMovable("index")); SetConstant(ConstantIndex::INPUT_INDEX, factory->NewFromASCIINonMovable("input")); @@ -593,6 +607,19 @@ void GlobalEnvConstants::InitGlobalConstant(JSThread *thread) SetConstant(ConstantIndex::DOLLAR_STRING_SEVEN_INDEX, factory->NewFromASCIINonMovable("$7")); SetConstant(ConstantIndex::DOLLAR_STRING_EIGHT_INDEX, factory->NewFromASCIINonMovable("$8")); SetConstant(ConstantIndex::DOLLAR_STRING_NINE_INDEX, factory->NewFromASCIINonMovable("$9")); + // for object to string + SetConstant(ConstantIndex::UNDEFINED_TO_STRING_INDEX, factory->NewFromASCIINonMovable("[object Undefined]")); + SetConstant(ConstantIndex::NULL_TO_STRING_INDEX, factory->NewFromASCIINonMovable("[object Null]")); + SetConstant(ConstantIndex::OBJECT_TO_STRING_INDEX, factory->NewFromASCIINonMovable("[object Object]")); + SetConstant(ConstantIndex::ARRAY_TO_STRING_INDEX, factory->NewFromASCIINonMovable("[object Array]")); + SetConstant(ConstantIndex::STRING_TO_STRING_INDEX, factory->NewFromASCIINonMovable("[object String]")); + SetConstant(ConstantIndex::BOOLEAN_TO_STRING_INDEX, factory->NewFromASCIINonMovable("[object Boolean]")); + SetConstant(ConstantIndex::NUMBER_TO_STRING_INDEX, factory->NewFromASCIINonMovable("[object Number]")); + SetConstant(ConstantIndex::ARGUMENTS_TO_STRING_INDEX, factory->NewFromASCIINonMovable("[object Arguments]")); + SetConstant(ConstantIndex::FUNCTION_TO_STRING_INDEX, factory->NewFromASCIINonMovable("[object Function]")); + SetConstant(ConstantIndex::DATE_TO_STRING_INDEX, factory->NewFromASCIINonMovable("[object Date]")); + SetConstant(ConstantIndex::ERROR_TO_STRING_INDEX, factory->NewFromASCIINonMovable("[object Error]")); + SetConstant(ConstantIndex::REGEXP_TO_STRING_INDEX, factory->NewFromASCIINonMovable("[object RegExp]")); auto accessor = factory->NewInternalAccessor(reinterpret_cast(JSFunction::PrototypeSetter), reinterpret_cast(JSFunction::PrototypeGetter)); @@ -609,6 +636,19 @@ void GlobalEnvConstants::InitGlobalConstant(JSThread *thread) InitClassConstructorOptimizedClass(factory); } +void GlobalEnvConstants::InitGlobalCaches() +{ + SetConstant(ConstantIndex::CACHED_JSCOLLATOR_LOCALES_INDEX, JSTaggedValue::Undefined()); +} + +void GlobalEnvConstants::SetCachedLocales(JSTaggedValue value) +{ + JSTaggedValue cached = GetCachedJSCollatorLocales(); + if (cached.IsUndefined()) { + SetConstant(ConstantIndex::CACHED_JSCOLLATOR_LOCALES_INDEX, value); + } +} + void GlobalEnvConstants::InitJSAPIContainers() { for (size_t i = GetJSAPIContainersBegin(); i <= GetJSAPIContainersEnd(); i++) { @@ -633,4 +673,17 @@ void GlobalEnvConstants::InitClassConstructorOptimizedClass(ObjectFactory *facto fastCall->SetCanFastCall(true); SetConstant(ConstantIndex::CLASS_CONSTRUCTOR_OPTIMIZED_WITH_FAST_CALL_HCLASS_INDEX, fastCall); } + +void GlobalEnvConstants::InitElementKindHClass(const JSThread *thread, JSHandle originHClass) +{ + auto map = thread->GetArrayHClassIndexMap(); + for (auto iter : map) { + JSHandle hclass = originHClass; + if (iter.first != ElementsKind::GENERIC) { + hclass = JSHClass::Clone(thread, originHClass); + hclass->SetElementsKind(iter.first); + } + SetConstant(iter.second, hclass); + } +} } // namespace panda::ecmascript diff --git a/ecmascript/global_env_constants.h b/ecmascript/global_env_constants.h index 096cd77410d7814c3970a7148b9ce445404be081..ed115b7e38dc7259634c3d85dd723c60c974b121 100644 --- a/ecmascript/global_env_constants.h +++ b/ecmascript/global_env_constants.h @@ -37,11 +37,13 @@ class ObjectFactory; V(JSTaggedValue, FreeObjectWithOneFieldClass, FREE_OBJECT_WITH_ONE_FIELD_CLASS_INDEX, ecma_roots_class) \ V(JSTaggedValue, FreeObjectWithTwoFieldClass, FREE_OBJECT_WITH_TWO_FIELD_CLASS_INDEX, ecma_roots_class) \ V(JSTaggedValue, LineStringClass, LINE_STRING_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, SlicedStringClass, SLICED_STRING_CLASS_INDEX, ecma_roots_class) \ V(JSTaggedValue, ConstantStringClass, CONSTANT_STRING_CLASS_INDEX, ecma_roots_class) \ V(JSTaggedValue, TreeStringClass, TREE_STRING_CLASS_INDEX, ecma_roots_class) \ V(JSTaggedValue, ArrayClass, ARRAY_CLASS_INDEX, ecma_roots_class) \ V(JSTaggedValue, ByteArrayClass, BYTE_ARRAY_CLASS_INDEX, ecma_roots_class) \ V(JSTaggedValue, ConstantPoolClass, CONSTANT_POOL_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, ProfileTypeInfoClass, PROFILE_TYPE_INFO_CLASS_INDEX, ecma_roots_class) \ V(JSTaggedValue, DictionaryClass, DICTIONARY_CLASS_INDEX, ecma_roots_class) \ V(JSTaggedValue, COWArrayClass, COW_ARRAY_CLASS_INDEX, ecma_roots_class) \ V(JSTaggedValue, BigIntClass, BIGINT_CLASS_INDEX, ecma_roots_class) \ @@ -64,6 +66,8 @@ class ObjectFactory; V(JSTaggedValue, PendingJobClass, PENDING_JOB_CLASS_INDEX, ecma_roots_class) \ V(JSTaggedValue, ProtoChangeMarkerClass, PROTO_CHANGE_MARKER_CLASS_INDEX, ecma_roots_class) \ V(JSTaggedValue, ProtoChangeDetailsClass, PROTO_CHANGE_DETAILS_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, MarkerCellClass, MARKER_CELL_CLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, TrackInfoClass, TRACK_INFO_CLASS_INDEX, ecma_roots_class) \ V(JSTaggedValue, PrototypeHandlerClass, PROTOTYPE_HANDLER_CLASS_INDEX, ecma_roots_class) \ V(JSTaggedValue, TransitionHandlerClass, TRANSITION_HANDLER_CLASS_INDEX, ecma_roots_class) \ V(JSTaggedValue, TransWithProtoHandlerClass, TRANS_WITH_PROTO_HANDLER_CLASS_INDEX, ecma_roots_class) \ @@ -126,6 +130,17 @@ class ObjectFactory; V(JSTaggedValue, AOTLiteralInfoClass, AOT_LITERAL_INFO_CLASS_INDEX, ecma_roots_class) \ V(JSTaggedValue, VTableClass, VTABLE_CLASS_INDEX, ecma_roots_class) \ V(JSTaggedValue, ClassLiteralClass, CLASS_LITERAL_HCLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, ElementNoneClass, ELEMENT_NONE_HCLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, ElementIntClass, ELEMENT_INT_HCLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, ElementNumberClass, ELEMENT_NUMBER_HCLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, ElementStringClass, ELEMENT_STRING_HCLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, ElementObjectClass, ELEMENT_OBJECT_HCLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, ElementTaggedClass, ELEMENT_TAGGED_HCLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, ElementHoleIntClass, ELEMENT_HOLE_INT_HCLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, ElementHoleNumberClass, ELEMENT_HOLE_NUMBER_HCLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, ElementHoleStringClass, ELEMENT_HOLE_STRING_HCLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, ElementHoleObjectClass, ELEMENT_HOLE_OBJECT_HCLASS_INDEX, ecma_roots_class) \ + V(JSTaggedValue, ElementHoleTaggedClass, ELEMENT_HOLE_TAGGED_HCLASS_INDEX, ecma_roots_class) // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define GLOBAL_ENV_CONSTANT_SPECIAL(V) \ @@ -143,7 +158,10 @@ class ObjectFactory; V(JSTaggedValue, MathACosFunction, MATH_ACOS_FUNCTION_INDEX, ecma_roots_special) \ V(JSTaggedValue, MathATanFunction, MATH_ATAN_FUNCTION_INDEX, ecma_roots_special) \ V(JSTaggedValue, MathAbsFunction, MATH_ABS_FUNCTION_INDEX, ecma_roots_special) \ - V(JSTaggedValue, MathFloorFunction, MATH_FLOOR_FUNCTION_INDEX, ecma_roots_special) + V(JSTaggedValue, MathFloorFunction, MATH_FLOOR_FUNCTION_INDEX, ecma_roots_special) \ + V(JSTaggedValue, LocaleCompareFunction, LOCALE_COMPARE_FUNCTION_INDEX, ecma_roots_special) \ + V(JSTaggedValue, ArraySortFunction, ARRAY_SORT_FUNCTION_INDEX, ecma_roots_special) \ + V(JSTaggedValue, JsonStringifyFunction, JSON_STRINGIFY_FUNCTION_INDEX, ecma_roots_special) /* GlobalConstant */ // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) @@ -246,6 +264,7 @@ class ObjectFactory; V(JSTaggedValue, ToJsonString, TO_JSON_STRING_INDEX, toJSON) \ V(JSTaggedValue, GlobalString, GLOBAL_STRING_INDEX, global) \ V(JSTaggedValue, MessageString, MESSAGE_STRING_INDEX, message) \ + V(JSTaggedValue, CauseString, CAUSE_STRING_INDEX, cause) \ V(JSTaggedValue, ErrorString, ERROR_STRING_INDEX, Error) \ V(JSTaggedValue, RangeErrorString, RANGE_ERROR_STRING_INDEX, RangeError) \ V(JSTaggedValue, ReferenceErrorString, REFERENCE_ERROR_STRING_INDEX, ReferenceError) \ @@ -389,6 +408,8 @@ class ObjectFactory; V(JSTaggedValue, GregoryString, GREGORY_INDEX, gregory) \ V(JSTaggedValue, EthioaaString, ETHIOAA_INDEX, ethioaa) \ V(JSTaggedValue, StickyString, STICKY_INDEX, sticky) \ + V(JSTaggedValue, HasIndicesString, HAS_INDICES_INDEX, hasIndices) \ + V(JSTaggedValue, IndicesString, INDICES_INDEX, indices) \ V(JSTaggedValue, UString, U_INDEX, u) \ V(JSTaggedValue, IndexString, INDEX_INDEX, index) \ V(JSTaggedValue, InputString, INPUT_INDEX, input) \ @@ -444,14 +465,29 @@ class ObjectFactory; V(JSTaggedValue, DollarStringSix, DOLLAR_STRING_SIX_INDEX, dollarStrSix) \ V(JSTaggedValue, DollarStringSeven, DOLLAR_STRING_SEVEN_INDEX, dollarStrSeven) \ V(JSTaggedValue, DollarStringEight, DOLLAR_STRING_EIGHT_INDEX, dollarStrEight) \ - V(JSTaggedValue, DollarStringNine, DOLLAR_STRING_NINE_INDEX, dollarStrNine) + V(JSTaggedValue, DollarStringNine, DOLLAR_STRING_NINE_INDEX, dollarStrNine) \ + /* for object to string */ \ + V(JSTaggedValue, UndefinedToString, UNDEFINED_TO_STRING_INDEX, undefinedToString) \ + V(JSTaggedValue, NullToString, NULL_TO_STRING_INDEX, nullToString) \ + V(JSTaggedValue, ObjectToString, OBJECT_TO_STRING_INDEX, objectToString) \ + V(JSTaggedValue, ArrayToString, ARRAY_TO_STRING_INDEX, arrayToString) \ + V(JSTaggedValue, StringToString, STRING_TO_STRING_INDEX, stringToString) \ + V(JSTaggedValue, BooleanToString, BOOLEAN_TO_STRING_INDEX, booleanToString) \ + V(JSTaggedValue, NumberToString, NUMBER_TO_STRING_INDEX, numberToString) \ + V(JSTaggedValue, ArgumentsToString, ARGUMENTS_TO_STRING_INDEX, argumentsToString) \ + V(JSTaggedValue, FunctionToString, FUNCTION_TO_STRING_INDEX, functionToString) \ + V(JSTaggedValue, DateToString, DATE_TO_STRING_INDEX, dateToString) \ + V(JSTaggedValue, ErrorToString, ERROR_TO_STRING_INDEX, errorToString) \ + V(JSTaggedValue, RegExpToString, REGEXP_TO_STRING_INDEX, regExpToString) // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define GLOBAL_ENV_CONSTANT_ACCESSOR(V) \ V(JSTaggedValue, FunctionPrototypeAccessor, FUNCTION_PROTOTYPE_ACCESSOR, ecma_roots_accessor) \ V(JSTaggedValue, FunctionNameAccessor, FUNCTION_NAME_ACCESSOR, ecma_roots_accessor) \ V(JSTaggedValue, ArrayLengthAccessor, ARRAY_LENGTH_ACCESSOR, ecma_roots_accessor) -/* RealmConstant */ + +#define GLOBAL_ENV_CACHES(V) \ + V(JSTaggedValue, CachedJSCollatorLocales, CACHED_JSCOLLATOR_LOCALES_INDEX, cachedCollatorLocales) // ConstantIndex used for explicit visit each constant. enum class ConstantIndex : size_t { @@ -459,6 +495,7 @@ enum class ConstantIndex : size_t { #define INDEX_FILTER(Type, Name, Index, Desc) Index, GLOBAL_ENV_CONSTANT_CLASS(INDEX_FILTER) GLOBAL_ENV_CONSTANT_SPECIAL(INDEX_FILTER) GLOBAL_ENV_CONSTANT_CONSTANT(INDEX_FILTER) GLOBAL_ENV_CONSTANT_ACCESSOR(INDEX_FILTER) + GLOBAL_ENV_CACHES(INDEX_FILTER) #undef INDEX_FILTER CONSTATNT_COUNT, @@ -492,11 +529,16 @@ public: void InitGlobalConstantSpecial(JSThread *thread); void InitGlobalConstant(JSThread *thread); + void InitGlobalCaches(); void InitJSAPIContainers(); void InitSpecialForSnapshot(); void InitClassConstructorOptimizedClass(ObjectFactory *factory); + void InitElementKindHClass(const JSThread *thread, JSHandle originHClass); + + void SetCachedLocales(JSTaggedValue value); + void SetConstant(ConstantIndex index, JSTaggedValue value); template @@ -514,6 +556,7 @@ public: GLOBAL_ENV_CONSTANT_SPECIAL(DECL_GET) GLOBAL_ENV_CONSTANT_CONSTANT(DECL_GET) GLOBAL_ENV_CONSTANT_ACCESSOR(DECL_GET) + GLOBAL_ENV_CACHES(DECL_GET) #undef DECL_GET void VisitRangeSlot(const RootRangeVisitor &visitor) diff --git a/ecmascript/ic/ic_binary_op.h b/ecmascript/ic/ic_binary_op.h index 1e47bd33e41dadc31853340f3f5097abcb748c60..c204659a3adec5377a28f44fd7394aa0746cc5eb 100644 --- a/ecmascript/ic/ic_binary_op.h +++ b/ecmascript/ic/ic_binary_op.h @@ -68,6 +68,7 @@ public: JSHandle stringA0 = JSHandle(JSHandle(thread, left)); JSHandle stringA1 = JSHandle(JSHandle(thread, right)); EcmaString *ret = EcmaStringAccessor::Concat(thread->GetEcmaVM(), stringA0, stringA1); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return JSTaggedValue(ret); } // Support cases, such as: string + null, string + object, string + boolean, string + number, etc. @@ -77,12 +78,16 @@ public: if (left.IsString()) { JSHandle stringA0 = JSHandle(leftValue); JSHandle stringA1 = JSTaggedValue::ToString(thread, rightValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); EcmaString *ret = EcmaStringAccessor::Concat(thread->GetEcmaVM(), stringA0, stringA1); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return JSTaggedValue(ret); } else { JSHandle stringA0 = JSTaggedValue::ToString(thread, leftValue); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle stringA1 = JSHandle(rightValue); EcmaString *ret = EcmaStringAccessor::Concat(thread->GetEcmaVM(), stringA0, stringA1); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return JSTaggedValue(ret); } } diff --git a/ecmascript/ic/ic_compare_op.h b/ecmascript/ic/ic_compare_op.h index 770f175da8e40ec016db74278b4744b37083ec6e..aacff9d7f7df4b01f6f6519878c91ebd793d6f41 100644 --- a/ecmascript/ic/ic_compare_op.h +++ b/ecmascript/ic/ic_compare_op.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_IC_IC_COMPARE_H -#define ECMASCRIPT_IC_IC_COMPARE_H +#ifndef ECMASCRIPT_IC_IC_COMPARE_OP_H +#define ECMASCRIPT_IC_IC_COMPARE_OP_H #include "ecmascript/js_function.h" #include "ecmascript/js_thread.h" @@ -75,4 +75,4 @@ public: JSTaggedValue right, CompareOpType operationType); }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_IC_IC_COMPAREOP_H \ No newline at end of file +#endif // ECMASCRIPT_IC_IC_COMPARE_OP_H \ No newline at end of file diff --git a/ecmascript/ic/ic_handler.h b/ecmascript/ic/ic_handler.h index 7e62da108f4ea12683a8bf3ead6cf90c061ed084..c6abc7e87445fc8cb851f2d6a17a06e9909c9581 100644 --- a/ecmascript/ic/ic_handler.h +++ b/ecmascript/ic/ic_handler.h @@ -29,6 +29,8 @@ public: FIELD, ELEMENT, DICTIONARY, + STRING, + STRING_LENGTH, NON_EXIST, }; @@ -38,7 +40,8 @@ public: using InternalAccessorBit = AccessorBit::NextFlag; using IsJSArrayBit = InternalAccessorBit::NextFlag; using OffsetBit = IsJSArrayBit::NextField; - using AttrIndexBit = OffsetBit::NextField; + using RepresentationBit = OffsetBit::NextField; + using AttrIndexBit = RepresentationBit::NextField; HandlerBase() = default; virtual ~HandlerBase() = default; @@ -63,11 +66,26 @@ public: return GetKind(handler) == HandlerKind::FIELD; } + static inline bool IsString(uint32_t handler) + { + return GetKind(handler) == HandlerKind::STRING; + } + + static inline bool IsStringLength(uint32_t handler) + { + return GetKind(handler) == HandlerKind::STRING_LENGTH; + } + static inline bool IsElement(uint32_t handler) { return GetKind(handler) == HandlerKind::ELEMENT; } + static inline bool IsStringElement(uint32_t handler) + { + return GetKind(handler) == HandlerKind::STRING; + } + static inline bool IsDictionary(uint32_t handler) { return GetKind(handler) == HandlerKind::DICTIONARY; @@ -112,8 +130,22 @@ public: } bool hasAccessor = op.IsAccessorDescriptor(); AccessorBit::Set(hasAccessor, &handler); + if (!hasAccessor) { - KindBit::Set(HandlerKind::FIELD, &handler); + if (op.GetReceiver()->IsString()) { + JSTaggedValue lenKey = thread->GlobalConstants()->GetLengthString(); + EcmaString *proKey = nullptr; + if (op.GetKey()->IsString()) { + proKey = EcmaString::Cast(op.GetKey()->GetTaggedObject()); + } + if (EcmaStringAccessor::StringsAreEqual(proKey, EcmaString::Cast(lenKey.GetTaggedObject()))) { + KindBit::Set(HandlerKind::STRING_LENGTH, &handler); + } else { + KindBit::Set(HandlerKind::STRING, &handler); + } + } else { + KindBit::Set(HandlerKind::FIELD, &handler); + } } if (op.IsInlinedProps()) { @@ -122,6 +154,7 @@ public: auto index = holder->GetJSHClass()->GetInlinedPropertiesIndex(op.GetIndex()); OffsetBit::Set(index, &handler); AttrIndexBit::Set(op.GetIndex(), &handler); + RepresentationBit::Set(op.GetRepresentation(), &handler); return JSHandle(thread, JSTaggedValue(handler)); } if (op.IsFastMode()) { @@ -129,16 +162,28 @@ public: uint32_t inlinePropNum = holder->GetJSHClass()->GetInlinedProperties(); AttrIndexBit::Set(op.GetIndex() + inlinePropNum, &handler); OffsetBit::Set(op.GetIndex(), &handler); + RepresentationBit::Set(Representation::TAGGED, &handler); return JSHandle(thread, JSTaggedValue(handler)); } LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); } - static inline JSHandle LoadElement(const JSThread *thread) + static inline JSHandle LoadElement(const JSThread *thread, const ObjectOperator &op) { uint32_t handler = 0; KindBit::Set(HandlerKind::ELEMENT, &handler); + + if (op.GetReceiver()->IsJSArray()) { + IsJSArrayBit::Set(true, &handler); + } + return JSHandle(thread, JSTaggedValue(handler)); + } + + static inline JSHandle LoadStringElement(const JSThread *thread) + { + uint32_t handler = 0; + KindBit::Set(HandlerKind::STRING, &handler); return JSHandle(thread, JSTaggedValue(handler)); } }; @@ -172,6 +217,7 @@ public: } AttrIndexBit::Set(op.GetIndex(), &handler); OffsetBit::Set(index, &handler); + RepresentationBit::Set(op.GetRepresentation(), &handler); return JSHandle(thread, JSTaggedValue(handler)); } ASSERT(op.IsFastMode()); @@ -179,6 +225,7 @@ public: uint32_t inlinePropNum = receiver->GetJSHClass()->GetInlinedProperties(); AttrIndexBit::Set(op.GetIndex() + inlinePropNum, &handler); OffsetBit::Set(op.GetIndex(), &handler); + RepresentationBit::Set(Representation::TAGGED, &handler); return JSHandle(thread, JSTaggedValue(handler)); } diff --git a/ecmascript/ic/ic_runtime.cpp b/ecmascript/ic/ic_runtime.cpp index d860b8a63b73e6a7dc9e353a719409eedbde146e..5d268ba49a6877b2902481a721d8d12d5041525a 100644 --- a/ecmascript/ic/ic_runtime.cpp +++ b/ecmascript/ic/ic_runtime.cpp @@ -41,12 +41,12 @@ void ICRuntime::UpdateLoadHandler(const ObjectOperator &op, JSHandle(); } JSHandle handlerValue; - JSHandle hclass(GetThread(), JSHandle::Cast(receiver)->GetClass()); + JSHandle hclass(GetThread(), receiver->GetTaggedObject()->GetClass()); if (op.IsElement()) { if (!op.IsFound() && hclass->IsDictionaryElement()) { return; } - handlerValue = LoadHandler::LoadElement(thread_); + handlerValue = LoadHandler::LoadElement(thread_, op); } else { if (!op.IsFound()) { JSTaggedValue proto = hclass->GetPrototype(); @@ -58,8 +58,8 @@ void ICRuntime::UpdateLoadHandler(const ObjectOperator &op, JSHandleIsString()) { return; } handlerValue = PrototypeHandler::LoadPrototype(thread_, op, hclass); @@ -79,6 +79,16 @@ void ICRuntime::UpdateLoadHandler(const ObjectOperator &op, JSHandle receiver) +{ + if (icAccessor_.GetICState() == ProfileTypeAccessor::ICState::MEGA) { + return; + } + JSHandle handlerValue = LoadHandler::LoadStringElement(thread_); + JSHandle hclass(GetThread(), receiver->GetTaggedObject()->GetClass()); + icAccessor_.AddElementHandler(JSHandle::Cast(hclass), handlerValue); +} + void ICRuntime::UpdateStoreHandler(const ObjectOperator &op, JSHandle key, JSHandle receiver) { @@ -95,7 +105,6 @@ void ICRuntime::UpdateStoreHandler(const ObjectOperator &op, JSHandle hclass(thread_, JSHandle::Cast(receiver)->GetClass()); handlerValue = StoreTSHandler::StoreAOT(thread_, op, hclass); } else if (op.IsTransition()) { - ASSERT(!op.IsElement()); if (op.IsOnPrototype()) { JSHandle hclass(thread_, JSHandle::Cast(receiver)->GetClass()); handlerValue = TransWithProtoHandler::StoreTransition(thread_, op, hclass); @@ -145,11 +154,55 @@ void ICRuntime::TraceIC([[maybe_unused]] JSHandle receiver, #endif } +JSTaggedValue LoadICRuntime::LoadValueMiss(JSHandle receiver, JSHandle key) +{ + if ((!receiver->IsJSObject() || receiver->HasOrdinaryGet()) && !receiver->IsString()) { + icAccessor_.SetAsMega(); + JSHandle propKey = JSTaggedValue::ToPropertyKey(thread_, key); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); + return JSTaggedValue::GetProperty(thread_, receiver, propKey).GetValue().GetTaggedValue(); + } + + ObjectOperator op(GetThread(), receiver, key); + auto result = JSHandle(thread_, JSObject::GetProperty(GetThread(), &op)); + + if (receiver->IsString()) { + // do not cache element + if (!op.IsFastMode()) { + icAccessor_.SetAsMega(); + return result.GetTaggedValue(); + } + UpdateLoadStringHandler(receiver); + } else { + if (op.GetValue().IsInternalAccessor()) { + op = ObjectOperator(GetThread(), receiver, key); + } + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(GetThread()); + // ic-switch + if (!GetThread()->GetEcmaVM()->ICEnabled()) { + icAccessor_.SetAsMega(); + return result.GetTaggedValue(); + } + TraceIC(receiver, key); + // do not cache element + if (!op.IsFastMode()) { + icAccessor_.SetAsMega(); + return result.GetTaggedValue(); + } + + UpdateLoadHandler(op, key, receiver); + } + + return result.GetTaggedValue(); +} + JSTaggedValue LoadICRuntime::LoadMiss(JSHandle receiver, JSHandle key) { - if (!receiver->IsJSObject() || receiver->HasOrdinaryGet()) { + if ((!receiver->IsJSObject() || receiver->HasOrdinaryGet()) && !receiver->IsString()) { icAccessor_.SetAsMega(); - return JSTaggedValue::GetProperty(thread_, receiver, key).GetValue().GetTaggedValue(); + JSHandle propKey = JSTaggedValue::ToPropertyKey(thread_, key); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); + return JSTaggedValue::GetProperty(thread_, receiver, propKey).GetValue().GetTaggedValue(); } ICKind kind = GetICKind(); @@ -225,6 +278,7 @@ JSTaggedValue StoreICRuntime::StoreMiss(JSHandle receiver, JSHand } } bool success = JSObject::SetProperty(&op, value, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); // ic-switch if (!GetThread()->GetEcmaVM()->ICEnabled()) { icAccessor_.SetAsMega(); diff --git a/ecmascript/ic/ic_runtime.h b/ecmascript/ic/ic_runtime.h index 350fa71bdf10bd8adfd555a5a5171bc1e9a6eb9c..54ae9b881f8a9dcabaec884f2ce6a9931fa1ffc5 100644 --- a/ecmascript/ic/ic_runtime.h +++ b/ecmascript/ic/ic_runtime.h @@ -37,6 +37,7 @@ public: ~ICRuntime() = default; void UpdateLoadHandler(const ObjectOperator &op, JSHandle key, JSHandle receiver); + void UpdateLoadStringHandler(JSHandle receiver); void UpdateStoreHandler(const ObjectOperator &op, JSHandle key, JSHandle receiver); JSThread *GetThread() const @@ -72,6 +73,7 @@ public: ~LoadICRuntime() = default; JSTaggedValue LoadMiss(JSHandle receiver, JSHandle key); + JSTaggedValue LoadValueMiss(JSHandle receiver, JSHandle key); }; class StoreICRuntime : public ICRuntime { diff --git a/ecmascript/ic/ic_runtime_stub-inl.h b/ecmascript/ic/ic_runtime_stub-inl.h index 482c807fd77b37731819655fd9478aac3f6e1bd0..2356b87ff5dd51101de005d3af9d3c862cc4d405 100644 --- a/ecmascript/ic/ic_runtime_stub-inl.h +++ b/ecmascript/ic/ic_runtime_stub-inl.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_IC_IC_RUNTIME_STUB_INL_H_ -#define ECMASCRIPT_IC_IC_RUNTIME_STUB_INL_H_ +#ifndef ECMASCRIPT_IC_IC_RUNTIME_STUB_INL_H +#define ECMASCRIPT_IC_IC_RUNTIME_STUB_INL_H #include "ecmascript/base/config.h" #include "ecmascript/global_env.h" @@ -113,8 +113,16 @@ ARK_INLINE JSTaggedValue ICRuntimeStub::TryLoadICByValue(JSThread *thread, JSTag if (receiver.IsHeapObject()) { auto hclass = receiver.GetTaggedObject()->GetClass(); if (firstValue.GetWeakReferentUnChecked() == hclass) { - ASSERT(HandlerBase::IsElement(secondValue.GetInt())); - return LoadElement(JSObject::Cast(receiver.GetTaggedObject()), key); + if (HandlerBase::IsElement(secondValue.GetInt())) { + return LoadElement(JSObject::Cast(receiver.GetTaggedObject()), key); + } + ASSERT(HandlerBase::IsStringElement(secondValue.GetInt())); + return LoadStringElement(thread, receiver, key); + } + // check ploy + if (secondValue.IsHole() && !firstValue.IsHole()) { + JSTaggedValue cachedHandler = CheckPolyHClass(firstValue, hclass); + return LoadICWithElementHandler(thread, receiver, cachedHandler, key); } // Check key if (firstValue == key) { @@ -131,7 +139,7 @@ ARK_NOINLINE JSTaggedValue ICRuntimeStub::LoadICByValue(JSThread *thread, Profil JSTaggedValue receiver, JSTaggedValue key, uint32_t slotId) { INTERPRETER_TRACE(thread, LoadICByValue); - return LoadMiss(thread, profileTypeInfo, receiver, key, slotId, ICKind::LoadIC); + return LoadValueMiss(thread, profileTypeInfo, receiver, key, slotId, ICKind::LoadIC); } ARK_INLINE JSTaggedValue ICRuntimeStub::TryStoreICByValue(JSThread *thread, JSTaggedValue receiver, @@ -283,7 +291,7 @@ void ICRuntimeStub::StoreWithTransition(JSThread *thread, JSObject *receiver, JS handlerInfo = static_cast(transitionHandler->GetHandlerInfo().GetInt()); } - receiver->SetClass(newHClass); + receiver->SynchronizedSetClass(newHClass); ASSERT(HandlerBase::IsField(handlerInfo)); if (!HandlerBase::IsInlinedProps(handlerInfo)) { @@ -301,8 +309,10 @@ void ICRuntimeStub::StoreWithTransition(JSThread *thread, JSObject *receiver, JS properties = factory->NewTaggedArray(capacity); } else { auto arrayHandle = JSHandle(thread, array); - properties = factory->CopyArray(arrayHandle, capacity, - JSObject::ComputePropertyCapacity(capacity)); + uint32_t maxNonInlinedFastPropsCapacity = objHandle->GetNonInlinedFastPropsCapacity(); + uint32_t newLen = JSObject::ComputeNonInlinedFastPropsCapacity(capacity, + maxNonInlinedFastPropsCapacity); + properties = factory->CopyArray(arrayHandle, capacity, newLen); } properties->Set(thread, index, valueHandle); objHandle->SetProperties(thread, properties); @@ -403,9 +413,17 @@ ARK_INLINE JSTaggedValue ICRuntimeStub::LoadICWithHandler(JSThread *thread, JSTa if (LIKELY(HandlerBase::IsField(handlerInfo))) { return LoadFromField(JSObject::Cast(holder.GetTaggedObject()), handlerInfo); } + if (LIKELY(HandlerBase::IsString(handlerInfo))) { + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + holder = env->GetStringFunction().GetObject()->GetFunctionPrototype(); + return LoadFromField(JSObject::Cast(holder.GetTaggedObject()), handlerInfo); + } if (HandlerBase::IsNonExist(handlerInfo)) { return JSTaggedValue::Undefined(); } + if (HandlerBase::IsStringLength(handlerInfo)) { + return JSTaggedNumber((EcmaStringAccessor(EcmaString::Cast(holder)).GetLength())); + } ASSERT(HandlerBase::IsAccessor(handlerInfo) || HandlerBase::IsInternalAccessor(handlerInfo)); auto accessor = LoadFromField(JSObject::Cast(holder.GetTaggedObject()), handlerInfo); return FastRuntimeStub::CallGetter(thread, receiver, holder, accessor); @@ -418,6 +436,20 @@ ARK_INLINE JSTaggedValue ICRuntimeStub::LoadICWithHandler(JSThread *thread, JSTa return LoadGlobal(handler); } +ARK_INLINE JSTaggedValue ICRuntimeStub::LoadICWithElementHandler(JSThread *thread, JSTaggedValue receiver, + JSTaggedValue handler, JSTaggedValue key) +{ + if (LIKELY(handler.IsInt())) { + auto handlerInfo = static_cast(handler.GetInt()); + if (HandlerBase::IsElement(handlerInfo)) { + return LoadElement(JSObject::Cast(receiver.GetTaggedObject()), key); + } + ASSERT(HandlerBase::IsStringElement(handlerInfo)); + return LoadStringElement(thread, receiver, key); + } + return JSTaggedValue::Hole(); +} + ARK_INLINE JSTaggedValue ICRuntimeStub::LoadElement(JSObject *receiver, JSTaggedValue key) { auto index = TryToElementsIndex(key); @@ -435,6 +467,27 @@ ARK_INLINE JSTaggedValue ICRuntimeStub::LoadElement(JSObject *receiver, JSTagged return value; } +ARK_INLINE JSTaggedValue ICRuntimeStub::LoadStringElement(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key) +{ + auto index = TryToElementsIndex(key); + if (index < 0) { + return JSTaggedValue::Hole(); + } + uint32_t elementIndex = static_cast(index); + uint16_t tmpChar = 0; + { + JSHandle strHandle(thread, receiver); + JSHandle strFlat(thread, EcmaStringAccessor::Flatten(thread->GetEcmaVM(), strHandle)); + if (EcmaStringAccessor(strFlat).GetLength() <= elementIndex) { + return JSTaggedValue::Hole(); + } + tmpChar = EcmaStringAccessor(strFlat).Get(elementIndex); + } + auto factory = thread->GetEcmaVM()->GetFactory(); + JSHandle value(factory->NewFromUtf16(&tmpChar, 1)); + return value.GetTaggedValue(); +} + JSTaggedValue ICRuntimeStub::StoreElement(JSThread *thread, JSObject *receiver, JSTaggedValue key, JSTaggedValue value, JSTaggedValue handler) { @@ -488,7 +541,7 @@ JSTaggedValue ICRuntimeStub::StoreElement(JSThread *thread, JSObject *receiver, return JSTaggedValue::Undefined(); } -ARK_INLINE int32_t ICRuntimeStub::TryToElementsIndex(JSTaggedValue key) +ARK_INLINE int64_t ICRuntimeStub::TryToElementsIndex(JSTaggedValue key) { if (LIKELY(key.IsInt())) { return key.GetInt(); @@ -497,7 +550,7 @@ ARK_INLINE int32_t ICRuntimeStub::TryToElementsIndex(JSTaggedValue key) if (key.IsString()) { uint32_t index = 0; if (JSTaggedValue::StringToElementIndex(key, &index)) { - return static_cast(index); + return static_cast(index); } } @@ -523,6 +576,17 @@ JSTaggedValue ICRuntimeStub::LoadMiss(JSThread *thread, ProfileTypeInfo *profile return icRuntime.LoadMiss(receiverHandle, keyHandle); } +JSTaggedValue ICRuntimeStub::LoadValueMiss(JSThread *thread, ProfileTypeInfo *profileTypeInfo, JSTaggedValue receiver, + JSTaggedValue key, uint32_t slotId, ICKind kind) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + auto keyHandle = JSHandle(thread, key); + auto receiverHandle = JSHandle(thread, receiver); + auto profileInfoHandle = JSHandle(thread, profileTypeInfo); + LoadICRuntime icRuntime(thread, JSHandle::Cast(profileInfoHandle), slotId, kind); + return icRuntime.LoadValueMiss(receiverHandle, keyHandle); +} + JSTaggedValue ICRuntimeStub::StoreMiss(JSThread *thread, ProfileTypeInfo *profileTypeInfo, JSTaggedValue receiver, JSTaggedValue key, JSTaggedValue value, uint32_t slotId, ICKind kind) { @@ -536,4 +600,4 @@ JSTaggedValue ICRuntimeStub::StoreMiss(JSThread *thread, ProfileTypeInfo *profil } } // namespace panda::ecmascript -#endif // ECMASCRIPT_IC_IC_RUNTIME_STUB_INL_H_ +#endif // ECMASCRIPT_IC_IC_RUNTIME_STUB_INL_H diff --git a/ecmascript/ic/ic_runtime_stub.h b/ecmascript/ic/ic_runtime_stub.h index 455146ae9f37f5b38c24e6e55f6e3b7de17c8aec..dc275e505f44b028a0f4d9287b4a681d29eb94b8 100644 --- a/ecmascript/ic/ic_runtime_stub.h +++ b/ecmascript/ic/ic_runtime_stub.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_IC_IC_RUNTIME_STUB_H_ -#define ECMASCRIPT_IC_IC_RUNTIME_STUB_H_ +#ifndef ECMASCRIPT_IC_IC_RUNTIME_STUB_H +#define ECMASCRIPT_IC_IC_RUNTIME_STUB_H #include "ecmascript/ic/profile_type_info.h" #include "ecmascript/js_tagged_value.h" @@ -42,6 +42,8 @@ public: static inline JSTaggedValue CheckPolyHClass(JSTaggedValue cachedValue, JSHClass* hclass); static inline JSTaggedValue LoadICWithHandler(JSThread *thread, JSTaggedValue receiver, JSTaggedValue holder, JSTaggedValue handler); + static inline JSTaggedValue LoadICWithElementHandler(JSThread *thread, JSTaggedValue receiver, + JSTaggedValue handler, JSTaggedValue key); static inline JSTaggedValue StoreICWithHandler(JSThread *thread, JSTaggedValue receiver, JSTaggedValue holder, JSTaggedValue value, JSTaggedValue handler); static inline void StoreWithTransition(JSThread *thread, JSObject *receiver, JSTaggedValue value, @@ -69,14 +71,17 @@ public: JSTaggedValue receiver, JSTaggedValue key, JSTaggedValue value, uint32_t slotId); static inline JSTaggedValue LoadElement(JSObject *receiver, JSTaggedValue key); + static inline JSTaggedValue LoadStringElement(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key); static inline JSTaggedValue StoreElement(JSThread *thread, JSObject *receiver, JSTaggedValue key, JSTaggedValue value, JSTaggedValue handlerInfo); - static inline int32_t TryToElementsIndex(JSTaggedValue key); + static inline int64_t TryToElementsIndex(JSTaggedValue key); static inline JSTaggedValue LoadMiss(JSThread *thread, ProfileTypeInfo *profileTypeInfo, JSTaggedValue receiver, JSTaggedValue key, uint32_t slotId, ICKind kind); + static inline JSTaggedValue LoadValueMiss(JSThread *thread, ProfileTypeInfo *profileTypeInfo, + JSTaggedValue receiver, JSTaggedValue key, uint32_t slotId, ICKind kind); static inline JSTaggedValue StoreMiss(JSThread *thread, ProfileTypeInfo *profileTypeInfo, JSTaggedValue receiver, JSTaggedValue key, JSTaggedValue value, uint32_t slotId, ICKind kind); }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_IC_IC_RUNTIME_STUB_H_ +#endif // ECMASCRIPT_IC_IC_RUNTIME_STUB_H diff --git a/ecmascript/ic/invoke_cache.h b/ecmascript/ic/invoke_cache.h index 82d308a641011f992adadf67f700ab6d22cb0e7c..e533122ca597455c3d2395f0aeae4c8e6f414f10 100644 --- a/ecmascript/ic/invoke_cache.h +++ b/ecmascript/ic/invoke_cache.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_IC_INVOKE_CACHE_H_ -#define ECMASCRIPT_IC_INVOKE_CACHE_H_ +#ifndef ECMASCRIPT_IC_INVOKE_CACHE_H +#define ECMASCRIPT_IC_INVOKE_CACHE_H #include "ecmascript/ic/profile_type_info.h" #include "ecmascript/js_tagged_value.h" @@ -48,4 +48,4 @@ public: }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_IC_INVOKE_CACHE_H_ +#endif // ECMASCRIPT_IC_INVOKE_CACHE_H diff --git a/ecmascript/ic/profile_type_info.h b/ecmascript/ic/profile_type_info.h index eb1143e9cb827952a9e3c4e9ab53df0d695854c4..973c938012b705bdd6072a4ff2ef712885bcd179 100644 --- a/ecmascript/ic/profile_type_info.h +++ b/ecmascript/ic/profile_type_info.h @@ -83,19 +83,82 @@ static inline bool IsGlobalIC(ICKind kind) std::string ICKindToString(ICKind kind); +/* ProfileTypeInfo + * +--------------------------------+---- + * | cache | + * | ..... | + * +--------------------------------+---- + * | low 32bits(PeriodCount) | + * | hight 32bits(Reserved) | + * +--------------------------------+ + */ class ProfileTypeInfo : public TaggedArray { public: static const uint32_t MAX_FUNC_CACHE_INDEX = std::numeric_limits::max(); static constexpr uint32_t INVALID_SLOT_INDEX = 0xFF; static constexpr uint32_t MAX_SLOT_INDEX = 0xFFFF; + static constexpr size_t BIT_FIELD_INDEX = 1; + static constexpr size_t RESERVED_LENGTH = BIT_FIELD_INDEX; + static constexpr size_t CHANGED_PEROID_COUNT = 0; static ProfileTypeInfo *Cast(TaggedObject *object) { ASSERT(JSTaggedValue(object).IsTaggedArray()); return static_cast(object); } -}; + static size_t ComputeSize(uint32_t cacheSize) + { + return TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), cacheSize + RESERVED_LENGTH); + } + + inline uint32_t GetCacheLength() const + { + return GetLength() - RESERVED_LENGTH; + } + + inline void InitializeWithSpecialValue(JSTaggedValue initValue, uint32_t capacity, uint32_t extraLength = 0) + { + ASSERT(initValue.IsSpecial()); + SetLength(capacity + RESERVED_LENGTH); + SetExtraLength(extraLength); + for (uint32_t i = 0; i < capacity; i++) { + size_t offset = JSTaggedValue::TaggedTypeSize() * i; + Barriers::SetPrimitive(GetData(), offset, initValue.GetRawData()); + } + ResetPeriodCount(); + } + + void ResetPeriodCount() + { + SetPeriodCount(0); + } + + bool IsProfileTypeInfoChanged() const + { + return GetPeroidCount() == CHANGED_PEROID_COUNT; + } + + DECL_VISIT_ARRAY(DATA_OFFSET, GetCacheLength()); + + DECL_DUMP() + +private: + uint32_t GetPeroidCount() const + { + return Barriers::GetValue(GetData(), GetBitfieldOffset()); + } + + void SetPeriodCount(uint32_t count) + { + Barriers::SetPrimitive(GetData(), GetBitfieldOffset(), count); + } + + inline size_t GetBitfieldOffset() const + { + return JSTaggedValue::TaggedTypeSize() * (GetLength() - BIT_FIELD_INDEX); + } +}; class ProfileTypeAccessor { public: diff --git a/ecmascript/ic/tests/ic_handler_test.cpp b/ecmascript/ic/tests/ic_handler_test.cpp index 2f5fd9569e5bef9c3d4e3f6c59a8392ef36336df..f45e987467ae75aeba4318cd8375912288ce7c3b 100644 --- a/ecmascript/ic/tests/ic_handler_test.cpp +++ b/ecmascript/ic/tests/ic_handler_test.cpp @@ -61,7 +61,10 @@ public: */ HWTEST_F_L0(ICHandlerTest, LoadElement) { - JSTaggedValue result = LoadHandler::LoadElement(thread).GetTaggedValue(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle handleKey(factory->NewFromASCII("key")); + ObjectOperator handleOp(thread, handleKey); + JSTaggedValue result = LoadHandler::LoadElement(thread, handleOp).GetTaggedValue(); EXPECT_TRUE(HandlerBase::IsElement(result.GetInt())); EXPECT_EQ(HandlerBase::GetKind(result.GetInt()), HandlerKind::ELEMENT); } diff --git a/ecmascript/ic/tests/ic_runtime_test.cpp b/ecmascript/ic/tests/ic_runtime_test.cpp index 63fa46db8f2879766b5ecfbe4dc511fd5d943b8b..5060ccaa41f84ea8d611e0dda6e924c590c194fc 100644 --- a/ecmascript/ic/tests/ic_runtime_test.cpp +++ b/ecmascript/ic/tests/ic_runtime_test.cpp @@ -14,6 +14,7 @@ */ #include "ecmascript/ic/ic_runtime.h" +#include "ecmascript/ic/profile_type_info.h" #include "ecmascript/interpreter/slow_runtime_stub.h" #include "ecmascript/global_env.h" #include "ecmascript/object_operator.h" @@ -64,7 +65,7 @@ HWTEST_F_L0(ICRunTimeTest, UpdateLoadHandler) JSHandle handleStoreArray(factory->NewTaggedArray(2)); JSHandle undefinedVal; - JSHandle handleTaggedArray = factory->NewTaggedArray(arrayLength); + JSHandle handleProfileTypeInfo = factory->NewProfileTypeInfo(arrayLength); for (uint32_t i = 0; i < arrayLength; i++) { undefinedVal = globalConst->GetHandledUndefinedString(); if (i == static_cast(ICKind::NamedLoadIC) || i == static_cast(ICKind::LoadIC)) { @@ -74,9 +75,8 @@ HWTEST_F_L0(ICRunTimeTest, UpdateLoadHandler) i == static_cast(ICKind::LoadIC) + 1) { undefinedVal = JSHandle(thread, JSTaggedValue::Hole()); } - handleTaggedArray->Set(thread, i, undefinedVal.GetTaggedValue()); + handleProfileTypeInfo->Set(thread, i, undefinedVal.GetTaggedValue()); } - JSHandle handleProfileTypeInfo = JSHandle::Cast(handleTaggedArray); // test op is Element ObjectOperator handleOp1(thread, handleKeyWithElement); uint32_t slotId = 2; @@ -107,7 +107,7 @@ HWTEST_F_L0(ICRunTimeTest, UpdateStoreHandler) JSHandle handleStoreArray(factory->NewTaggedArray(2)); JSHandle undefinedVal; - JSHandle handleTaggedArray = factory->NewTaggedArray(arrayLength); + JSHandle handleProfileTypeInfo = factory->NewProfileTypeInfo(arrayLength); for (uint32_t i = 0; i < arrayLength; i++) { undefinedVal = globalConst->GetHandledUndefinedString(); if (i == static_cast(ICKind::NamedStoreIC) || i == static_cast(ICKind::StoreIC)) { @@ -117,9 +117,8 @@ HWTEST_F_L0(ICRunTimeTest, UpdateStoreHandler) i == static_cast(ICKind::StoreIC) + 1) { undefinedVal = handleStoreArray; } - handleTaggedArray->Set(thread, i, undefinedVal.GetTaggedValue()); + handleProfileTypeInfo->Set(thread, i, undefinedVal.GetTaggedValue()); } - JSHandle handleProfileTypeInfo = JSHandle::Cast(handleTaggedArray); // test op is Element uint32_t slotId = 3; ObjectOperator handleOp1(thread, handleKeyWithElement); @@ -153,8 +152,7 @@ HWTEST_F_L0(ICRunTimeTest, TraceIC) JSHandle handleKeyWithString(factory->NewFromASCII("key")); JSHandle handleKeyWithElement(thread, JSTaggedValue(2)); - JSHandle handleTaggedArray = factory->NewTaggedArray(arrayLength); - JSHandle handleProfileTypeInfo = JSHandle::Cast(handleTaggedArray); + JSHandle handleProfileTypeInfo = factory->NewProfileTypeInfo(arrayLength); ICRuntime icRuntime(thread, handleProfileTypeInfo, 4, ICKind::NamedGlobalLoadIC); // 4: means the NamedGlobalLoadIC icRuntime.TraceIC(handleReceiver, handleKeyWithString); @@ -173,8 +171,9 @@ HWTEST_F_L0(ICRunTimeTest, StoreMiss) JSHandle handleKeyWithString(factory->NewFromASCII("key")); JSHandle handleValueWithElement(thread, JSTaggedValue(2)); - JSHandle handleTaggedArray = factory->NewTaggedArray(arrayLength); - JSHandle handleProfileTypeInfo = JSHandle::Cast(handleTaggedArray); + JSHandle handleProfileTypeInfo = factory->NewProfileTypeInfo(arrayLength); + handleProfileTypeInfo->Set(thread, 0, JSTaggedValue::Hole()); + handleProfileTypeInfo->Set(thread, 1, JSTaggedValue::Hole()); StoreICRuntime storeICRuntime(thread, handleProfileTypeInfo, 0, ICKind::NamedGlobalStoreIC); storeICRuntime.StoreMiss(handleReceiver, handleKeyWithString, handleValueWithElement); EXPECT_EQ(JSObject::GetProperty(thread, handleReceiver, handleKeyWithString).GetValue(), handleValueWithElement); @@ -183,7 +182,7 @@ HWTEST_F_L0(ICRunTimeTest, StoreMiss) SlowRuntimeStub::StGlobalRecord(thread, handleKeyWithString.GetTaggedValue(), handleKeyWithString.GetTaggedValue(), false); - handleTaggedArray->Set(thread, 0, JSTaggedValue::Undefined()); + handleProfileTypeInfo->Set(thread, 0, JSTaggedValue::Undefined()); storeICRuntime.StoreMiss(handleReceiver1, handleKeyWithString, handleValueWithElement); EXPECT_TRUE(handleProfileTypeInfo->Get(0).IsPropertyBox()); } @@ -201,8 +200,9 @@ HWTEST_F_L0(ICRunTimeTest, LoadMiss) JSHandle handleValueWithElement(thread, JSTaggedValue(2)); JSObject::SetProperty(thread, handleReceiver, handleKeyWithString, handleValueWithElement); - JSHandle handleTaggedArray = factory->NewTaggedArray(arrayLength); - JSHandle handleProfileTypeInfo = JSHandle::Cast(handleTaggedArray); + JSHandle handleProfileTypeInfo = factory->NewProfileTypeInfo(arrayLength); + handleProfileTypeInfo->Set(thread, 0, JSTaggedValue::Hole()); + handleProfileTypeInfo->Set(thread, 1, JSTaggedValue::Hole()); LoadICRuntime loadICRuntime(thread, handleProfileTypeInfo, 0, ICKind::NamedGlobalStoreIC); EXPECT_EQ(loadICRuntime.LoadMiss(handleReceiver, handleKeyWithString), handleValueWithElement.GetTaggedValue()); EXPECT_TRUE(handleProfileTypeInfo->Get(0).IsHole()); diff --git a/ecmascript/ic/tests/profile_type_info_test.cpp b/ecmascript/ic/tests/profile_type_info_test.cpp index 76781d6fadb77ab3f80d4d1e5e4c5dcf2dcb61d2..58cc789885e7375c9ddec68a8e6af262cd5bd027 100644 --- a/ecmascript/ic/tests/profile_type_info_test.cpp +++ b/ecmascript/ic/tests/profile_type_info_test.cpp @@ -78,15 +78,13 @@ HWTEST_F_L0(ProfileTypeInfoTest, GetICState) JSHandle newBox(factory->NewPropertyBox(newValue)); uint32_t arrayLength = 6; - JSHandle handleDetailsArray = factory->NewTaggedArray(arrayLength); - handleDetailsArray->Set(thread, 0, JSTaggedValue::Undefined()); - handleDetailsArray->Set(thread, 1, JSTaggedValue::Hole()); - handleDetailsArray->Set(thread, 2, newArray.GetTaggedValue()); - handleDetailsArray->Set(thread, 3, newArray.GetTaggedValue()); - handleDetailsArray->Set(thread, 4, newBox.GetTaggedValue()); - handleDetailsArray->Set(thread, 5, newArray.GetTaggedValue()); - - JSHandle handleProfileTypeInfo = JSHandle::Cast(handleDetailsArray); + JSHandle handleProfileTypeInfo = factory->NewProfileTypeInfo(arrayLength); + handleProfileTypeInfo->Set(thread, 1, JSTaggedValue::Hole()); + handleProfileTypeInfo->Set(thread, 2, newArray.GetTaggedValue()); + handleProfileTypeInfo->Set(thread, 3, newArray.GetTaggedValue()); + handleProfileTypeInfo->Set(thread, 4, newBox.GetTaggedValue()); + handleProfileTypeInfo->Set(thread, 5, newArray.GetTaggedValue()); + EXPECT_TRUE(*handleProfileTypeInfo != nullptr); // slotId = 0 ProfileTypeAccessor handleProfileTypeAccessor0(thread, handleProfileTypeInfo, 0, ICKind::StoreIC); @@ -133,12 +131,8 @@ HWTEST_F_L0(ProfileTypeInfoTest, AddHandlerWithoutKey) handleTaggedArray->Set(thread, 0, JSTaggedValue(1)); handleTaggedArray->Set(thread, 1, JSTaggedValue(2)); - JSHandle handleDetailsArray = factory->NewTaggedArray(4); - handleDetailsArray->Set(thread, 0, JSTaggedValue::Undefined()); - handleDetailsArray->Set(thread, 1, JSTaggedValue::Undefined()); - handleDetailsArray->Set(thread, 2, JSTaggedValue::Hole()); - handleDetailsArray->Set(thread, 3, JSTaggedValue::Undefined()); - JSHandle handleProfileTypeInfo = JSHandle::Cast(handleDetailsArray); + JSHandle handleProfileTypeInfo = factory->NewProfileTypeInfo(4); + handleProfileTypeInfo->Set(thread, 2, JSTaggedValue::Hole()); uint32_t slotId = 0; // test profileData is Undefined ProfileTypeAccessor handleProfileTypeAccessor0(thread, handleProfileTypeInfo, slotId, ICKind::StoreIC); @@ -188,10 +182,7 @@ HWTEST_F_L0(ProfileTypeInfoTest, AddElementHandler) JSHandle objClassVal(thread, handleObj->GetJSHClass()); JSHandle HandlerValue(thread, JSTaggedValue(3)); - JSHandle handleDetailsArray = factory->NewTaggedArray(4); - handleDetailsArray->Set(thread, 0, JSTaggedValue::Undefined()); - handleDetailsArray->Set(thread, 1, JSTaggedValue::Undefined()); - JSHandle handleProfileTypeInfo = JSHandle::Cast(handleDetailsArray); + JSHandle handleProfileTypeInfo = factory->NewProfileTypeInfo(4); uint32_t slotId = 0; ProfileTypeAccessor handleProfileTypeAccessor0(thread, handleProfileTypeInfo, slotId, ICKind::StoreIC); @@ -227,12 +218,9 @@ HWTEST_F_L0(ProfileTypeInfoTest, AddHandlerWithKey) handleTaggedArray->Set(thread, 0, JSTaggedValue(1)); handleTaggedArray->Set(thread, 1, JSTaggedValue(2)); - JSHandle handleDetailsArray = factory->NewTaggedArray(4); - handleDetailsArray->Set(thread, 0, JSTaggedValue::Undefined()); - handleDetailsArray->Set(thread, 1, JSTaggedValue::Undefined()); - handleDetailsArray->Set(thread, 2, handleTaggedArray.GetTaggedValue()); - handleDetailsArray->Set(thread, 3, handleTaggedArray.GetTaggedValue()); - JSHandle handleProfileTypeInfo = JSHandle::Cast(handleDetailsArray); + JSHandle handleProfileTypeInfo = factory->NewProfileTypeInfo(4); + handleProfileTypeInfo->Set(thread, 2, handleTaggedArray.GetTaggedValue()); + handleProfileTypeInfo->Set(thread, 3, handleTaggedArray.GetTaggedValue()); uint32_t slotId = 0; // test profileData is Undefined ProfileTypeAccessor handleProfileTypeAccessor0(thread, handleProfileTypeInfo, slotId, ICKind::StoreIC); @@ -273,10 +261,8 @@ HWTEST_F_L0(ProfileTypeInfoTest, AddGlobalHandlerKey) handleTaggedArray->Set(thread, 1, JSTaggedValue(333)); JSHandle arrayKey(factory->NewFromASCII("array")); - JSHandle handleDetailsArray = factory->NewTaggedArray(2); - handleDetailsArray->Set(thread, 0, JSTaggedValue::Undefined()); - handleDetailsArray->Set(thread, 1, handleTaggedArray.GetTaggedValue()); - JSHandle handleProfileTypeInfo = JSHandle::Cast(handleDetailsArray); + JSHandle handleProfileTypeInfo = factory->NewProfileTypeInfo(2); + handleProfileTypeInfo->Set(thread, 1, handleTaggedArray.GetTaggedValue()); uint32_t slotId = 0; ProfileTypeAccessor handleProfileTypeAccessor0(thread, handleProfileTypeInfo, slotId, ICKind::StoreIC); @@ -313,17 +299,16 @@ HWTEST_F_L0(ProfileTypeInfoTest, AddGlobalRecordHandler) JSHandle HandlerValue1(thread, JSTaggedValue(232)); JSHandle HandlerValue2(thread, JSTaggedValue(5)); - JSHandle handleDetailsArray = factory->NewTaggedArray(2); - JSHandle handleProfileTypeInfo = JSHandle::Cast(handleDetailsArray); + JSHandle handleProfileTypeInfo = factory->NewProfileTypeInfo(2); uint32_t slotId = 0; ProfileTypeAccessor handleProfileTypeAccessor(thread, handleProfileTypeInfo, slotId, ICKind::StoreIC); handleProfileTypeAccessor.AddGlobalRecordHandler(HandlerValue1); EXPECT_EQ(handleProfileTypeInfo->Get(slotId).GetInt(), 232); - EXPECT_TRUE(handleProfileTypeInfo->Get(slotId + 1).IsHole()); + EXPECT_TRUE(handleProfileTypeInfo->Get(slotId + 1).IsUndefined()); handleProfileTypeAccessor.AddGlobalRecordHandler(HandlerValue2); EXPECT_EQ(handleProfileTypeInfo->Get(slotId).GetInt(), 5); - EXPECT_TRUE(handleProfileTypeInfo->Get(slotId + 1).IsHole()); + EXPECT_TRUE(handleProfileTypeInfo->Get(slotId + 1).IsUndefined()); } /** @@ -337,11 +322,10 @@ HWTEST_F_L0(ProfileTypeInfoTest, SetAsMega) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); uint32_t arrayLength = 2; - JSHandle handleDetailsArray = factory->NewTaggedArray(arrayLength); - handleDetailsArray->Set(thread, 0, JSTaggedValue(111)); - handleDetailsArray->Set(thread, 1, JSTaggedValue(222)); + JSHandle handleProfileTypeInfo = factory->NewProfileTypeInfo(arrayLength); + handleProfileTypeInfo->Set(thread, 0, JSTaggedValue(111)); + handleProfileTypeInfo->Set(thread, 1, JSTaggedValue(222)); - JSHandle handleProfileTypeInfo = JSHandle::Cast(handleDetailsArray); EXPECT_TRUE(*handleProfileTypeInfo != nullptr); uint32_t slotId = 0; ProfileTypeAccessor handleProfileTypeAccessor(thread, handleProfileTypeInfo, slotId, ICKind::StoreIC); @@ -368,8 +352,7 @@ HWTEST_F_L0(ProfileTypeInfoTest, GetWeakRef) JSTaggedValue weakRefValue(handleObj.GetTaggedValue()); uint32_t arrayLength = 2; - JSHandle handleDetailsArray = factory->NewTaggedArray(arrayLength); - JSHandle handleProfileTypeInfo = JSHandle::Cast(handleDetailsArray); + JSHandle handleProfileTypeInfo = factory->NewProfileTypeInfo(arrayLength); EXPECT_TRUE(*handleProfileTypeInfo != nullptr); uint32_t slotId = 0; @@ -397,8 +380,7 @@ HWTEST_F_L0(ProfileTypeInfoTest, GetRefFromWeak) handleProfileType.CreateWeakRef(); uint32_t arrayLength = 2; - JSHandle handleDetailsArray = factory->NewTaggedArray(arrayLength); - JSHandle handleProfileTypeInfo = JSHandle::Cast(handleDetailsArray); + JSHandle handleProfileTypeInfo = factory->NewProfileTypeInfo(arrayLength); EXPECT_TRUE(*handleProfileTypeInfo != nullptr); uint32_t slotId = 0; diff --git a/ecmascript/interpreter/fast_runtime_stub-inl.h b/ecmascript/interpreter/fast_runtime_stub-inl.h index 9deddc53eea9739dd3de0495e7089d041ffd48c8..0d18dc76a0f9e024c1d9a2bfac623ce341aa26f4 100644 --- a/ecmascript/interpreter/fast_runtime_stub-inl.h +++ b/ecmascript/interpreter/fast_runtime_stub-inl.h @@ -18,6 +18,7 @@ #include "ecmascript/interpreter/fast_runtime_stub.h" +#include "ecmascript/ecma_string-inl.h" #include "ecmascript/global_dictionary-inl.h" #include "ecmascript/global_env.h" #include "ecmascript/interpreter/interpreter.h" diff --git a/ecmascript/interpreter/interpreter-inl.h b/ecmascript/interpreter/interpreter-inl.h index 4ced9b7119d331186d9dc9de2209168b5c9be08e..1d2c3fb5494434858ea823d1d8eca3ce3c4c28f2 100644 --- a/ecmascript/interpreter/interpreter-inl.h +++ b/ecmascript/interpreter/interpreter-inl.h @@ -140,7 +140,7 @@ using CommonStubCSigns = kungfu::CommonStubCSigns; #define SET_ACC(val) (acc = val) // NOLINT(cppcoreguidelines-macro-usage) #define GET_METHOD_FROM_CACHE(index) \ - ConstantPool::GetMethodFromCache(thread, constpool, index) + ConstantPool::GetMethodFromCache(thread, constpool, module, index) #define GET_STR_FROM_CACHE(index) \ ConstantPool::GetStringFromCache(thread, constpool, index) @@ -198,12 +198,42 @@ using CommonStubCSigns = kungfu::CommonStubCSigns; } \ } while (false) +#define JUMP_IF_ENTRYFRAME_PENDING() \ + do { \ + if (thread->IsEntryFrameDroppedPending()) { \ + thread->ResetEntryFrameDroppedState(); \ + DROPFRAME_JUMP(); \ + } \ + } while (false) + +#define DROPFRAME_JUMP() \ + do { \ + thread->ResetFrameDroppedState(); \ + sp = const_cast(thread->GetCurrentSPFrame()); \ + InterpretedFrame *state = GET_FRAME(sp); \ + pc = state->pc; \ + RESTORE_ACC(); \ + DISPATCH_OFFSET(0); \ + } while (false) + +#define RESET_AND_JUMP_IF_DROPFRAME() \ + do { \ + if (thread->IsFrameDropped()) { \ + if (thread->IsEntryFrameDroppedTrue()) { \ + return; \ + } \ + DROPFRAME_JUMP(); \ + } \ + } while (false) + // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define NOTIFY_DEBUGGER_EVENT() \ do { \ + JUMP_IF_ENTRYFRAME_PENDING(); \ SAVE_ACC(); \ SAVE_PC(); \ NotifyBytecodePcChanged(thread); \ + RESET_AND_JUMP_IF_DROPFRAME(); \ RESTORE_ACC(); \ } while (false) @@ -712,14 +742,22 @@ JSTaggedValue EcmaInterpreter::Execute(EcmaRuntimeCallInfo *info) thread->CheckSafepoint(); LOG_INST() << "Entry: Runtime Call " << std::hex << reinterpret_cast(newSp) << " " << std::hex << reinterpret_cast(pc); - + MethodEntry(thread); EcmaInterpreter::RunInternal(thread, pc, newSp); // NOLINTNEXTLINE(readability-identifier-naming) const JSTaggedValue resAcc = state->acc; - // pop frame + InterpretedEntryFrame *entryState = GET_ENTRY_FRAME(sp); JSTaggedType *prevSp = entryState->base.prev; + + if (thread->IsEntryFrameDroppedTrue()) { + thread->PendingEntryFrameDroppedState(); + InterpretedFrame *prevState = GET_FRAME(prevSp); + return prevState->acc; + } + + // pop frame thread->SetCurrentSPFrame(prevSp); return resAcc; #else @@ -731,8 +769,7 @@ JSTaggedValue EcmaInterpreter::GeneratorReEnterInterpreter(JSThread *thread, JSH { [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle func = JSHandle::Cast(JSHandle(thread, context->GetMethod())); - Method *method = func->GetCallTarget(); - if (method->IsAotWithCallField()) { + if (func->GetClass()->IsOptimized()) { return GeneratorReEnterAot(thread, context); } @@ -769,6 +806,7 @@ JSTaggedValue EcmaInterpreter::GeneratorReEnterInterpreter(JSThread *thread, JSH newSp[i] = regsArray->Get(i).GetRawData(); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) } uint32_t pcOffset = context->GetBCOffset(); + Method *method = func->GetCallTarget(); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) const uint8_t *resumePc = method->GetBytecodeArray() + pcOffset; @@ -786,6 +824,7 @@ JSTaggedValue EcmaInterpreter::GeneratorReEnterInterpreter(JSThread *thread, JSH // execute interpreter thread->SetCurrentSPFrame(newSp); + MethodEntry(thread); EcmaInterpreter::RunInternal(thread, resumePc, newSp); JSTaggedValue res = state->acc; @@ -865,6 +904,41 @@ void EcmaInterpreter::NotifyDebuggerStmt(JSThread *thread) } } +void EcmaInterpreter::MethodEntry(JSThread *thread) +{ + FrameHandler frameHandler(thread); + for (; frameHandler.HasFrame(); frameHandler.PrevJSFrame()) { + if (frameHandler.IsEntryFrame()) { + continue; + } + Method *method = frameHandler.GetMethod(); + if (method->IsNativeWithCallField()) { + continue; + } + JSTaggedValue env = frameHandler.GetEnv(); + auto *debuggerMgr = thread->GetEcmaVM()->GetJsDebuggerManager(); + debuggerMgr->GetNotificationManager()->MethodEntryEvent(thread, method, env); + return; + } +} + +void EcmaInterpreter::MethodExit(JSThread *thread) +{ + FrameHandler frameHandler(thread); + for (; frameHandler.HasFrame(); frameHandler.PrevJSFrame()) { + if (frameHandler.IsEntryFrame()) { + continue; + } + Method *method = frameHandler.GetMethod(); + if (method->IsNativeWithCallField()) { + continue; + } + auto *debuggerMgr = thread->GetEcmaVM()->GetJsDebuggerManager(); + debuggerMgr->GetNotificationManager()->MethodExitEvent(thread, method); + return; + } +} + const JSPandaFile *EcmaInterpreter::GetNativeCallPandafile(JSThread *thread) { FrameHandler frameHandler(thread); @@ -898,8 +972,7 @@ JSTaggedValue EcmaInterpreter::GetCurrentEntryPoint(JSThread *thread) if (method->IsNativeWithCallField()) { continue; } - JSTaggedValue func = frameHandler.GetFunction(); - JSHandle module(thread, JSFunction::Cast(func.GetTaggedObject())->GetModule()); + JSHandle module(thread, method->GetModule()); if (module->IsSourceTextModule()) { recordName.Update(SourceTextModule::Cast(module->GetTaggedObject())->GetEcmaModuleRecordName()); @@ -1363,6 +1436,7 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, const uint8_t thread->SetCurrentSPFrame(newSp); LOG_INST() << "Entry: Runtime Call " << std::hex << reinterpret_cast(sp) << " " << std::hex << reinterpret_cast(pc); + MethodEntry(thread); DISPATCH_OFFSET(0); } } @@ -1400,6 +1474,7 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, const uint8_t DISPATCH(DEPRECATED_CALLSPREAD_PREF_V8_V8_V8); } HANDLE_OPCODE(RETURN) { + MethodExit(thread); LOG_INST() << "return"; InterpretedFrame *state = GET_FRAME(sp); LOG_INST() << "Exit: Runtime Call " << std::hex << reinterpret_cast(sp) << " " @@ -1448,6 +1523,7 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, const uint8_t INTERPRETER_HANDLE_RETURN(); } HANDLE_OPCODE(RETURNUNDEFINED) { + MethodExit(thread); LOG_INST() << "return.undefined"; InterpretedFrame *state = GET_FRAME(sp); LOG_INST() << "Exit: Runtime Call " << std::hex << reinterpret_cast(sp) << " " @@ -3050,6 +3126,7 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, const uint8_t DISPATCH(CREATEASYNCGENERATOROBJ_V8); } HANDLE_OPCODE(ASYNCGENERATORRESOLVE_V8_V8_V8) { + MethodExit(thread); uint16_t v0 = READ_INST_8_0(); uint16_t v1 = READ_INST_8_1(); uint16_t v2 = READ_INST_8_2(); @@ -3235,6 +3312,7 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, const uint8_t thread->SetCurrentSPFrame(newSp); LOG_INST() << "Entry: Runtime SuperCall " << std::hex << reinterpret_cast(sp) << " " << std::hex << reinterpret_cast(pc); + MethodEntry(thread); DISPATCH_OFFSET(0); } } @@ -3370,6 +3448,7 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, const uint8_t thread->SetCurrentSPFrame(newSp); LOG_INST() << "Entry: Runtime SuperCall " << std::hex << reinterpret_cast(sp) << " " << std::hex << reinterpret_cast(pc); + MethodEntry(thread); DISPATCH_OFFSET(0); } } @@ -3505,6 +3584,7 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, const uint8_t thread->SetCurrentSPFrame(newSp); LOG_INST() << "Entry: Runtime SuperCall " << std::hex << reinterpret_cast(sp) << " " << std::hex << reinterpret_cast(pc); + MethodEntry(thread); DISPATCH_OFFSET(0); } } @@ -3640,6 +3720,7 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, const uint8_t thread->SetCurrentSPFrame(newSp); LOG_INST() << "Entry: Runtime SuperCall " << std::hex << reinterpret_cast(sp) << " " << std::hex << reinterpret_cast(pc); + MethodEntry(thread); DISPATCH_OFFSET(0); } } @@ -3671,6 +3752,7 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, const uint8_t << " imm:" << imm; SAVE_ACC(); auto constpool = GetConstantPool(sp); + auto module = GetEcmaModule(sp); JSObject *result = JSObject::Cast(GET_METHOD_FROM_CACHE(imm).GetTaggedObject()); RESTORE_ACC(); JSTaggedValue env = GET_ACC(); @@ -3728,9 +3810,14 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, const uint8_t pc = method->GetBytecodeArray() + pcOffset; break; } + if (!method->IsNativeWithCallField()) { + auto *debuggerMgr = thread->GetEcmaVM()->GetJsDebuggerManager(); + debuggerMgr->GetNotificationManager()->MethodExitEvent(thread, method); + } } if (pcOffset == INVALID_INDEX) { - return; + LOG_FULL(FATAL) << "EXCEPTION: EntryFrame Not Found"; + UNREACHABLE(); } auto exception = thread->GetException(); @@ -4005,6 +4092,7 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, const uint8_t thread->SetCurrentSPFrame(newSp); LOG_INST() << "Entry: Runtime New " << std::hex << reinterpret_cast(sp) << " " << std::hex << reinterpret_cast(pc); + MethodEntry(thread); DISPATCH_OFFSET(0); } } @@ -4141,6 +4229,7 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, const uint8_t thread->SetCurrentSPFrame(newSp); LOG_INST() << "Entry: Runtime New " << std::hex << reinterpret_cast(sp) << " " << std::hex << reinterpret_cast(pc); + MethodEntry(thread); DISPATCH_OFFSET(0); } } @@ -4276,6 +4365,7 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, const uint8_t thread->SetCurrentSPFrame(newSp); LOG_INST() << "Entry: Runtime New " << std::hex << reinterpret_cast(sp) << " " << std::hex << reinterpret_cast(pc); + MethodEntry(thread); DISPATCH_OFFSET(0); } } @@ -4445,6 +4535,7 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, const uint8_t LOG_INST() << "intrinsics::createarraywithbuffer" << " imm:" << imm; auto constpool = GetConstantPool(sp); + auto module = GetEcmaModule(sp); JSArray *result = JSArray::Cast(GET_METHOD_FROM_CACHE(imm).GetTaggedObject()); SAVE_PC(); JSTaggedValue res = SlowRuntimeStub::CreateArrayWithBuffer(thread, factory, result); @@ -4485,6 +4576,7 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, const uint8_t LOG_INST() << "intrinsics::createobjectwithbuffer" << " imm:" << imm; auto constpool = GetConstantPool(sp); + auto module = GetEcmaModule(sp); JSObject *result = JSObject::Cast(GET_METHOD_FROM_CACHE(imm).GetTaggedObject()); SAVE_PC(); @@ -4751,6 +4843,8 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, const uint8_t uint16_t length = READ_INST_8_3(); LOG_INST() << "intrinsics::definefunc length: " << length; auto constpool = GetConstantPool(sp); + + auto module = GetEcmaModule(sp); Method *method = Method::Cast(GET_METHOD_FROM_CACHE(methodId).GetTaggedObject()); ASSERT(method != nullptr); @@ -4763,7 +4857,6 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, const uint8_t jsFunc->SetLexicalEnv(thread, envHandle); JSFunction *currentFunc = JSFunction::Cast((GET_FRAME(sp)->function).GetTaggedObject()); - jsFunc->SetModule(thread, currentFunc->GetModule()); jsFunc->SetHomeObject(thread, currentFunc->GetHomeObject()); SET_ACC(JSTaggedValue(jsFunc)); @@ -4775,6 +4868,7 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, const uint8_t LOG_INST() << "intrinsics::definefunc length: " << length; auto constpool = GetConstantPool(sp); + auto module = GetEcmaModule(sp); Method *method = Method::Cast(GET_METHOD_FROM_CACHE(methodId).GetTaggedObject()); ASSERT(method != nullptr); @@ -4787,7 +4881,6 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, const uint8_t jsFunc->SetLexicalEnv(thread, envHandle); JSFunction *currentFunc = JSFunction::Cast((GET_FRAME(sp)->function).GetTaggedObject()); - jsFunc->SetModule(thread, currentFunc->GetModule()); jsFunc->SetHomeObject(thread, currentFunc->GetHomeObject()); SET_ACC(JSTaggedValue(jsFunc)); @@ -4799,6 +4892,7 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, const uint8_t LOG_INST() << "intrinsics::definemethod length: " << length; SAVE_ACC(); auto constpool = GetConstantPool(sp); + auto module = GetEcmaModule(sp); Method *method = Method::Cast(GET_METHOD_FROM_CACHE(methodId).GetTaggedObject()); ASSERT(method != nullptr); RESTORE_ACC(); @@ -4814,8 +4908,6 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, const uint8_t JSTaggedValue taggedCurEnv = state->env; result->SetLexicalEnv(thread, taggedCurEnv); - JSFunction *currentFunc = JSFunction::Cast((GET_FRAME(sp)->function).GetTaggedObject()); - result->SetModule(thread, currentFunc->GetModule()); SET_ACC(JSTaggedValue(result)); DISPATCH(DEFINEMETHOD_IMM8_ID16_IMM8); @@ -4826,6 +4918,7 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, const uint8_t LOG_INST() << "intrinsics::definemethod length: " << length; SAVE_ACC(); auto constpool = GetConstantPool(sp); + auto module = GetEcmaModule(sp); Method *method = Method::Cast(GET_METHOD_FROM_CACHE(methodId).GetTaggedObject()); ASSERT(method != nullptr); RESTORE_ACC(); @@ -4841,8 +4934,6 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, const uint8_t JSTaggedValue taggedCurEnv = state->env; result->SetLexicalEnv(thread, taggedCurEnv); - JSFunction *currentFunc = JSFunction::Cast((GET_FRAME(sp)->function).GetTaggedObject()); - result->SetModule(thread, currentFunc->GetModule()); SET_ACC(JSTaggedValue(result)); DISPATCH(DEFINEMETHOD_IMM16_ID16_IMM8); @@ -4868,7 +4959,6 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, const uint8_t JSFunction *cls = JSFunction::Cast(res.GetTaggedObject()); cls->SetLexicalEnv(thread, state->env); - cls->SetModule(thread, GetEcmaModule(sp)); SlowRuntimeStub::SetClassConstructorLength(thread, res, JSTaggedValue(length)); @@ -4897,8 +4987,6 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, const uint8_t cls->SetLexicalEnv(thread, state->env); - cls->SetModule(thread, GetEcmaModule(sp)); - SlowRuntimeStub::SetClassConstructorLength(thread, res, JSTaggedValue(length)); SET_ACC(res); @@ -4926,7 +5014,6 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, const uint8_t lexenv = GET_VREG_VALUE(v0); // slow runtime may gc cls->SetLexicalEnv(thread, lexenv); - cls->SetModule(thread, GetEcmaModule(sp)); SlowRuntimeStub::SetClassConstructorLength(thread, res, JSTaggedValue(length)); @@ -5049,6 +5136,7 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, const uint8_t DISPATCH(DEPRECATED_DELOBJPROP_PREF_V8_V8); } HANDLE_OPCODE(SUSPENDGENERATOR_V8) { + MethodExit(thread); uint16_t v0 = READ_INST_8_0(); LOG_INST() << "intrinsics::suspendgenerator" << " v" << v0; @@ -7291,7 +7379,7 @@ JSTaggedValue EcmaInterpreter::GetEcmaModule(JSTaggedType *sp) { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) JSFunction *currentFunc = JSFunction::Cast((GET_FRAME(sp)->function).GetTaggedObject()); - return currentFunc->GetModule(); + return currentFunc->GetCallTarget()->GetModule(); } JSTaggedValue EcmaInterpreter::GetConstantPool(JSTaggedType *sp) @@ -7357,10 +7445,14 @@ JSTaggedType *EcmaInterpreter::GetInterpreterFrameEnd(JSThread *thread, JSTagged } else { if (FrameHandler::GetFrameType(sp) == FrameType::INTERPRETER_FRAME || FrameHandler::GetFrameType(sp) == FrameType::INTERPRETER_FAST_NEW_FRAME) { - newSp = sp - InterpretedFrame::NumOfMembers(); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + newSp = sp - InterpretedFrame::NumOfMembers(); + } else if (FrameHandler::GetFrameType(sp) == FrameType::INTERPRETER_BUILTIN_FRAME) { + // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + newSp = sp - InterpretedBuiltinFrame::NumOfMembers(); } else { - newSp = - sp - InterpretedEntryFrame::NumOfMembers(); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + newSp = sp - InterpretedEntryFrame::NumOfMembers(); } } return newSp; diff --git a/ecmascript/interpreter/interpreter.h b/ecmascript/interpreter/interpreter.h index b37c6a53e2e5f192d8a03e34b4b90a976b4b106e..73c166e2d79fcca62f13379cabb12977d961f693 100644 --- a/ecmascript/interpreter/interpreter.h +++ b/ecmascript/interpreter/interpreter.h @@ -61,6 +61,8 @@ public: static inline bool UpdateHotnessCounter(JSThread* thread, JSTaggedType *sp, JSTaggedValue acc, int32_t offset); static inline void NotifyBytecodePcChanged(JSThread *thread); static inline void NotifyDebuggerStmt(JSThread *thread); + static inline void MethodEntry(JSThread *thread); + static inline void MethodExit(JSThread *thread); static inline const JSPandaFile *GetNativeCallPandafile(JSThread *thread); static inline JSTaggedValue GetCurrentEntryPoint(JSThread *thread); static inline JSTaggedValue GetFunction(JSTaggedType *sp); diff --git a/ecmascript/interpreter/interpreter_assembly.cpp b/ecmascript/interpreter/interpreter_assembly.cpp index 3f88c46acd5fa2ecd06335c8816a3a3c3c9941fe..a9c73083630872702a6e074bb454b113b718a544 100644 --- a/ecmascript/interpreter/interpreter_assembly.cpp +++ b/ecmascript/interpreter/interpreter_assembly.cpp @@ -32,6 +32,7 @@ #include "ecmascript/mem/concurrent_marker.h" #include "ecmascript/runtime_call_id.h" #include "ecmascript/template_string.h" +#include "ecmascript/debugger/js_debugger_manager.h" #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER) #include "ecmascript/dfx/cpu_profiler/cpu_profiler.h" @@ -256,8 +257,19 @@ JSTaggedValue InterpreterAssembly::Execute(EcmaRuntimeCallInfo *info) #if ECMASCRIPT_ENABLE_FUNCTION_CALL_TIMER RuntimeStubs::StartCallTimer(thread->GetGlueAddr(), info->GetFunctionValue().GetRawData(), false); #endif + if (thread->IsDebugMode() && !method->IsNativeWithCallField()) { + JSHandle func(thread, info->GetFunctionValue()); + JSTaggedValue env = func->GetLexicalEnv(); + MethodEntry(thread, method, env); + } auto acc = reinterpret_cast(entry)(thread->GetGlueAddr(), callTarget, method, method->GetCallField(), argc, argv); + + if (thread->IsEntryFrameDroppedTrue()) { + thread->PendingEntryFrameDroppedState(); + return JSTaggedValue::Hole(); + } + auto sp = const_cast(thread->GetCurrentSPFrame()); ASSERT(FrameHandler::GetFrameType(sp) == FrameType::INTERPRETER_ENTRY_FRAME); auto prevEntry = InterpretedEntryFrame::GetFrameFromSp(sp)->GetPrevFrameFp(); @@ -266,11 +278,30 @@ JSTaggedValue InterpreterAssembly::Execute(EcmaRuntimeCallInfo *info) return JSTaggedValue(acc); } +void InterpreterAssembly::MethodEntry(JSThread *thread, Method *method, JSTaggedValue env) +{ + FrameHandler frameHandler(thread); + for (; frameHandler.HasFrame(); frameHandler.PrevJSFrame()) { + if (frameHandler.IsEntryFrame()) { + continue; + } + auto *debuggerMgr = thread->GetEcmaVM()->GetJsDebuggerManager(); + debuggerMgr->GetNotificationManager()->MethodEntryEvent(thread, method, env); + return; + } +} + JSTaggedValue InterpreterAssembly::GeneratorReEnterInterpreter(JSThread *thread, JSHandle context) { // check is or not debugger thread->CheckSwitchDebuggerBCStub(); auto entry = thread->GetRTInterface(kungfu::RuntimeStubCSigns::ID_GeneratorReEnterAsmInterp); + JSTaggedValue func = context->GetMethod(); + Method *method = ECMAObject::Cast(func.GetTaggedObject())->GetCallTarget(); + JSTaggedValue env = context->GetLexicalEnv(); + if (thread->IsDebugMode() && !method->IsNativeWithCallField()) { + MethodEntry(thread, method, env); + } auto acc = reinterpret_cast(entry)(thread->GetGlueAddr(), context.GetTaggedType()); return JSTaggedValue(acc); } @@ -3940,7 +3971,8 @@ void InterpreterAssembly::HandleDeprecatedCreateobjecthavingmethodPrefImm16( << " imm:" << imm; SAVE_ACC(); constpool = GetConstantPool(sp); - JSObject *result = JSObject::Cast(ConstantPool::GetMethodFromCache(thread, constpool, imm).GetTaggedObject()); + JSObject *result = + JSObject::Cast(ConstantPool::GetMethodFromCache(thread, constpool, GetModule(sp), imm).GetTaggedObject()); RESTORE_ACC(); JSTaggedValue env = GET_ACC(); @@ -4497,11 +4529,9 @@ void InterpreterAssembly::HandleDeprecatedDefineclasswithbufferPrefId16Imm16Imm1 JSTaggedValue proto = GET_VREG_VALUE(v1); SAVE_PC(); - JSFunction *currentFunc = - JSFunction::Cast(((reinterpret_cast(sp) - 1)->function).GetTaggedObject()); JSTaggedValue res = SlowRuntimeStub::CreateClassWithBuffer(thread, proto, lexenv, GetConstantPool(sp), - methodId, methodId + 1, currentFunc->GetModule()); + methodId, methodId + 1, GetModule(sp)); INTERPRETER_RETURN_IF_ABRUPT(res); ASSERT(res.IsClassConstructor()); @@ -4509,7 +4539,6 @@ void InterpreterAssembly::HandleDeprecatedDefineclasswithbufferPrefId16Imm16Imm1 lexenv = GET_VREG_VALUE(v0); // slow runtime may gc cls->SetLexicalEnv(thread, lexenv); - cls->SetModule(thread, currentFunc->GetModule()); SlowRuntimeStub::SetClassConstructorLength(thread, res, JSTaggedValue(length)); @@ -4738,7 +4767,8 @@ void InterpreterAssembly::HandleDeprecatedCreateobjectwithbufferPrefImm16( LOG_INST() << "intrinsics::createobjectwithbuffer" << " imm:" << imm; constpool = GetConstantPool(sp); - JSObject *result = JSObject::Cast(ConstantPool::GetMethodFromCache(thread, constpool, imm).GetTaggedObject()); + JSObject *result = + JSObject::Cast(ConstantPool::GetMethodFromCache(thread, constpool, GetModule(sp), imm).GetTaggedObject()); SAVE_PC(); EcmaVM *ecmaVm = thread->GetEcmaVM(); @@ -4757,7 +4787,8 @@ void InterpreterAssembly::HandleDeprecatedCreatearraywithbufferPrefImm16( LOG_INST() << "intrinsics::createarraywithbuffer" << " imm:" << imm; constpool = GetConstantPool(sp); - JSArray *result = JSArray::Cast(ConstantPool::GetMethodFromCache(thread, constpool, imm).GetTaggedObject()); + JSArray *result = + JSArray::Cast(ConstantPool::GetMethodFromCache(thread, constpool, GetModule(sp), imm).GetTaggedObject()); SAVE_PC(); EcmaVM *ecmaVm = thread->GetEcmaVM(); ObjectFactory *factory = ecmaVm->GetFactory(); @@ -5862,11 +5893,9 @@ void InterpreterAssembly::HandleDefineclasswithbufferImm16Id16Id16Imm16V8( JSTaggedValue proto = GET_VREG_VALUE(v0); SAVE_PC(); - JSFunction *currentFunc = - JSFunction::Cast(((reinterpret_cast(sp) - 1)->function).GetTaggedObject()); JSTaggedValue res = SlowRuntimeStub::CreateClassWithBuffer(thread, proto, state->env, GetConstantPool(sp), - methodId, literaId, currentFunc->GetModule()); + methodId, literaId, GetModule(sp)); INTERPRETER_RETURN_IF_ABRUPT(res); ASSERT(res.IsClassConstructor()); @@ -5874,8 +5903,6 @@ void InterpreterAssembly::HandleDefineclasswithbufferImm16Id16Id16Imm16V8( cls->SetLexicalEnv(thread, state->env); - cls->SetModule(thread, currentFunc->GetModule()); - SlowRuntimeStub::SetClassConstructorLength(thread, res, JSTaggedValue(length)); SET_ACC(res); @@ -5897,18 +5924,15 @@ void InterpreterAssembly::HandleDefineclasswithbufferImm8Id16Id16Imm16V8( SAVE_PC(); InterpretedFrame *state = (reinterpret_cast(sp) - 1); - JSFunction *currentFunc = - JSFunction::Cast(((reinterpret_cast(sp) - 1)->function).GetTaggedObject()); JSTaggedValue res = SlowRuntimeStub::CreateClassWithBuffer(thread, proto, state->env, GetConstantPool(sp), - methodId, literaId, currentFunc->GetModule()); + methodId, literaId, GetModule(sp)); INTERPRETER_RETURN_IF_ABRUPT(res); ASSERT(res.IsClassConstructor()); JSFunction *cls = JSFunction::Cast(res.GetTaggedObject()); cls->SetLexicalEnv(thread, state->env); - cls->SetModule(thread, currentFunc->GetModule()); SlowRuntimeStub::SetClassConstructorLength(thread, res, JSTaggedValue(length)); @@ -6477,7 +6501,8 @@ void InterpreterAssembly::HandleDefinemethodImm16Id16Imm8( LOG_INST() << "intrinsics::definemethod length: " << length; SAVE_ACC(); constpool = GetConstantPool(sp); - Method *method = Method::Cast(ConstantPool::GetMethodFromCache(thread, constpool, methodId).GetTaggedObject()); + Method *method = + Method::Cast(ConstantPool::GetMethodFromCache(thread, constpool, GetModule(sp), methodId).GetTaggedObject()); ASSERT(method != nullptr); RESTORE_ACC(); @@ -6492,9 +6517,6 @@ void InterpreterAssembly::HandleDefinemethodImm16Id16Imm8( JSTaggedValue taggedCurEnv = state->env; result->SetLexicalEnv(thread, taggedCurEnv); - JSFunction *currentFunc = - JSFunction::Cast(((reinterpret_cast(sp) - 1)->function).GetTaggedObject()); - result->SetModule(thread, currentFunc->GetModule()); SET_ACC(JSTaggedValue(result)); DISPATCH(DEFINEMETHOD_IMM16_ID16_IMM8); @@ -6592,7 +6614,8 @@ void InterpreterAssembly::HandleDefinemethodImm8Id16Imm8( LOG_INST() << "intrinsics::definemethod length: " << length; SAVE_ACC(); constpool = GetConstantPool(sp); - Method *method = Method::Cast(ConstantPool::GetMethodFromCache(thread, constpool, methodId).GetTaggedObject()); + Method *method = + Method::Cast(ConstantPool::GetMethodFromCache(thread, constpool, GetModule(sp), methodId).GetTaggedObject()); ASSERT(method != nullptr); RESTORE_ACC(); @@ -6607,9 +6630,6 @@ void InterpreterAssembly::HandleDefinemethodImm8Id16Imm8( JSTaggedValue taggedCurEnv = state->env; result->SetLexicalEnv(thread, taggedCurEnv); - JSFunction *currentFunc = - JSFunction::Cast(((reinterpret_cast(sp) - 1)->function).GetTaggedObject()); - result->SetModule(thread, currentFunc->GetModule()); SET_ACC(JSTaggedValue(result)); DISPATCH(DEFINEMETHOD_IMM8_ID16_IMM8); @@ -6624,7 +6644,8 @@ void InterpreterAssembly::HandleDefinefuncImm16Id16Imm8( LOG_INST() << "intrinsics::definefunc length: " << length; constpool = GetConstantPool(sp); - Method *method = Method::Cast(ConstantPool::GetMethodFromCache(thread, constpool, methodId).GetTaggedObject()); + Method *method = + Method::Cast(ConstantPool::GetMethodFromCache(thread, constpool, GetModule(sp), methodId).GetTaggedObject()); ASSERT(method != nullptr); auto res = SlowRuntimeStub::DefineFunc(thread, method); @@ -6637,7 +6658,6 @@ void InterpreterAssembly::HandleDefinefuncImm16Id16Imm8( JSFunction *currentFunc = JSFunction::Cast(((reinterpret_cast(sp) - 1)->function).GetTaggedObject()); - jsFunc->SetModule(thread, currentFunc->GetModule()); jsFunc->SetHomeObject(thread, currentFunc->GetHomeObject()); SET_ACC(JSTaggedValue(jsFunc)); @@ -6652,7 +6672,8 @@ void InterpreterAssembly::HandleDefinefuncImm8Id16Imm8( uint16_t length = READ_INST_8_3(); LOG_INST() << "intrinsics::definefunc length: " << length; constpool = GetConstantPool(sp); - Method *method = Method::Cast(ConstantPool::GetMethodFromCache(thread, constpool, methodId).GetTaggedObject()); + Method *method = + Method::Cast(ConstantPool::GetMethodFromCache(thread, constpool, GetModule(sp), methodId).GetTaggedObject()); ASSERT(method != nullptr); auto res = SlowRuntimeStub::DefineFunc(thread, method); @@ -6665,7 +6686,6 @@ void InterpreterAssembly::HandleDefinefuncImm8Id16Imm8( JSFunction *currentFunc = JSFunction::Cast(((reinterpret_cast(sp) - 1)->function).GetTaggedObject()); - jsFunc->SetModule(thread, currentFunc->GetModule()); jsFunc->SetHomeObject(thread, currentFunc->GetHomeObject()); SET_ACC(JSTaggedValue(jsFunc)); @@ -7324,11 +7344,9 @@ void InterpreterAssembly::HandleCreateobjectwithbufferImm16Id16( LOG_INST() << "intrinsics::createobjectwithbuffer" << " imm:" << imm; constpool = GetConstantPool(sp); - JSFunction *currentFunc = - JSFunction::Cast(((reinterpret_cast(sp) - 1)->function).GetTaggedObject()); JSObject *result = JSObject::Cast( ConstantPool::GetLiteralFromCache( - thread, constpool, imm, currentFunc->GetModule()).GetTaggedObject()); + thread, constpool, imm, GetModule(sp)).GetTaggedObject()); SAVE_PC(); InterpretedFrame *state = (reinterpret_cast(sp) - 1); EcmaVM *ecmaVm = thread->GetEcmaVM(); @@ -7347,11 +7365,9 @@ void InterpreterAssembly::HandleCreateobjectwithbufferImm8Id16( LOG_INST() << "intrinsics::createobjectwithbuffer" << " imm:" << imm; constpool = GetConstantPool(sp); - JSFunction *currentFunc = - JSFunction::Cast(((reinterpret_cast(sp) - 1)->function).GetTaggedObject()); JSObject *result = JSObject::Cast( ConstantPool::GetLiteralFromCache( - thread, constpool, imm, currentFunc->GetModule()).GetTaggedObject()); + thread, constpool, imm, GetModule(sp)).GetTaggedObject()); SAVE_PC(); InterpretedFrame *state = (reinterpret_cast(sp) - 1); EcmaVM *ecmaVm = thread->GetEcmaVM(); @@ -7386,11 +7402,9 @@ void InterpreterAssembly::HandleCreatearraywithbufferImm8Id16( LOG_INST() << "intrinsics::createarraywithbuffer" << " imm:" << imm; constpool = GetConstantPool(sp); - JSFunction *currentFunc = - JSFunction::Cast(((reinterpret_cast(sp) - 1)->function).GetTaggedObject()); JSArray *result = JSArray::Cast( ConstantPool::GetLiteralFromCache( - thread, constpool, imm, currentFunc->GetModule()).GetTaggedObject()); + thread, constpool, imm, GetModule(sp)).GetTaggedObject()); SAVE_PC(); EcmaVM *ecmaVm = thread->GetEcmaVM(); ObjectFactory *factory = ecmaVm->GetFactory(); @@ -7535,6 +7549,14 @@ JSTaggedValue InterpreterAssembly::GetConstantPool(JSTaggedType *sp) return method->GetConstantPool(); } +JSTaggedValue InterpreterAssembly::GetModule(JSTaggedType *sp) +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + AsmInterpretedFrame *state = reinterpret_cast(sp) - 1; + Method *method = JSFunction::Cast(state->function.GetTaggedObject())->GetCallTarget(); + return method->GetModule(); +} + JSTaggedValue InterpreterAssembly::GetProfileTypeInfo(JSTaggedType *sp) { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) diff --git a/ecmascript/interpreter/interpreter_assembly.h b/ecmascript/interpreter/interpreter_assembly.h index 5ba6b9f0f591deaedbc2aa7bfff5da97da62af64..eeb68af97ba0f4853f3d35ea3f40958c99fc0414 100644 --- a/ecmascript/interpreter/interpreter_assembly.h +++ b/ecmascript/interpreter/interpreter_assembly.h @@ -40,6 +40,7 @@ public: static JSTaggedValue Execute(EcmaRuntimeCallInfo *info); static JSTaggedValue GeneratorReEnterInterpreter(JSThread *thread, JSHandle context); static inline size_t GetJumpSizeAfterCall(const uint8_t *prevPc); + static inline void MethodEntry(JSThread *thread, Method *method, JSTaggedValue env); static inline JSTaggedValue UpdateHotnessCounter(JSThread* thread, JSTaggedType *sp); static inline void InterpreterFrameCopyArgs(JSTaggedType *newSp, uint32_t numVregs, uint32_t numActualArgs, @@ -48,6 +49,7 @@ public: static JSTaggedValue GetNewTarget(JSTaggedType *sp); static JSTaggedValue GetThis(JSTaggedType *sp); static JSTaggedValue GetConstantPool(JSTaggedType *sp); + static JSTaggedValue GetModule(JSTaggedType *sp); static JSTaggedValue GetProfileTypeInfo(JSTaggedType *sp); static uint32_t GetNumArgs(JSTaggedType *sp, uint32_t restIdx, uint32_t &startIdx); static JSTaggedType *GetAsmInterpreterFramePointer(AsmInterpretedFrame *state); diff --git a/ecmascript/jobs/micro_job_queue.cpp b/ecmascript/jobs/micro_job_queue.cpp index 86ee75fdd8d6045a820b73fd8a7ecfbf19dee008..d598dea14507448157eb13b6d001e52abd2a526a 100644 --- a/ecmascript/jobs/micro_job_queue.cpp +++ b/ecmascript/jobs/micro_job_queue.cpp @@ -27,6 +27,13 @@ #include "ecmascript/tagged_queue.h" namespace panda::ecmascript::job { +uint32_t MicroJobQueue::GetPromiseQueueSize(JSThread *thread, JSHandle jobQueue) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle promiseQueue(thread, jobQueue->GetPromiseJobQueue()); + return promiseQueue->Size(); +} + void MicroJobQueue::EnqueueJob(JSThread *thread, JSHandle jobQueue, QueueType queueType, const JSHandle &job, const JSHandle &argv) { @@ -75,4 +82,11 @@ void MicroJobQueue::ExecutePendingJob(JSThread *thread, JSHandle } } } + +bool MicroJobQueue::HasPendingJob(JSThread *thread, JSHandle jobQueue) +{ + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle promiseQueue(thread, jobQueue->GetPromiseJobQueue()); + return !promiseQueue->Empty(); +} } // namespace panda::ecmascript::job diff --git a/ecmascript/jobs/micro_job_queue.h b/ecmascript/jobs/micro_job_queue.h index fb2c81a20baa12c62bb1db56bdd27b34becc7db9..35f04c54991088a9a5307f5ca7d30fc58d7cec9c 100644 --- a/ecmascript/jobs/micro_job_queue.h +++ b/ecmascript/jobs/micro_job_queue.h @@ -35,9 +35,11 @@ public: return static_cast(object); } + static uint32_t GetPromiseQueueSize(JSThread *thread, JSHandle jobQueue); static void EnqueueJob(JSThread *thread, JSHandle jobQueue, QueueType queueType, const JSHandle &job, const JSHandle &argv); static void ExecutePendingJob(JSThread *thread, JSHandle jobQueue); + static bool HasPendingJob(JSThread *thread, JSHandle jobQueue); static constexpr size_t PROMISE_JOB_QUEUE_OFFSET = Record::SIZE; ACCESSORS(PromiseJobQueue, PROMISE_JOB_QUEUE_OFFSET, SCRIPT_JOB_QUEUE_OFFSET); diff --git a/ecmascript/jobs/pending_job.h b/ecmascript/jobs/pending_job.h index b3b2249cb76bfb0bda3651d046425494f1cb0ba2..81d5ca0ef5d064aadadef63ef7b207b7b7b44015 100644 --- a/ecmascript/jobs/pending_job.h +++ b/ecmascript/jobs/pending_job.h @@ -40,13 +40,11 @@ public: { [[maybe_unused]] EcmaHandleScope handleScope(thread); EXECUTE_JOB_HITRACE(pendingJob); - tooling::JsDebuggerManager *jsDebuggerManager = thread->GetEcmaVM()->GetJsDebuggerManager(); - jsDebuggerManager->GetNotificationManager()->PendingJobEntryEvent(); JSHandle job(thread, pendingJob->GetJob()); ASSERT(job->IsCallable()); JSHandle argv(thread, pendingJob->GetArguments()); - const int32_t argsLength = static_cast(argv->GetLength()); + const uint32_t argsLength = argv->GetLength(); JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, job, undefined, undefined, argsLength); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); diff --git a/ecmascript/js_api/js_api_arraylist.cpp b/ecmascript/js_api/js_api_arraylist.cpp index ac0770a3c18ae6c95e470b9993e60dcd503d062f..8c79ba3d871776d33ab898672434d4543478e394 100644 --- a/ecmascript/js_api/js_api_arraylist.cpp +++ b/ecmascript/js_api/js_api_arraylist.cpp @@ -78,7 +78,7 @@ JSHandle JSAPIArrayList::Clone(JSThread *thread, const JSHandle< JSHandle srcElements(thread, obj->GetElements()); ASSERT(!srcElements->IsDictionaryMode()); - int32_t length = obj->GetSize(); + uint32_t length = obj->GetSize(); auto factory = thread->GetEcmaVM()->GetFactory(); JSHandle newArrayList = factory->NewJSAPIArrayList(0); newArrayList->SetLength(thread, JSTaggedValue(length)); @@ -199,7 +199,7 @@ bool JSAPIArrayList::Remove(JSThread *thread, const JSHandle &ar const JSHandle &value) { int index = GetIndexOf(thread, arrayList, value); - int length = arrayList->GetSize(); + uint32_t length = arrayList->GetSize(); if (index >= 0) { JSHandle elements(thread, arrayList->GetElements()); ASSERT(!elements->IsDictionaryMode()); @@ -253,7 +253,7 @@ JSTaggedValue JSAPIArrayList::ReplaceAllElements(JSThread *thread, const JSHandl const JSHandle &thisArg) { JSHandle arrayList = JSHandle::Cast(thisHandle); - uint32_t length = static_cast(arrayList->GetSize()); + uint32_t length = arrayList->GetSize(); JSMutableHandle key(thread, JSTaggedValue::Undefined()); JSMutableHandle kValue(thread, JSTaggedValue::Undefined()); const int32_t argsLength = 3; @@ -334,7 +334,7 @@ JSTaggedValue JSAPIArrayList::ForEach(JSThread *thread, const JSHandle &thisArg) { JSHandle arrayList = JSHandle::Cast(thisHandle); - uint32_t length = static_cast(arrayList->GetSize()); + uint32_t length = arrayList->GetSize(); JSMutableHandle key(thread, JSTaggedValue::Undefined()); JSMutableHandle kValue(thread, JSTaggedValue::Undefined()); const int32_t argsLength = 3; @@ -348,8 +348,8 @@ JSTaggedValue JSAPIArrayList::ForEach(JSThread *thread, const JSHandleSetCallArg(kValue.GetTaggedValue(), key.GetTaggedValue(), thisHandle.GetTaggedValue()); JSTaggedValue funcResult = JSFunction::Call(info); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, funcResult); - if (static_cast(length) != arrayList->GetSize()) { - length = static_cast(arrayList->GetSize()); + if (length != arrayList->GetSize()) { + length = arrayList->GetSize(); } } @@ -376,12 +376,12 @@ JSHandle JSAPIArrayList::GrowCapacity(const JSThread *thread, const bool JSAPIArrayList::Has(const JSTaggedValue value) const { TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); - int32_t length = GetSize(); + uint32_t length = GetSize(); if (length == 0) { return false; } - for (int32_t i = 0; i < length; i++) { + for (uint32_t i = 0; i < length; i++) { if (JSTaggedValue::SameValue(elements->Get(i), value)) { return true; } @@ -405,6 +405,7 @@ bool JSAPIArrayList::GetOwnProperty(JSThread *thread, const JSHandle result = JSTaggedValue::ToString(thread, key.GetTaggedValue()); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); CString errorMsg = "The type of \"index\" can not obtain attributes of no-number type. Received value is: " + ConvertToString(*result); diff --git a/ecmascript/js_api/js_api_arraylist.h b/ecmascript/js_api/js_api_arraylist.h index a62ae8c4dbc0b52ac32f45f9f357c5c4b20cdff5..7b2438a6dfdf21c0e6fbe46d7018a6a3bed34977 100644 --- a/ecmascript/js_api/js_api_arraylist.h +++ b/ecmascript/js_api/js_api_arraylist.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_JS_API_ARRAYLIST_H -#define ECMASCRIPT_JS_API_ARRAYLIST_H +#ifndef ECMASCRIPT_JS_API_JS_API_ARRAYLIST_H +#define ECMASCRIPT_JS_API_JS_API_ARRAYLIST_H #include "ecmascript/js_object.h" #include "ecmascript/js_tagged_value-inl.h" @@ -77,7 +77,7 @@ public: static bool SetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, const JSHandle &value); - inline int GetSize() const + inline uint32_t GetSize() const { return GetLength().GetInt(); } @@ -99,4 +99,4 @@ private: }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_JS_API_ARRAYLIST_H +#endif // ECMASCRIPT_JS_API_JS_API_ARRAYLIST_H diff --git a/ecmascript/js_api/js_api_arraylist_iterator.h b/ecmascript/js_api/js_api_arraylist_iterator.h index 192480c6f70c4c4ade98f5e6650b18e0c5e89c1f..067889655f6a62283beb3a89aacf343d0f376406 100644 --- a/ecmascript/js_api/js_api_arraylist_iterator.h +++ b/ecmascript/js_api/js_api_arraylist_iterator.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_JS_API_ARRAYLIST_ITERATOR_H -#define ECMASCRIPT_JS_API_ARRAYLIST_ITERATOR_H +#ifndef ECMASCRIPT_JS_API_JS_API_ARRAYLIST_ITERATOR_H +#define ECMASCRIPT_JS_API_JS_API_ARRAYLIST_ITERATOR_H #include "ecmascript/js_iterator.h" #include "ecmascript/js_object.h" @@ -44,4 +44,4 @@ public: }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_JS_API_ARRAYLIST_ITERATOR_H +#endif // ECMASCRIPT_JS_API_JS_API_ARRAYLIST_ITERATOR_H diff --git a/ecmascript/js_api/js_api_deque.cpp b/ecmascript/js_api/js_api_deque.cpp index 65f8afbae33b7371d7283b49f72ec308250c5a7b..1ba39fe30b994b17eb43242d492cfca2b51db4c4 100644 --- a/ecmascript/js_api/js_api_deque.cpp +++ b/ecmascript/js_api/js_api_deque.cpp @@ -242,6 +242,7 @@ bool JSAPIDeque::GetOwnProperty(JSThread *thread, const JSHandle &de uint32_t index = 0; if (UNLIKELY(!JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) { JSHandle result = JSTaggedValue::ToString(thread, key.GetTaggedValue()); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); CString errorMsg = "The type of \"key\" can not obtain attributes of no-number type. Received value is: " + ConvertToString(*result); diff --git a/ecmascript/js_api/js_api_deque.h b/ecmascript/js_api/js_api_deque.h index 8e5d955325b70e204ebbec6d9f967a0e62d40cc1..d407de9440e04642153555e82b051f44ddfbe202 100644 --- a/ecmascript/js_api/js_api_deque.h +++ b/ecmascript/js_api/js_api_deque.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_JS_API_DEQUE_H -#define ECMASCRIPT_JS_API_DEQUE_H +#ifndef ECMASCRIPT_JS_API_JS_API_DEQUE_H +#define ECMASCRIPT_JS_API_JS_API_DEQUE_H #include #include @@ -92,4 +92,4 @@ private: }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_JS_API_DEQUE_H +#endif // ECMASCRIPT_JS_API_JS_API_DEQUE_H diff --git a/ecmascript/js_api/js_api_deque_iterator.h b/ecmascript/js_api/js_api_deque_iterator.h index 5bccc32636bb323fb1bf4597433f11f65d8f7564..83972be235e1517908a761b063f049e6325af04d 100644 --- a/ecmascript/js_api/js_api_deque_iterator.h +++ b/ecmascript/js_api/js_api_deque_iterator.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_JS_API_DEQUE_ITERATOR_H -#define ECMASCRIPT_JS_API_DEQUE_ITERATOR_H +#ifndef ECMASCRIPT_JS_API_JS_API_DEQUE_ITERATOR_H +#define ECMASCRIPT_JS_API_JS_API_DEQUE_ITERATOR_H #include #include @@ -48,4 +48,4 @@ public: }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_JS_API_DEQUE_ITERATOR_H +#endif // ECMASCRIPT_JS_API_JS_API_DEQUE_ITERATOR_H diff --git a/ecmascript/js_api/js_api_hashmap.h b/ecmascript/js_api/js_api_hashmap.h index c79f71fb82ea8e99a712a008c380cf06d39fa0fc..745eff7b2cc3a56977c76a3ee0b11ff6e2e2394e 100644 --- a/ecmascript/js_api/js_api_hashmap.h +++ b/ecmascript/js_api/js_api_hashmap.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_JS_API_HASHMAP_H -#define ECMASCRIPT_JS_API_HASHMAP_H +#ifndef ECMASCRIPT_JS_API_JS_API_HASHMAP_H +#define ECMASCRIPT_JS_API_JS_API_HASHMAP_H #include "ecmascript/js_object-inl.h" #include "ecmascript/js_object.h" @@ -61,4 +61,4 @@ private: static bool HasValueRBTreeNode(JSTaggedValue node, JSTaggedValue value); }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_JS_API_HASHMAP_H +#endif // ECMASCRIPT_JS_API_JS_API_HASHMAP_H diff --git a/ecmascript/js_api/js_api_hashmap_iterator.h b/ecmascript/js_api/js_api_hashmap_iterator.h index 63d9bc529301a1aa1f35c2466bf8cd48c9fccff4..0bbe1406dd2413e89d23c5db99a7fbe5590471b7 100644 --- a/ecmascript/js_api/js_api_hashmap_iterator.h +++ b/ecmascript/js_api/js_api_hashmap_iterator.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_JS_API_HASHMAP_ITERATOR_H -#define ECMASCRIPT_JS_API_HASHMAP_ITERATOR_H +#ifndef ECMASCRIPT_JS_API_JS_API_HASHMAP_ITERATOR_H +#define ECMASCRIPT_JS_API_JS_API_HASHMAP_ITERATOR_H #include "ecmascript/js_iterator.h" #include "ecmascript/js_object.h" @@ -56,4 +56,4 @@ public: DECL_DUMP() }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_JS_API_HASHMAP_ITERATOR_H \ No newline at end of file +#endif // ECMASCRIPT_JS_API_JS_API_HASHMAP_ITERATOR_H \ No newline at end of file diff --git a/ecmascript/js_api/js_api_hashset.cpp b/ecmascript/js_api/js_api_hashset.cpp index a6de4b02423f3161d07e22aa0f893d460e142b4e..1e4b6f8f9337dff8e1f56eabdcbaa9ff95514fce 100644 --- a/ecmascript/js_api/js_api_hashset.cpp +++ b/ecmascript/js_api/js_api_hashset.cpp @@ -32,6 +32,7 @@ JSTaggedValue JSAPIHashSet::Has(JSThread *thread, JSTaggedValue value) { if (!TaggedHashArray::IsKey(value)) { JSHandle result = JSTaggedValue::ToString(thread, value); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"value\" must be Key of JS. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); @@ -80,6 +81,7 @@ JSTaggedValue JSAPIHashSet::Remove(JSThread *thread, JSHandle hash { if (!TaggedHashArray::IsKey(key)) { JSHandle result = JSTaggedValue::ToString(thread, key); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"key\" must be not null. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); diff --git a/ecmascript/js_api/js_api_hashset.h b/ecmascript/js_api/js_api_hashset.h index a8ea1b02407a822577f81666641870ec0dbb3a27..59dbaab5f7b3c79619ce659dab25a1e4c00a2557 100644 --- a/ecmascript/js_api/js_api_hashset.h +++ b/ecmascript/js_api/js_api_hashset.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_JS_API_HASHSET_H -#define ECMASCRIPT_JS_API_HASHSET_H +#ifndef ECMASCRIPT_JS_API_JS_API_HASHSET_H +#define ECMASCRIPT_JS_API_JS_API_HASHSET_H #include "ecmascript/js_object-inl.h" #include "ecmascript/js_tagged_value-inl.h" @@ -47,4 +47,4 @@ public: DECL_DUMP() }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_JS_API_HASHSET_H \ No newline at end of file +#endif // ECMASCRIPT_JS_API_JS_API_HASHSET_H \ No newline at end of file diff --git a/ecmascript/js_api/js_api_hashset_iterator.h b/ecmascript/js_api/js_api_hashset_iterator.h index 669602db440569e67cf6e9678c22f3cf0335c69b..6f060f64c20f38276bf9003ef752d1a454f60033 100644 --- a/ecmascript/js_api/js_api_hashset_iterator.h +++ b/ecmascript/js_api/js_api_hashset_iterator.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_JS_API_HASHSET_ITERATOR_H -#define ECMASCRIPT_JS_API_HASHSET_ITERATOR_H +#ifndef ECMASCRIPT_JS_API_JS_API_HASHSET_ITERATOR_H +#define ECMASCRIPT_JS_API_JS_API_HASHSET_ITERATOR_H #include "ecmascript/js_iterator.h" #include "ecmascript/js_object.h" @@ -55,4 +55,4 @@ public: DECL_DUMP() }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_JS_API_HASHSET_ITERATOR_H \ No newline at end of file +#endif // ECMASCRIPT_JS_API_JS_API_HASHSET_ITERATOR_H \ No newline at end of file diff --git a/ecmascript/js_api/js_api_lightweightmap.cpp b/ecmascript/js_api/js_api_lightweightmap.cpp index a13968cbf3120037c4b34b4da5f9697940872c52..f434f61df9c00ed4f38bfe3ca4be67bda35df9a7 100644 --- a/ecmascript/js_api/js_api_lightweightmap.cpp +++ b/ecmascript/js_api/js_api_lightweightmap.cpp @@ -31,8 +31,8 @@ JSTaggedValue JSAPILightWeightMap::IncreaseCapacityTo(JSThread *thread, const JSHandle &lightWeightMap, int32_t index) { - int32_t num = lightWeightMap->GetSize(); - if (index < DEFAULT_CAPACITY_LENGTH || num >= index) { + uint32_t num = lightWeightMap->GetSize(); + if (index < DEFAULT_CAPACITY_LENGTH || static_cast(num) >= index) { return JSTaggedValue::False(); } JSHandle hashArray = GetArrayByKind(thread, lightWeightMap, AccossorsKind::HASH); @@ -51,7 +51,7 @@ void JSAPILightWeightMap::InsertValue(const JSThread *thread, const JSHandle &value, AccossorsKind kind) { JSHandle array = GetArrayByKind(thread, lightWeightMap, kind); - int32_t len = lightWeightMap->GetSize(); + uint32_t len = lightWeightMap->GetSize(); JSHandle newArray = GrowCapacity(thread, array, len + 1); TaggedArray::InsertElementByIndex(thread, newArray, value, index, len); SetArrayByKind(thread, lightWeightMap, newArray, kind); @@ -61,7 +61,7 @@ void JSAPILightWeightMap::ReplaceValue(const JSThread *thread, const JSHandle &value, AccossorsKind kind) { JSHandle array = GetArrayByKind(thread, lightWeightMap, kind); - ASSERT(0 <= index || index < lightWeightMap->GetSize()); + ASSERT(0 <= index || index < static_cast(lightWeightMap->GetSize())); array->Set(thread, index, value.GetTaggedValue()); } @@ -104,8 +104,8 @@ JSTaggedValue JSAPILightWeightMap::Get(JSThread *thread, const JSHandle &lightWeightMap, const JSHandle &newLightWeightMap) { - int32_t length = newLightWeightMap->GetSize(); - int32_t len = lightWeightMap->GetSize(); + uint32_t length = newLightWeightMap->GetSize(); + uint32_t len = lightWeightMap->GetSize(); if (length > len) { return JSTaggedValue::False(); } @@ -118,11 +118,11 @@ JSTaggedValue JSAPILightWeightMap::HasAll(JSThread *thread, const JSHandleGet(num); hash = Hash(dealKey); - index = BinarySearchHashes(oldHashArray, hash, len); - if (index < 0 || index >= len) { + index = BinarySearchHashes(oldHashArray, hash, static_cast(len)); + if (index < 0 || index >= static_cast(len)) { return JSTaggedValue::False(); } HashParams params { oldHashArray, oldKeyArray, &dealKey }; @@ -147,8 +147,8 @@ JSTaggedValue JSAPILightWeightMap::HasValue(JSThread *thread, const JSHandle &value) { JSHandle valueArray = GetArrayByKind(thread, lightWeightMap, AccossorsKind::VALUE); - int32_t length = lightWeightMap->GetSize(); - for (int32_t num = 0; num < length; num++) { + uint32_t length = lightWeightMap->GetSize(); + for (uint32_t num = 0; num < length; num++) { if (JSTaggedValue::SameValue(valueArray->Get(num), value.GetTaggedValue())) { return JSTaggedValue::True(); } @@ -167,7 +167,7 @@ KeyState JSAPILightWeightMap::GetStateOfKey(JSThread *thread, const JSHandle &key) { int32_t hash = Hash(key.GetTaggedValue()); - int32_t length = lightWeightMap->GetSize(); + int32_t length = static_cast(lightWeightMap->GetSize()); JSHandle hashArray = GetArrayByKind(thread, lightWeightMap, AccossorsKind::HASH); int32_t index = BinarySearchHashes(hashArray, hash, length); if (index >= 0) { @@ -196,9 +196,9 @@ int32_t JSAPILightWeightMap::GetIndexOfValue(JSThread *thread, const JSHandle &value) { JSHandle valueArray = GetArrayByKind(thread, lightWeightMap, AccossorsKind::VALUE); - int32_t length = lightWeightMap->GetSize(); + uint32_t length = lightWeightMap->GetSize(); JSTaggedValue compValue = value.GetTaggedValue(); - for (int32_t i = 0; i < length; i++) { + for (uint32_t i = 0; i < length; i++) { if (valueArray->Get(i) == compValue) { return i; } @@ -209,7 +209,7 @@ int32_t JSAPILightWeightMap::GetIndexOfValue(JSThread *thread, const JSHandle &lightWeightMap, int32_t index) { - int32_t length = lightWeightMap->GetSize(); + int32_t length = static_cast(lightWeightMap->GetSize()); if (index < 0 || length <= index) { std::ostringstream oss; oss << "The value of \"index\" is out of range. It must be >= 0 && <= " << (length - 1) @@ -224,7 +224,7 @@ JSTaggedValue JSAPILightWeightMap::GetKeyAt(JSThread *thread, const JSHandle &lightWeightMap, int32_t index) { - int32_t length = lightWeightMap->GetSize(); + int32_t length = static_cast(lightWeightMap->GetSize()); if (index < 0 || length <= index) { std::ostringstream oss; oss << "The value of \"index\" is out of range. It must be >= 0 && <= " << (length - 1) @@ -243,8 +243,8 @@ void JSAPILightWeightMap::SetAll(JSThread *thread, const JSHandle needValueArray = GetArrayByKind(thread, needLightWeightMap, AccossorsKind::VALUE); JSMutableHandle key(thread, JSTaggedValue::Undefined()); JSMutableHandle value(thread, JSTaggedValue::Undefined()); - int32_t length = needLightWeightMap->GetSize(); - for (int32_t num = 0; num < length; num++) { + uint32_t length = needLightWeightMap->GetSize(); + for (uint32_t num = 0; num < length; num++) { key.Update(needKeyArray->Get(num)); value.Update(needValueArray->Get(num)); JSAPILightWeightMap::Set(thread, lightWeightMap, key, value); @@ -271,8 +271,8 @@ JSTaggedValue JSAPILightWeightMap::Remove(JSThread *thread, const JSHandle &lightWeightMap, int32_t index) { - int32_t length = lightWeightMap->GetSize(); - if (index < 0 || length <= index) { + uint32_t length = lightWeightMap->GetSize(); + if (index < 0 || static_cast(length) <= index) { return JSTaggedValue::False(); } RemoveValue(thread, lightWeightMap, index, AccossorsKind::HASH); @@ -306,7 +306,7 @@ void JSAPILightWeightMap::Clear(JSThread *thread, const JSHandle &lightWeightMap, int32_t index, const JSHandle &value) { - int32_t length = lightWeightMap->GetSize(); + int32_t length = static_cast(lightWeightMap->GetSize()); if (index < 0 || length <= index) { std::ostringstream oss; oss << "The value of \"index\" is out of range. It must be >= 0 && <= " << (length - 1) @@ -318,10 +318,10 @@ JSTaggedValue JSAPILightWeightMap::SetValueAt(JSThread *thread, const JSHandleGet(right).GetInt() == hash)) { + while ((right < static_cast(size)) && ((params.hashArray)->Get(right).GetInt() == hash)) { if (JSTaggedValue::SameValue((params.keyArray)->Get(right), *(params.key))) { return right; } diff --git a/ecmascript/js_api/js_api_lightweightmap.h b/ecmascript/js_api/js_api_lightweightmap.h index 09891c300968c47123a1a453dc5a5b1671f112d4..62feb9392eb20dec51157ddd83d9f426e79f4e9d 100644 --- a/ecmascript/js_api/js_api_lightweightmap.h +++ b/ecmascript/js_api/js_api_lightweightmap.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_JS_API_LIGHTWEIGHTMAP_H -#define ECMASCRIPT_JS_API_LIGHTWEIGHTMAP_H +#ifndef ECMASCRIPT_JS_API_JS_API_LIGHTWEIGHTMAP_H +#define ECMASCRIPT_JS_API_JS_API_LIGHTWEIGHTMAP_H #include "ecmascript/js_object.h" #include "ecmascript/js_tagged_value-inl.h" @@ -82,9 +82,9 @@ public: const JSHandle &key, PropertyDescriptor &desc); JSTaggedValue IsEmpty(); - inline int32_t GetSize() const + inline uint32_t GetSize() const { - return static_cast(GetLength()); + return GetLength(); } static constexpr size_t LWP_HASHES_OFFSET = JSObject::SIZE; @@ -118,8 +118,8 @@ private: static JSHandle GetArrayByKind(const JSThread *thread, const JSHandle &lightWeightMap, AccossorsKind kind); - static int32_t AvoidHashCollision(HashParams ¶ms, int32_t index, int32_t size, int32_t hash); + static int32_t AvoidHashCollision(HashParams ¶ms, int32_t index, uint32_t size, int32_t hash); }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_JS_API_LIGHTWEIGHTMAP_H +#endif // ECMASCRIPT_JS_API_JS_API_LIGHTWEIGHTMAP_H diff --git a/ecmascript/js_api/js_api_lightweightmap_iterator.cpp b/ecmascript/js_api/js_api_lightweightmap_iterator.cpp index 14e731d3af9ee1fa73d751a17b09e675dd9d3dcf..16fbca6962d0b22a7d8672c0b3b69dc4e905d886 100644 --- a/ecmascript/js_api/js_api_lightweightmap_iterator.cpp +++ b/ecmascript/js_api/js_api_lightweightmap_iterator.cpp @@ -48,7 +48,7 @@ JSTaggedValue JSAPILightWeightMapIterator::Next(EcmaRuntimeCallInfo *argv) return globalConst->GetUndefinedIterResult(); } int32_t index = iter->GetNextIndex(); - int32_t length = lightWeightMap->GetSize(); + int32_t length = static_cast(lightWeightMap->GetSize()); if (index >= length) { JSHandle undefinedHandle = globalConst->GetHandledUndefined(); iter->SetIteratedLightWeightMap(thread, undefinedHandle); diff --git a/ecmascript/js_api/js_api_lightweightmap_iterator.h b/ecmascript/js_api/js_api_lightweightmap_iterator.h index 40a3ea1ca3d35499dfa613ade0c4a20b8ed37bcf..c8aa2868d5f3c770330454476a5ecd4a01841675 100644 --- a/ecmascript/js_api/js_api_lightweightmap_iterator.h +++ b/ecmascript/js_api/js_api_lightweightmap_iterator.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_JS_API_LIGHT_WEIGHT_MAP_ITERATOR_H -#define ECMASCRIPT_JS_API_LIGHT_WEIGHT_MAP_ITERATOR_H +#ifndef ECMASCRIPT_JS_API_JS_API_LIGHT_WEIGHT_MAP_ITERATOR_H +#define ECMASCRIPT_JS_API_JS_API_LIGHT_WEIGHT_MAP_ITERATOR_H #include "ecmascript/js_iterator.h" #include "ecmascript/js_object.h" @@ -48,4 +48,4 @@ public: }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_JS_LinkedList_ITERATOR_H \ No newline at end of file +#endif // ECMASCRIPT_JS_API_JS_API_LIGHT_WEIGHT_MAP_ITERATOR_H \ No newline at end of file diff --git a/ecmascript/js_api/js_api_lightweightset.h b/ecmascript/js_api/js_api_lightweightset.h index 4f28ed2b319cbb1103582a70565e38bb4eab03b8..7118699aaa732343e4375ae602e546ddea72beea 100644 --- a/ecmascript/js_api/js_api_lightweightset.h +++ b/ecmascript/js_api/js_api_lightweightset.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_JS_API_LIGHTWEIGHTSET_H -#define ECMASCRIPT_JS_API_LIGHTWEIGHTSET_H +#ifndef ECMASCRIPT_JS_API_JS_API_LIGHTWEIGHTSET_H +#define ECMASCRIPT_JS_API_JS_API_LIGHTWEIGHTSET_H #include "ecmascript/js_object.h" #include "ecmascript/js_tagged_value-inl.h" @@ -77,4 +77,4 @@ public: DECL_DUMP() }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_JS_API_LIGHTWEIGHTSET_H \ No newline at end of file +#endif // ECMASCRIPT_JS_API_JS_API_LIGHTWEIGHTSET_H \ No newline at end of file diff --git a/ecmascript/js_api/js_api_lightweightset_iterator.h b/ecmascript/js_api/js_api_lightweightset_iterator.h index 4f867cf9df18067e17186f56b177a23e4271a9cf..8eb6f56d33317ee5445284a271ea08085f5a8601 100644 --- a/ecmascript/js_api/js_api_lightweightset_iterator.h +++ b/ecmascript/js_api/js_api_lightweightset_iterator.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_JS_API_LIGHTWEIGHTSET_ITERATOR_H -#define ECMASCRIPT_JS_API_LIGHTWEIGHTSET_ITERATOR_H +#ifndef ECMASCRIPT_JS_API_JS_API_LIGHTWEIGHTSET_ITERATOR_H +#define ECMASCRIPT_JS_API_JS_API_LIGHTWEIGHTSET_ITERATOR_H #include "ecmascript/js_iterator.h" #include "ecmascript/js_object.h" @@ -43,4 +43,4 @@ public: DECL_DUMP() }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_JS_API_LIGHT_WEIGHT_SET_ITERATOR_H \ No newline at end of file +#endif // ECMASCRIPT_JS_API_JS_API_LIGHTWEIGHTSET_ITERATOR_H \ No newline at end of file diff --git a/ecmascript/js_api/js_api_linked_list.cpp b/ecmascript/js_api/js_api_linked_list.cpp index 742a7c5720861c12a2a3a496e3248081d3d68fb8..b0f80707401e18819f047bb6349295dc2ec40301 100644 --- a/ecmascript/js_api/js_api_linked_list.cpp +++ b/ecmascript/js_api/js_api_linked_list.cpp @@ -55,9 +55,9 @@ JSHandle JSAPILinkedList::Clone(JSThread *thread, const JSHandl JSHandle srcDoubleList(thread, doubleListTaggedValue); JSHandle srcTaggedArray(thread, doubleListTaggedValue); ASSERT(!srcDoubleList->IsDictionaryMode()); - int numberOfNodes = srcDoubleList->NumberOfNodes(); - int numberOfDeletedNodes = srcDoubleList->NumberOfDeletedNodes(); - int effectiveCapacity = TaggedDoubleList::ELEMENTS_START_INDEX + + uint32_t numberOfNodes = static_cast(srcDoubleList->NumberOfNodes()); + uint32_t numberOfDeletedNodes = static_cast(srcDoubleList->NumberOfDeletedNodes()); + uint32_t effectiveCapacity = TaggedDoubleList::ELEMENTS_START_INDEX + (numberOfNodes + numberOfDeletedNodes + 1) * TaggedDoubleList::ENTRY_SIZE; ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle newLinkedList = factory->NewJSAPILinkedList(); @@ -234,6 +234,7 @@ bool JSAPILinkedList::GetOwnProperty(JSThread *thread, const JSHandle result = JSTaggedValue::ToString(thread, key.GetTaggedValue()); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); CString errorMsg = "The type of \"index\" can not obtain attributes of no-number type. Received value is: " + ConvertToString(*result); diff --git a/ecmascript/js_api/js_api_linked_list.h b/ecmascript/js_api/js_api_linked_list.h index bcbd860cac46e4d27898bc7014816f0eb73367a4..7202c9add8e75ce48dd837bfeb54f7116104003c 100644 --- a/ecmascript/js_api/js_api_linked_list.h +++ b/ecmascript/js_api/js_api_linked_list.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_JS_API_LINKEDLIST_H -#define ECMASCRIPT_JS_API_LINKEDLIST_H +#ifndef ECMASCRIPT_JS_API_JS_API_LINKEDLIST_H +#define ECMASCRIPT_JS_API_JS_API_LINKEDLIST_H #include "ecmascript/js_object.h" #include "ecmascript/js_tagged_value-inl.h" @@ -23,7 +23,7 @@ namespace panda::ecmascript { class JSAPILinkedList : public JSObject { public: - static constexpr int DEFAULT_CAPACITY_LENGTH = 10; + static constexpr uint32_t DEFAULT_CAPACITY_LENGTH = 10; static JSAPILinkedList *Cast(TaggedObject *object) { ASSERT(JSTaggedValue(object).IsJSAPILinkedList()); @@ -61,7 +61,7 @@ public: JSTaggedValue Remove(JSThread *thread, const JSTaggedValue &element); JSTaggedValue GetIndexOf(const JSTaggedValue &element); JSTaggedValue GetLastIndexOf(const JSTaggedValue &element); - inline int Length() + inline uint32_t Length() { return TaggedDoubleList::Cast(GetDoubleList().GetTaggedObject())->Length(); } @@ -72,4 +72,4 @@ public: }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_JS_API_LinkedLIST_H +#endif // ECMASCRIPT_JS_API_JS_API_LinkedLIST_H diff --git a/ecmascript/js_api/js_api_linked_list_iterator.h b/ecmascript/js_api/js_api_linked_list_iterator.h index 6e4fe105f8d1588ffe991145c0f0cf1b3bdd33fd..0c282a81b20d9e40e5af80ccff7453929e3437f7 100644 --- a/ecmascript/js_api/js_api_linked_list_iterator.h +++ b/ecmascript/js_api/js_api_linked_list_iterator.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_JS_API_LINKED_LIST_ITERATOR_H -#define ECMASCRIPT_JS_API_LINKED_LIST_ITERATOR_H +#ifndef ECMASCRIPT_JS_API_JS_API_LINKED_LIST_ITERATOR_H +#define ECMASCRIPT_JS_API_JS_API_LINKED_LIST_ITERATOR_H #include "ecmascript/js_iterator.h" #include "ecmascript/js_object.h" @@ -41,4 +41,4 @@ public: }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_JS_LinkedList_ITERATOR_H \ No newline at end of file +#endif // ECMASCRIPT_JS_API_JS_API_LINKED_LIST_ITERATOR_H \ No newline at end of file diff --git a/ecmascript/js_api/js_api_list.cpp b/ecmascript/js_api/js_api_list.cpp index ea5475e9b2c3258690c388e72c211dacd09d3e85..6e7dfab0bf352442ab46224de0661c148b6fb3ca 100644 --- a/ecmascript/js_api/js_api_list.cpp +++ b/ecmascript/js_api/js_api_list.cpp @@ -198,7 +198,7 @@ JSTaggedValue JSAPIList::GetSubList(JSThread *thread, const JSHandle JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); } - int len = TaggedSingleList::ELEMENTS_START_INDEX + (toIndex - fromIndex + 1) * TaggedSingleList::ENTRY_SIZE; + uint32_t len = TaggedSingleList::ELEMENTS_START_INDEX + (toIndex - fromIndex + 1) * TaggedSingleList::ENTRY_SIZE; JSHandle newElement = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(len); JSHandle subSingleList = JSHandle::Cast(newElement); JSHandle sublist = thread->GetEcmaVM()->GetFactory()->NewJSAPIList(); @@ -218,6 +218,7 @@ bool JSAPIList::GetOwnProperty(JSThread *thread, const JSHandle &list uint32_t index = 0; if (UNLIKELY(!JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) { JSHandle result = JSTaggedValue::ToString(thread, key.GetTaggedValue()); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); CString errorMsg = "The type of \"index\" can not obtain attributes of no-number type. Received value is: " + ConvertToString(*result); diff --git a/ecmascript/js_api/js_api_list.h b/ecmascript/js_api/js_api_list.h index f5aa2027f74bb6ea0a1ded543b751207f2cf15f1..b1e163e6af22e08154ee5292dc9d0a68c99dad03 100644 --- a/ecmascript/js_api/js_api_list.h +++ b/ecmascript/js_api/js_api_list.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_JS_API_LIST_H -#define ECMASCRIPT_JS_API_LIST_H +#ifndef ECMASCRIPT_JS_API_JS_API_LIST_H +#define ECMASCRIPT_JS_API_JS_API_LIST_H #include "ecmascript/js_object.h" #include "ecmascript/js_tagged_value-inl.h" @@ -23,7 +23,7 @@ namespace panda::ecmascript { class JSAPIList : public JSObject { public: - static constexpr int DEFAULT_CAPACITY_LENGTH = 10; + static constexpr uint32_t DEFAULT_CAPACITY_LENGTH = 10; static JSAPIList *Cast(TaggedObject *object) { ASSERT(JSTaggedValue(object).IsJSAPIList()); @@ -63,7 +63,7 @@ public: JSTaggedValue Equal(JSThread *thread, const JSHandle &list); void Clear(JSThread *thread); JSTaggedValue Remove(JSThread *thread, const JSTaggedValue &element); - inline int Length() + inline uint32_t Length() { return TaggedSingleList::Cast(GetSingleList().GetTaggedObject())->Length(); } @@ -75,4 +75,4 @@ public: }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_JS_API_LIST_H +#endif // ECMASCRIPT_JS_API_JS_API_LIST_H diff --git a/ecmascript/js_api/js_api_list_iterator.h b/ecmascript/js_api/js_api_list_iterator.h index 2bc296b2f8d08d4a4b660aa682427e29e1a1ebcc..bbe61d700466d217a2df09dac48f5d873d03a195 100644 --- a/ecmascript/js_api/js_api_list_iterator.h +++ b/ecmascript/js_api/js_api_list_iterator.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_JS_API_LIST_ITERATOR_H -#define ECMASCRIPT_JS_API_LIST_ITERATOR_H +#ifndef ECMASCRIPT_JS_API_JS_API_LIST_ITERATOR_H +#define ECMASCRIPT_JS_API_JS_API_LIST_ITERATOR_H #include "ecmascript/js_iterator.h" #include "ecmascript/js_object.h" @@ -41,4 +41,4 @@ public: }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_JS_List_ITERATOR_H \ No newline at end of file +#endif // ECMASCRIPT_JS_API_JS_API_LIST_ITERATOR_H \ No newline at end of file diff --git a/ecmascript/js_api/js_api_plain_array.cpp b/ecmascript/js_api/js_api_plain_array.cpp index d8c304c361c7a3d2839d18c07fb899222aa81cf9..d26328010dbee70205aeb317292302a661b3720a 100644 --- a/ecmascript/js_api/js_api_plain_array.cpp +++ b/ecmascript/js_api/js_api_plain_array.cpp @@ -30,7 +30,7 @@ void JSAPIPlainArray::Add(JSThread *thread, const JSHandle &obj { JSHandle keyArray(thread, obj->GetKeys()); JSHandle valueArray(thread, obj->GetValues()); - int32_t size = obj->GetLength(); + uint32_t size = obj->GetLength(); int32_t index = obj->BinarySearch(*keyArray, 0, size, key.GetTaggedValue().GetNumber()); if (index >= 0) { keyArray->Set(thread, index, key); @@ -38,12 +38,12 @@ void JSAPIPlainArray::Add(JSThread *thread, const JSHandle &obj return; } index ^= 0xFFFFFFFF; - if (index < size) { + if (index < static_cast(size)) { obj->AdjustArray(thread, *keyArray, index, size, true); obj->AdjustArray(thread, *valueArray, index, size, true); } uint32_t capacity = valueArray->GetLength(); - if (size + 1 >= static_cast(capacity)) { + if (size + 1 >= capacity) { uint32_t newCapacity = capacity << 1U; keyArray = thread->GetEcmaVM()->GetFactory()->CopyArray(keyArray, capacity, newCapacity); @@ -68,12 +68,12 @@ JSHandle JSAPIPlainArray::CreateSlot(const JSThread *thread, const bool JSAPIPlainArray::AdjustForward(JSThread *thread, int32_t index, int32_t forwardSize) { - int32_t size = GetLength(); + uint32_t size = GetLength(); TaggedArray *keys = TaggedArray::Cast(GetKeys().GetTaggedObject()); TaggedArray *values = TaggedArray::Cast(GetValues().GetTaggedObject()); AdjustArray(thread, keys, index + forwardSize, index, false); AdjustArray(thread, values, index + forwardSize, index, false); - size = size - forwardSize; + size = size - static_cast(forwardSize); SetLength(size); return true; } @@ -81,8 +81,8 @@ bool JSAPIPlainArray::AdjustForward(JSThread *thread, int32_t index, int32_t for void JSAPIPlainArray::AdjustArray(JSThread *thread, TaggedArray *srcArray, int32_t fromIndex, int32_t toIndex, bool direction) { - int32_t size = GetLength(); - int32_t idx = size - 1; + uint32_t size = GetLength(); + uint32_t idx = size - 1; if (direction) { while (fromIndex < toIndex) { JSTaggedValue value = srcArray->Get(idx); @@ -91,9 +91,9 @@ void JSAPIPlainArray::AdjustArray(JSThread *thread, TaggedArray *srcArray, int32 fromIndex++; } } else { - int32_t moveSize = size - fromIndex; - for (int32_t i = 0; i < moveSize; i++) { - if ((fromIndex + i) < size) { + uint32_t moveSize = size - static_cast(fromIndex); + for (uint32_t i = 0; i < moveSize; i++) { + if ((fromIndex + static_cast(i)) < static_cast(size)) { JSTaggedValue value = srcArray->Get(fromIndex + i); srcArray->Set(thread, toIndex + i, value); } else { @@ -126,8 +126,8 @@ void JSAPIPlainArray::Clear(JSThread *thread) { TaggedArray *keys = TaggedArray::Cast(GetKeys().GetTaggedObject()); TaggedArray *values = TaggedArray::Cast(GetValues().GetTaggedObject()); - int32_t size = GetLength(); - for (int32_t index = 0; index < size; index++) { + uint32_t size = GetLength(); + for (uint32_t index = 0; index < size; index++) { keys->Set(thread, index, JSTaggedValue::Hole()); values->Set(thread, index, JSTaggedValue::Hole()); } @@ -136,7 +136,7 @@ void JSAPIPlainArray::Clear(JSThread *thread) JSTaggedValue JSAPIPlainArray::RemoveRangeFrom(JSThread *thread, int32_t index, int32_t batchSize) { - int32_t size = GetLength(); + int32_t size = static_cast(GetLength()); if (index < 0 || index >= size) { std::ostringstream oss; oss << "The value of \"index\" is out of range. It must be >= 0 && <= " << (size - 1) @@ -168,9 +168,9 @@ bool JSAPIPlainArray::GetOwnProperty(JSThread *thread, const JSHandle &key) { TaggedArray *keyArray = TaggedArray::Cast(obj->GetKeys().GetTaggedObject()); - int32_t size = obj->GetLength(); + uint32_t size = obj->GetLength(); int32_t index = obj->BinarySearch(keyArray, 0, size, key.GetTaggedValue().GetInt()); - if (index < 0 || index >= size) { + if (index < 0 || index >= static_cast(size)) { std::ostringstream oss; oss << "The value of \"index\" is out of range. It must be >= 0 && <= " << (size - 1) << ". Received value is: " << index; @@ -186,9 +186,9 @@ OperationResult JSAPIPlainArray::GetProperty(JSThread *thread, const JSHandle &key) { TaggedArray *keyArray = TaggedArray::Cast(obj->GetKeys().GetTaggedObject()); - int32_t size = obj->GetLength(); + uint32_t size = obj->GetLength(); int32_t index = obj->BinarySearch(keyArray, 0, size, key.GetTaggedValue().GetInt()); - if (index < 0 || index >= size) { + if (index < 0 || index >= static_cast(size)) { std::ostringstream oss; oss << "The value of \"index\" is out of range. It must be >= 0 && <= " << (size - 1) << ". Received value is: " << index; @@ -206,9 +206,9 @@ bool JSAPIPlainArray::SetProperty(JSThread *thread, const JSHandle &value) { TaggedArray *keyArray = TaggedArray::Cast(obj->GetKeys().GetTaggedObject()); - int32_t size = obj->GetLength(); + uint32_t size = obj->GetLength(); int32_t index = obj->BinarySearch(keyArray, 0, size, key.GetTaggedValue().GetInt()); - if (index < 0 || index >= size) { + if (index < 0 || index >= static_cast(size)) { return false; } @@ -223,7 +223,7 @@ JSHandle JSAPIPlainArray::Clone(JSThread *thread, const JSHandl auto factory = thread->GetEcmaVM()->GetFactory(); JSHandle newPlainArray = factory->NewJSAPIPlainArray(0); - int32_t length = obj->GetLength(); + uint32_t length = obj->GetLength(); newPlainArray->SetLength(length); JSHandle srcKeyArray(thread, obj->GetKeys()); JSHandle srcValueArray(thread, obj->GetValues()); @@ -238,7 +238,7 @@ JSHandle JSAPIPlainArray::Clone(JSThread *thread, const JSHandl bool JSAPIPlainArray::Has(const int32_t key) { - int32_t size = GetLength(); + uint32_t size = GetLength(); TaggedArray *keyArray = TaggedArray::Cast(GetKeys().GetTaggedObject()); int32_t index = BinarySearch(keyArray, 0, size, key); if (index < 0) { @@ -249,7 +249,7 @@ bool JSAPIPlainArray::Has(const int32_t key) JSTaggedValue JSAPIPlainArray::Get(const JSTaggedValue key) { - int32_t size = GetLength(); + uint32_t size = GetLength(); TaggedArray *keyArray = TaggedArray::Cast(GetKeys().GetTaggedObject()); int32_t index = BinarySearch(keyArray, 0, size, key.GetNumber()); if (index < 0) { @@ -273,11 +273,11 @@ JSTaggedValue JSAPIPlainArray::ForEach(JSThread *thread, const JSHandle &thisArg) { JSAPIPlainArray *plainarray = JSAPIPlainArray::Cast(thisHandle->GetTaggedObject()); - int32_t length = plainarray->GetLength(); + uint32_t length = plainarray->GetLength(); JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); JSHandle keyArray(thread, plainarray->GetKeys()); JSHandle valueArray(thread, plainarray->GetValues()); - for (int32_t k = 0; k < length; k++) { + for (uint32_t k = 0; k < length; k++) { JSTaggedValue kValue = valueArray->Get(k); JSTaggedValue key = keyArray->Get(k); EcmaRuntimeCallInfo *info = @@ -296,12 +296,12 @@ JSTaggedValue JSAPIPlainArray::ToString(JSThread *thread, const JSHandle, char16_t> {}.from_bytes(","); std::u16string colonStr = std::wstring_convert, char16_t> {}.from_bytes(":"); - int32_t length = plainarray->GetLength(); + uint32_t length = plainarray->GetLength(); std::u16string concatStr = std::wstring_convert, char16_t> {}.from_bytes(""); std::u16string concatStrNew = std::wstring_convert, char16_t> {}.from_bytes(""); JSMutableHandle valueHandle(thread, JSTaggedValue::Undefined()); JSMutableHandle keyHandle(thread, JSTaggedValue::Undefined()); - for (int32_t k = 0; k < length; k++) { + for (uint32_t k = 0; k < length; k++) { std::u16string valueStr; valueHandle.Update(plainarray->GetValueAt(thread, k)); if (!valueHandle->IsUndefined() && !valueHandle->IsNull()) { @@ -336,7 +336,7 @@ JSTaggedValue JSAPIPlainArray::ToString(JSThread *thread, const JSHandle(GetLength()); int32_t index = -1; for (int32_t i = 0; i < size; i++) { if (JSTaggedValue::SameValue(values->Get(i), value)) { @@ -364,15 +364,15 @@ JSTaggedValue JSAPIPlainArray::GetIndexOfValue(JSTaggedValue value) bool JSAPIPlainArray::IsEmpty() { - int32_t length = GetLength(); + uint32_t length = GetLength(); return length == 0; } JSTaggedValue JSAPIPlainArray::GetKeyAt(int32_t index) { - int32_t size = GetLength(); + uint32_t size = GetLength(); TaggedArray *keyArray = TaggedArray::Cast(GetKeys().GetTaggedObject()); - if (index < 0 || index >= size) { + if (index < 0 || index >= static_cast(size)) { return JSTaggedValue::Undefined(); } return keyArray->Get(index); @@ -380,8 +380,8 @@ JSTaggedValue JSAPIPlainArray::GetKeyAt(int32_t index) JSTaggedValue JSAPIPlainArray::GetValueAt(JSThread *thread, int32_t index) { - int32_t size = GetLength(); - if (index < 0 || index >= size) { + uint32_t size = GetLength(); + if (index < 0 || index >= static_cast(size)) { std::ostringstream oss; oss << "The value of \"index\" is out of range. It must be >= 0 && <= " << (size - 1) << ". Received value is: " << index; @@ -394,10 +394,10 @@ JSTaggedValue JSAPIPlainArray::GetValueAt(JSThread *thread, int32_t index) JSTaggedValue JSAPIPlainArray::Remove(JSThread *thread, JSTaggedValue key) { - int32_t size = GetLength(); + uint32_t size = GetLength(); TaggedArray *keyArray = TaggedArray::Cast(GetKeys().GetTaggedObject()); int32_t index = BinarySearch(keyArray, 0, size, key.GetNumber()); - if (index < 0 || index >= size) { + if (index < 0 || index >= static_cast(size)) { return JSTaggedValue::Undefined(); } TaggedArray *values = TaggedArray::Cast(GetValues().GetTaggedObject()); @@ -408,9 +408,9 @@ JSTaggedValue JSAPIPlainArray::Remove(JSThread *thread, JSTaggedValue key) JSTaggedValue JSAPIPlainArray::RemoveAt(JSThread *thread, JSTaggedValue index) { - int32_t size = GetLength(); + uint32_t size = GetLength(); int32_t seat = index.GetNumber(); - if (seat < 0 || seat >= size) { + if (seat < 0 || static_cast(seat) >= size) { return JSTaggedValue::Undefined(); } TaggedArray *values = TaggedArray::Cast(GetValues().GetTaggedObject()); @@ -421,9 +421,9 @@ JSTaggedValue JSAPIPlainArray::RemoveAt(JSThread *thread, JSTaggedValue index) bool JSAPIPlainArray::SetValueAt(JSThread *thread, JSTaggedValue index, JSTaggedValue value) { - int32_t size = GetLength(); + uint32_t size = GetLength(); int32_t seat = index.GetNumber(); - if (seat < 0 || seat >= size) { + if (seat < 0 || static_cast(seat) >= size) { std::ostringstream oss; oss << "The value of \"index\" is out of range. It must be >= 0 && <= " << (size - 1) << ". Received value is: " << seat; diff --git a/ecmascript/js_api/js_api_plain_array.h b/ecmascript/js_api/js_api_plain_array.h index 4315ad8afae6a361b5157d60ca81b595466fd77b..027a9df0875e900b08a2bfac7a1a4ce6d67f1788 100644 --- a/ecmascript/js_api/js_api_plain_array.h +++ b/ecmascript/js_api/js_api_plain_array.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_JS_API_PLAIN_ARRAY_H -#define ECMASCRIPT_JS_API_PLAIN_ARRAY_H +#ifndef ECMASCRIPT_JS_API_JS_API_PLAIN_ARRAY_H +#define ECMASCRIPT_JS_API_JS_API_PLAIN_ARRAY_H #include "ecmascript/js_object.h" #include "ecmascript/js_tagged_value-inl.h" @@ -62,14 +62,14 @@ public: void Clear(JSThread *thread); void AdjustArray(JSThread *thread, TaggedArray *srcArray, int32_t fromIndex, int32_t toIndex, bool direction); - inline int GetSize() const + inline uint32_t GetSize() const { return GetLength(); } static constexpr size_t KEYS_OFFSET = JSObject::SIZE; ACCESSORS(Keys, KEYS_OFFSET, VALUES_OFFSET); ACCESSORS(Values, VALUES_OFFSET, LENGTH_OFFSET); - ACCESSORS_PRIMITIVE_FIELD(Length, int32_t, LENGTH_OFFSET, LAST_OFFSET); + ACCESSORS_PRIMITIVE_FIELD(Length, uint32_t, LENGTH_OFFSET, LAST_OFFSET); DEFINE_ALIGN_SIZE(LAST_OFFSET); DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, KEYS_OFFSET, LENGTH_OFFSET) @@ -82,4 +82,4 @@ private: } }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_JS_API_PLAIN_ARRAY_H +#endif // ECMASCRIPT_JS_API_JS_API_PLAIN_ARRAY_H diff --git a/ecmascript/js_api/js_api_plain_array_iterator.cpp b/ecmascript/js_api/js_api_plain_array_iterator.cpp index 9c9ae9fc29f70e926ba18baf31c196bb7fa59386..66636e3fc3868806475b84024ff9146d1399f5dc 100644 --- a/ecmascript/js_api/js_api_plain_array_iterator.cpp +++ b/ecmascript/js_api/js_api_plain_array_iterator.cpp @@ -49,9 +49,9 @@ JSTaggedValue JSAPIPlainArrayIterator::Next(EcmaRuntimeCallInfo *argv) JSHandle apiPlainArray(plainArray); ASSERT(plainArray->IsJSAPIPlainArray()); - int32_t length = apiPlainArray->GetLength(); + uint32_t length = apiPlainArray->GetLength(); int32_t index = iter->GetNextIndex(); - if (index >= length) { + if (static_cast(index) >= length) { iter->SetIteratedPlainArray(thread, undefinedHandle); return JSIterator::CreateIterResultObject(thread, undefinedHandle, true).GetTaggedValue(); } diff --git a/ecmascript/js_api/js_api_plain_array_iterator.h b/ecmascript/js_api/js_api_plain_array_iterator.h index 1df2ebe44d68df8356b81b42818da4adf950c313..db8cdba574b841fda24cafdc8876480dcc769496 100644 --- a/ecmascript/js_api/js_api_plain_array_iterator.h +++ b/ecmascript/js_api/js_api_plain_array_iterator.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_JS_API_PLAIN_ARRAY_ITERATOR_H -#define ECMASCRIPT_JS_API_PLAIN_ARRAY_ITERATOR_H +#ifndef ECMASCRIPT_JS_API_JS_API_PLAIN_ARRAY_ITERATOR_H +#define ECMASCRIPT_JS_API_JS_API_PLAIN_ARRAY_ITERATOR_H #include "ecmascript/js_iterator.h" #include "ecmascript/js_object.h" @@ -42,4 +42,4 @@ public: DECL_DUMP() }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_JS_API_PLAIN_ARRAY_ITERATOR_H +#endif // ECMASCRIPT_JS_API_JS_API_PLAIN_ARRAY_ITERATOR_H diff --git a/ecmascript/js_api/js_api_queue.cpp b/ecmascript/js_api/js_api_queue.cpp index f0fc7d4c391c80cfd29862e22a998e294228c52e..67da40b2e2a0d9634dc8ff56085e3172cf043abb 100644 --- a/ecmascript/js_api/js_api_queue.cpp +++ b/ecmascript/js_api/js_api_queue.cpp @@ -197,6 +197,7 @@ bool JSAPIQueue::GetOwnProperty(JSThread *thread, const JSHandle &ob uint32_t index = 0; if (UNLIKELY(!JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) { JSHandle result = JSTaggedValue::ToString(thread, key.GetTaggedValue()); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); CString errorMsg = "The type of \"index\" can not obtain attributes of no-number type. Received value is: " + ConvertToString(*result); diff --git a/ecmascript/js_api/js_api_queue.h b/ecmascript/js_api/js_api_queue.h index 5c5032105260d6ce8a4ea98243d0ea1a9c0d0ab4..63d07847ca7ad9d9ab10c0f0dda716263dc499ad 100644 --- a/ecmascript/js_api/js_api_queue.h +++ b/ecmascript/js_api/js_api_queue.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_JS_API_QUEUE_H -#define ECMASCRIPT_JS_API_QUEUE_H +#ifndef ECMASCRIPT_JS_API_JS_API_QUEUE_H +#define ECMASCRIPT_JS_API_JS_API_QUEUE_H #include "ecmascript/js_object.h" #include "ecmascript/js_tagged_value-inl.h" @@ -22,7 +22,7 @@ namespace panda::ecmascript { class JSAPIQueue : public JSObject { public: - static constexpr int DEFAULT_CAPACITY_LENGTH = 8; + static constexpr uint32_t DEFAULT_CAPACITY_LENGTH = 8; static JSAPIQueue *Cast(TaggedObject *object) { ASSERT(JSTaggedValue(object).IsJSAPIQueue()); @@ -86,4 +86,4 @@ private: }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_JS_API_QUEUE_H +#endif // ECMASCRIPT_JS_API_JS_API_QUEUE_H diff --git a/ecmascript/js_api/js_api_queue_iterator.h b/ecmascript/js_api/js_api_queue_iterator.h index 3ef96cc8b7c86358294d46ee46f1c6080feeae0e..77d3a5cb06e659bbd616c04a152ffe10639082c2 100644 --- a/ecmascript/js_api/js_api_queue_iterator.h +++ b/ecmascript/js_api/js_api_queue_iterator.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_JS_API_QUEUE_ITERATOR_H -#define ECMASCRIPT_JS_API_QUEUE_ITERATOR_H +#ifndef ECMASCRIPT_JS_API_JS_API_QUEUE_ITERATOR_H +#define ECMASCRIPT_JS_API_JS_API_QUEUE_ITERATOR_H #include "ecmascript/js_iterator.h" #include "ecmascript/js_object.h" @@ -40,4 +40,4 @@ public: }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_JS_API_QUEUE_ITERATOR_H +#endif // ECMASCRIPT_JS_API_JS_API_QUEUE_ITERATOR_H diff --git a/ecmascript/js_api/js_api_stack.cpp b/ecmascript/js_api/js_api_stack.cpp index 565982932443637cd5aa7dc5626c912079db3ca7..fea7b157c4757bb05b5d33a66c3b21346521cc01 100644 --- a/ecmascript/js_api/js_api_stack.cpp +++ b/ecmascript/js_api/js_api_stack.cpp @@ -17,7 +17,7 @@ #include "ecmascript/containers/containers_errors.h" #include "ecmascript/js_tagged_value.h" -#include "ecmascript/object_factory.h" +#include "ecmascript/object_factory-inl.h" namespace panda::ecmascript { using ContainerError = containers::ContainerError; @@ -142,6 +142,7 @@ bool JSAPIStack::GetOwnProperty(JSThread *thread, const JSHandle &ob uint32_t index = 0; if (UNLIKELY(!JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) { JSHandle result = JSTaggedValue::ToString(thread, key.GetTaggedValue()); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); CString errorMsg = "The type of \"index\" can not obtain attributes of no-number type. Received value is: " + ConvertToString(*result); diff --git a/ecmascript/js_api/js_api_stack.h b/ecmascript/js_api/js_api_stack.h index c156cae45c25adc1f2fbea90d40a05917550d9af..c57de1c4b604cab5243578218521a7d0eafb5903 100644 --- a/ecmascript/js_api/js_api_stack.h +++ b/ecmascript/js_api/js_api_stack.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_JS_API_STACK_H -#define ECMASCRIPT_JS_API_STACK_H +#ifndef ECMASCRIPT_JS_API_JS_API_STACK_H +#define ECMASCRIPT_JS_API_JS_API_STACK_H #include "ecmascript/js_object.h" #include "ecmascript/js_tagged_value-inl.h" @@ -22,7 +22,7 @@ namespace panda::ecmascript { class JSAPIStack : public JSObject { public: - static constexpr int DEFAULT_CAPACITY_LENGTH = 10; + static constexpr uint32_t DEFAULT_CAPACITY_LENGTH = 10; static JSAPIStack *Cast(TaggedObject *object) { ASSERT(JSTaggedValue(object).IsJSAPIStack()); @@ -48,7 +48,7 @@ public: int Search(const JSHandle &value); - inline int GetSize() const + inline uint32_t GetSize() const { return GetTop(); } @@ -78,4 +78,4 @@ private: }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_JS_API_STACK_H +#endif // ECMASCRIPT_JS_API_JS_API_STACK_H diff --git a/ecmascript/js_api/js_api_stack_iterator.h b/ecmascript/js_api/js_api_stack_iterator.h index 884bf7a8d8fff1e2c535b5c5ddb8c98ad2c8ddd1..66964a0513824277dd8fa011fe521ae4456c23d3 100644 --- a/ecmascript/js_api/js_api_stack_iterator.h +++ b/ecmascript/js_api/js_api_stack_iterator.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_JS_API_STACK_ITERATOR_H -#define ECMASCRIPT_JS_API_STACK_ITERATOR_H +#ifndef ECMASCRIPT_JS_API_JS_API_STACK_ITERATOR_H +#define ECMASCRIPT_JS_API_JS_API_STACK_ITERATOR_H #include "ecmascript/js_api/js_api_stack.h" #include "ecmascript/js_object.h" @@ -40,4 +40,4 @@ public: }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_JS_API_STACK_ITERATOR_H +#endif // ECMASCRIPT_JS_API_JS_API_STACK_ITERATOR_H diff --git a/ecmascript/js_api/js_api_tree_map.cpp b/ecmascript/js_api/js_api_tree_map.cpp index 54234b5f32314ccf7c13c998072f041d347b083f..e0eccb6e19ccb991f6a085fa446f8315ac686414 100644 --- a/ecmascript/js_api/js_api_tree_map.cpp +++ b/ecmascript/js_api/js_api_tree_map.cpp @@ -27,6 +27,7 @@ void JSAPITreeMap::Set(JSThread *thread, const JSHandle &map, cons { if (!TaggedTreeMap::IsKey(key.GetTaggedValue())) { JSHandle result = JSTaggedValue::ToString(thread, key.GetTaggedValue()); + RETURN_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"key\" must be not null. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); diff --git a/ecmascript/js_api/js_api_tree_map.h b/ecmascript/js_api/js_api_tree_map.h index 2ef3a26bac98ab3ba82b9c524a8e31b3110cfe54..40e91b8c38233941da4f7475f937b20229fb4b93 100644 --- a/ecmascript/js_api/js_api_tree_map.h +++ b/ecmascript/js_api/js_api_tree_map.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_JS_API_TREE_MAP_H -#define ECMASCRIPT_JS_API_TREE_MAP_H +#ifndef ECMASCRIPT_JS_API_JS_API_TREE_MAP_H +#define ECMASCRIPT_JS_API_JS_API_TREE_MAP_H #include "ecmascript/js_object.h" #include "ecmascript/js_tagged_value-inl.h" @@ -62,4 +62,4 @@ public: }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_JS_API_TREE_MAP_H +#endif // ECMASCRIPT_JS_API_JS_API_TREE_MAP_H diff --git a/ecmascript/js_api/js_api_tree_map_iterator.h b/ecmascript/js_api/js_api_tree_map_iterator.h index b6f3190f92a81cfdb93e54961182c82b8b7f60ad..83ea3880528cfdb152f82f5ee2cf884232d8e1dd 100644 --- a/ecmascript/js_api/js_api_tree_map_iterator.h +++ b/ecmascript/js_api/js_api_tree_map_iterator.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_JS_API_TREE_MAP_ITERATOR_H -#define ECMASCRIPT_JS_API_TREE_MAP_ITERATOR_H +#ifndef ECMASCRIPT_JS_API_JS_API_TREE_MAP_ITERATOR_H +#define ECMASCRIPT_JS_API_JS_API_TREE_MAP_ITERATOR_H #include "ecmascript/js_iterator.h" #include "ecmascript/js_object.h" @@ -53,4 +53,4 @@ public: }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_JS_API_TREE_MAP_ITERATOR_H +#endif // ECMASCRIPT_JS_API_JS_API_TREE_MAP_ITERATOR_H diff --git a/ecmascript/js_api/js_api_tree_set.cpp b/ecmascript/js_api/js_api_tree_set.cpp index b8b57be71ae6d615f97f0a1e64715b33c2a7f3d7..39d78fa5756c3d1e1e089f089160683d72a1a266 100644 --- a/ecmascript/js_api/js_api_tree_set.cpp +++ b/ecmascript/js_api/js_api_tree_set.cpp @@ -26,6 +26,7 @@ void JSAPITreeSet::Add(JSThread *thread, const JSHandle &set, cons { if (!TaggedTreeSet::IsKey(value.GetTaggedValue())) { JSHandle result = JSTaggedValue::ToString(thread, value.GetTaggedValue()); + RETURN_IF_ABRUPT_COMPLETION(thread); CString errorMsg = "The type of \"value\" must be Key of JS. Received value is: " + ConvertToString(*result); JSTaggedValue error = ContainerError::BusinessError(thread, ErrorFlag::TYPE_ERROR, errorMsg.c_str()); diff --git a/ecmascript/js_api/js_api_tree_set.h b/ecmascript/js_api/js_api_tree_set.h index 0d53d1c49039f9297a794cb2561cc87d82b09a8b..7f799562b494ddcebbb15445186e7b5566e35cc1 100644 --- a/ecmascript/js_api/js_api_tree_set.h +++ b/ecmascript/js_api/js_api_tree_set.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_JS_API_TREE_SET_H -#define ECMASCRIPT_JS_API_TREE_SET_H +#ifndef ECMASCRIPT_JS_API_JS_API_TREE_SET_H +#define ECMASCRIPT_JS_API_JS_API_TREE_SET_H #include "ecmascript/js_object.h" #include "ecmascript/js_tagged_value-inl.h" @@ -57,4 +57,4 @@ public: }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_JS_API_TREE_SET_H_ +#endif // ECMASCRIPT_JS_API_JS_API_TREE_SET_H_ diff --git a/ecmascript/js_api/js_api_tree_set_iterator.h b/ecmascript/js_api/js_api_tree_set_iterator.h index 67292b50c2f57f972de483b494477348ab212acf..adfb068a8169c0a586ccb10ca67740572974977c 100644 --- a/ecmascript/js_api/js_api_tree_set_iterator.h +++ b/ecmascript/js_api/js_api_tree_set_iterator.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_JS_API_TREE_SET_ITERATOR_H -#define ECMASCRIPT_JS_API_TREE_SET_ITERATOR_H +#ifndef ECMASCRIPT_JS_API_JS_API_TREE_SET_ITERATOR_H +#define ECMASCRIPT_JS_API_JS_API_TREE_SET_ITERATOR_H #include "ecmascript/js_iterator.h" #include "ecmascript/js_object.h" @@ -53,4 +53,4 @@ public: }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_JS_API_TREE_SET_ITERATOR_H +#endif // ECMASCRIPT_JS_API_JS_API_TREE_SET_ITERATOR_H diff --git a/ecmascript/js_api/js_api_vector.cpp b/ecmascript/js_api/js_api_vector.cpp index 956fc4025a29171dea7d2fb0ccc3a995fdda58cd..5ad59ef9dd12e689ad81e200627df5f2f4684678 100644 --- a/ecmascript/js_api/js_api_vector.cpp +++ b/ecmascript/js_api/js_api_vector.cpp @@ -15,6 +15,7 @@ #include "ecmascript/js_api/js_api_vector.h" +#include "ecmascript/global_env_constants-inl.h" #include "ecmascript/interpreter/interpreter.h" #include "ecmascript/js_array.h" #include "ecmascript/js_api/js_api_vector_iterator.h" @@ -29,7 +30,7 @@ static const uint32_t MAX_VALUE = 0x7fffffff; static const uint32_t MAX_ARRAY_SIZE = MAX_VALUE - 8; bool JSAPIVector::Add(JSThread *thread, const JSHandle &vector, const JSHandle &value) { - int32_t length = vector->GetSize(); + uint32_t length = vector->GetSize(); GrowCapacity(thread, vector, length + 1); TaggedArray *elements = TaggedArray::Cast(vector->GetElements().GetTaggedObject()); @@ -43,15 +44,15 @@ bool JSAPIVector::Add(JSThread *thread, const JSHandle &vector, con void JSAPIVector::Insert(JSThread *thread, const JSHandle &vector, const JSHandle &value, int32_t index) { - int32_t length = vector->GetSize(); - if (index < 0 || index > length) { + uint32_t length = vector->GetSize(); + if (index < 0 || index > static_cast(length)) { THROW_ERROR(thread, ErrorType::RANGE_ERROR, "the index is out-of-bounds"); } GrowCapacity(thread, vector, length + 1); TaggedArray *elements = TaggedArray::Cast(vector->GetElements().GetTaggedObject()); ASSERT(!elements->IsDictionaryMode()); - for (int32_t i = length - 1; i >= index; i--) { + for (int32_t i = static_cast(length) - 1; i >= index; i--) { elements->Set(thread, i + 1, elements->Get(i)); } @@ -61,7 +62,7 @@ void JSAPIVector::Insert(JSThread *thread, const JSHandle &vector, void JSAPIVector::SetLength(JSThread *thread, const JSHandle &vector, uint32_t newSize) { - uint32_t len = static_cast(vector->GetSize()); + uint32_t len = vector->GetSize(); if (newSize > len) { GrowCapacity(thread, vector, newSize); } @@ -103,15 +104,15 @@ int32_t JSAPIVector::GetIndexFrom(JSThread *thread, const JSHandle { TaggedArray *elements = TaggedArray::Cast(vector->GetElements().GetTaggedObject()); ASSERT(!elements->IsDictionaryMode()); - int32_t length = vector->GetSize(); + uint32_t length = vector->GetSize(); if (index < 0) { index = 0; - } else if (index >= length) { + } else if (index >= static_cast(length)) { THROW_RANGE_ERROR_AND_RETURN(thread, "no-such-element", -1); } JSMutableHandle value(thread, JSTaggedValue::Undefined()); - for (int32_t i = index; i < length; i++) { + for (uint32_t i = static_cast(index); i < length; i++) { value.Update(JSTaggedValue(elements->Get(i))); if (JSTaggedValue::StrictEqual(thread, obj, value)) { return i; @@ -127,7 +128,7 @@ bool JSAPIVector::IsEmpty() const JSTaggedValue JSAPIVector::GetLastElement() { - int32_t length = GetSize(); + uint32_t length = GetSize(); if (length == 0) { return JSTaggedValue::Undefined(); } @@ -139,7 +140,7 @@ JSTaggedValue JSAPIVector::GetLastElement() int32_t JSAPIVector::GetLastIndexOf(JSThread *thread, const JSHandle &vector, const JSHandle &obj) { - int32_t index = vector->GetSize() - 1; + int32_t index = static_cast(vector->GetSize()) - 1; if (index < 0) { return -1; // vector isEmpty, defalut return -1 } @@ -149,8 +150,8 @@ int32_t JSAPIVector::GetLastIndexOf(JSThread *thread, const JSHandle &vector, const JSHandle &obj, int32_t index) { - int32_t length = vector->GetSize(); - if (index >= length) { + uint32_t length = vector->GetSize(); + if (index >= static_cast(length)) { THROW_RANGE_ERROR_AND_RETURN(thread, "index-out-of-bounds", -1); } else if (index < 0) { index = 0; @@ -170,7 +171,7 @@ int32_t JSAPIVector::GetLastIndexFrom(JSThread *thread, const JSHandle &vector, const JSHandle &obj) { int32_t index = GetIndexOf(thread, vector, obj); - int32_t length = vector->GetSize(); + uint32_t length = vector->GetSize(); if (index >= 0) { JSHandle elements(thread, vector->GetElements()); ASSERT(!elements->IsDictionaryMode()); @@ -184,8 +185,8 @@ bool JSAPIVector::Remove(JSThread *thread, const JSHandle &vector, JSTaggedValue JSAPIVector::RemoveByIndex(JSThread *thread, const JSHandle &vector, int32_t index) { - int32_t length = vector->GetSize(); - if (index < 0 || index >= length) { + uint32_t length = vector->GetSize(); + if (index < 0 || index >= static_cast(length)) { THROW_RANGE_ERROR_AND_RETURN(thread, "the index is out-of-bounds", JSTaggedValue::Exception()); } TaggedArray *resElements = TaggedArray::Cast(vector->GetElements().GetTaggedObject()); @@ -206,7 +207,7 @@ JSTaggedValue JSAPIVector::RemoveByIndex(JSThread *thread, const JSHandle &vector, int32_t fromIndex, int32_t toIndex) { - int32_t length = vector->GetSize(); + int32_t length = static_cast(vector->GetSize()); if (toIndex <= fromIndex) { THROW_RANGE_ERROR_AND_RETURN(thread, "the fromIndex cannot be less than or equal to toIndex", JSTaggedValue::Exception()); @@ -233,8 +234,9 @@ JSTaggedValue JSAPIVector::RemoveByRange(JSThread *thread, const JSHandle JSAPIVector::SubVector(JSThread *thread, const JSHandle &vector, int32_t fromIndex, int32_t toIndex) { - int32_t length = vector->GetSize(); - if (fromIndex < 0 || toIndex < 0 || fromIndex >= length || toIndex >= length) { + int32_t length = static_cast(vector->GetSize()); + if (fromIndex < 0 || toIndex < 0 || + fromIndex >= length || toIndex >= length) { THROW_RANGE_ERROR_AND_RETURN(thread, "the fromIndex or the toIndex is out-of-bounds", JSHandle()); } @@ -243,12 +245,12 @@ JSHandle JSAPIVector::SubVector(JSThread *thread, const JSHandle()); } - int32_t newLength = toIndex - fromIndex; + uint32_t newLength = static_cast(toIndex - fromIndex); JSHandle subVector = thread->GetEcmaVM()->GetFactory()->NewJSAPIVector(newLength); TaggedArray *elements = TaggedArray::Cast(vector->GetElements().GetTaggedObject()); subVector->SetLength(newLength); - for (int32_t i = 0; i < newLength; i++) { + for (uint32_t i = 0; i < newLength; i++) { subVector->Set(thread, i, elements->Get(fromIndex + i)); } @@ -260,10 +262,10 @@ JSTaggedValue JSAPIVector::ToString(JSThread *thread, const JSHandleGetEcmaVM()->GetFactory(); std::u16string sepHandle = std::wstring_convert, char16_t> {}.from_bytes(","); - int32_t length = vector->GetSize(); + uint32_t length = vector->GetSize(); std::u16string concatStr; JSMutableHandle element(thread, JSTaggedValue::Undefined()); - for (int32_t k = 0; k < length; k++) { + for (uint32_t k = 0; k < length; k++) { std::u16string nextStr; element.Update(Get(thread, vector, k)); if (!element->IsUndefined() && !element->IsNull()) { @@ -290,13 +292,13 @@ JSTaggedValue JSAPIVector::ForEach(JSThread *thread, const JSHandle &thisArg) { JSHandle vector = JSHandle::Cast(thisHandle); - int32_t length = vector->GetSize(); + uint32_t length = vector->GetSize(); JSTaggedValue key = JSTaggedValue::Undefined(); JSMutableHandle kValue(thread, JSTaggedValue::Undefined()); - const int32_t argsLength = NUM_MANDATORY_JSFUNC_ARGS; + const uint32_t argsLength = NUM_MANDATORY_JSFUNC_ARGS; JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); - for (int32_t k = 0; k < length; k++) { + for (uint32_t k = 0; k < length; k++) { kValue.Update(Get(thread, vector, k)); key = JSTaggedValue(k); EcmaRuntimeCallInfo *info = @@ -318,13 +320,13 @@ JSTaggedValue JSAPIVector::ReplaceAllElements(JSThread *thread, const JSHandle &thisArg) { JSHandle vector = JSHandle::Cast(thisHandle); - int32_t length = vector->GetSize(); + uint32_t length = vector->GetSize(); JSTaggedValue key = JSTaggedValue::Undefined(); JSMutableHandle kValue(thread, JSTaggedValue::Undefined()); - const int32_t argsLength = NUM_MANDATORY_JSFUNC_ARGS; + const uint32_t argsLength = NUM_MANDATORY_JSFUNC_ARGS; JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); - for (int32_t k = 0; k < length; k++) { + for (uint32_t k = 0; k < length; k++) { kValue.Update(Get(thread, vector, k)); key = JSTaggedValue(k); EcmaRuntimeCallInfo *info = @@ -335,6 +337,9 @@ JSTaggedValue JSAPIVector::ReplaceAllElements(JSThread *thread, const JSHandleGetSize()) { // prevent length change length = vector->GetSize(); + if (k >= length) { + break; + } } vector->Set(thread, k, funcResult); } @@ -367,8 +372,8 @@ void JSAPIVector::GrowCapacity(JSThread *thread, const JSHandle &ve JSTaggedValue JSAPIVector::Get(JSThread *thread, const JSHandle &vector, int32_t index) { - int32_t len = vector->GetSize(); - if (index < 0 || index >= len) { + uint32_t len = vector->GetSize(); + if (index < 0 || index >= static_cast(len)) { THROW_RANGE_ERROR_AND_RETURN(thread, "the index is out-of-bounds", JSTaggedValue::Exception()); } @@ -386,12 +391,12 @@ JSTaggedValue JSAPIVector::Set(JSThread *thread, int32_t index, const JSTaggedVa bool JSAPIVector::Has(const JSTaggedValue &value) const { TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); - int32_t length = GetSize(); + uint32_t length = GetSize(); if (length == 0) { return false; } - for (int32_t i = 0; i < length; i++) { + for (uint32_t i = 0; i < length; i++) { if (JSTaggedValue::SameValue(elements->Get(i), value)) { return true; } @@ -417,7 +422,7 @@ bool JSAPIVector::GetOwnProperty(JSThread *thread, const JSHandle & THROW_TYPE_ERROR_AND_RETURN(thread, "Can not obtain attributes of no-number type", false); } - uint32_t length = static_cast(obj->GetSize()); + uint32_t length = obj->GetSize(); if (index >= length) { THROW_RANGE_ERROR_AND_RETURN(thread, "GetOwnProperty index out-of-bounds", false); } @@ -429,21 +434,21 @@ bool JSAPIVector::GetOwnProperty(JSThread *thread, const JSHandle & void JSAPIVector::TrimToCurrentLength(JSThread *thread, const JSHandle &obj) { - int32_t length = obj->GetSize(); + uint32_t length = obj->GetSize(); uint32_t capacity = obj->GetCapacity(); TaggedArray *elements = TaggedArray::Cast(obj->GetElements().GetTaggedObject()); ASSERT(!elements->IsDictionaryMode()); - if (capacity > static_cast(length)) { + if (capacity > length) { elements->Trim(thread, length); } } void JSAPIVector::Clear(JSThread *thread, const JSHandle &obj) { - int length = obj->GetLength(); + uint32_t length = obj->GetLength(); JSHandle elements(thread, obj->GetElements()); ASSERT(!elements->IsDictionaryMode()); - for (int i = 0; i <= length; ++i) { + for (uint32_t i = 0; i < length; ++i) { elements->Set(thread, i, JSTaggedValue::Hole()); } obj->SetLength(0); @@ -455,7 +460,7 @@ JSHandle JSAPIVector::Clone(JSThread *thread, const JSHandleGetEcmaVM()->GetFactory(); JSHandle newVector = factory->NewJSAPIVector(0); - int32_t length = obj->GetSize(); + uint32_t length = obj->GetSize(); newVector->SetLength(length); JSHandle dstElements = factory->NewAndCopyTaggedArray(srcElements, length, length); @@ -465,7 +470,7 @@ JSHandle JSAPIVector::Clone(JSThread *thread, const JSHandle &vector) { - int32_t length = vector->GetSize(); + uint32_t length = vector->GetSize(); if (length == 0) { return JSTaggedValue::Undefined(); } @@ -484,9 +489,9 @@ JSTaggedValue JSAPIVector::GetIteratorObj(JSThread *thread, const JSHandle &obj, const JSHandle &key) { - int length = obj->GetSize(); + uint32_t length = obj->GetSize(); int index = key->GetInt(); - if (index < 0 || index >= length) { + if (index < 0 || index >= static_cast(length)) { THROW_RANGE_ERROR_AND_RETURN(thread, "GetProperty index out-of-bounds", OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false))); } @@ -498,9 +503,9 @@ bool JSAPIVector::SetProperty(JSThread *thread, const JSHandle &obj const JSHandle &key, const JSHandle &value) { - int length = obj->GetSize(); + uint32_t length = obj->GetSize(); int index = key->GetInt(); - if (index < 0 || index >= length) { + if (index < 0 || index >= static_cast(length)) { return false; } diff --git a/ecmascript/js_api/js_api_vector.h b/ecmascript/js_api/js_api_vector.h index 28edd7d20e2cb7b27bb579c3f5e67fc474bb3239..68a05bd24b182138ca895c6fb1b1502e2453a2aa 100644 --- a/ecmascript/js_api/js_api_vector.h +++ b/ecmascript/js_api/js_api_vector.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_JS_API_VECTOR_H -#define ECMASCRIPT_JS_API_VECTOR_H +#ifndef ECMASCRIPT_JS_API_JS_API_VECTOR_H +#define ECMASCRIPT_JS_API_JS_API_VECTOR_H #include "ecmascript/js_object.h" #include "ecmascript/js_tagged_value-inl.h" @@ -103,13 +103,13 @@ public: const JSHandle &key, const JSHandle &value); - inline int32_t GetSize() const + inline uint32_t GetSize() const { return GetLength(); } static constexpr size_t ELEMENT_COUNT_OFFSET = JSObject::SIZE; - ACCESSORS_PRIMITIVE_FIELD(Length, int32_t, ELEMENT_COUNT_OFFSET, ENDL_OFFSET) + ACCESSORS_PRIMITIVE_FIELD(Length, uint32_t, ELEMENT_COUNT_OFFSET, ENDL_OFFSET) DEFINE_ALIGN_SIZE(ENDL_OFFSET); DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, ELEMENT_COUNT_OFFSET, ELEMENT_COUNT_OFFSET) @@ -119,4 +119,4 @@ private: }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_JSAPIVECTOR_H +#endif // ECMASCRIPT_JS_API_JS_API_VECTOR_H diff --git a/ecmascript/js_api/js_api_vector_iterator.h b/ecmascript/js_api/js_api_vector_iterator.h index d4d5c975401c4bd95fac7aa2c2a1d7d79000e21d..e52371d76a6325a7d3a71739ff48c09a9d64814a 100644 --- a/ecmascript/js_api/js_api_vector_iterator.h +++ b/ecmascript/js_api/js_api_vector_iterator.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_JS_API_VECTOR_ITERATOR_H -#define ECMASCRIPT_JS_API_VECTOR_ITERATOR_H +#ifndef ECMASCRIPT_JS_API_JS_API_VECTOR_ITERATOR_H +#define ECMASCRIPT_JS_API_JS_API_VECTOR_ITERATOR_H #include "ecmascript/js_iterator.h" #include "ecmascript/js_object.h" @@ -40,4 +40,4 @@ public: }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_JS_API_VECTOR_ITERATOR_H \ No newline at end of file +#endif // ECMASCRIPT_JS_API_JS_API_VECTOR_ITERATOR_H \ No newline at end of file diff --git a/ecmascript/js_array.cpp b/ecmascript/js_array.cpp index 60b2b31a553c724535a77ed23e46e2c138c94ba8..5d1c2c418324ee4d5408bad8bdd4e04c52a0f76e 100644 --- a/ecmascript/js_array.cpp +++ b/ecmascript/js_array.cpp @@ -24,9 +24,11 @@ #include "ecmascript/object_fast_operator-inl.h" namespace panda::ecmascript { +using base::ArrayHelper; + JSTaggedValue JSArray::LengthGetter([[maybe_unused]] JSThread *thread, const JSHandle &self) { - return JSArray::Cast(*self)->GetLength(); + return JSTaggedValue(JSArray::Cast(*self)->GetLength()); } bool JSArray::LengthSetter(JSThread *thread, const JSHandle &self, const JSHandle &value, @@ -74,7 +76,7 @@ JSHandle JSArray::ArrayCreate(JSThread *thread, JSTaggedNumber le // 8. Set the [[Prototype]] internal slot of A to proto. JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); JSHandle arrayFunc(env->GetArrayFunction()); - JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(arrayFunc), newTarget); + JSHandle obj = factory->NewJSObjectByConstructor(arrayFunc, newTarget); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); // 9. Set the [[Extensible]] internal slot of A to true. obj->GetJSHClass()->SetExtensible(true); @@ -84,7 +86,7 @@ JSHandle JSArray::ArrayCreate(JSThread *thread, JSTaggedNumber le if (mode == ArrayMode::LITERAL) { JSArray::Cast(*obj)->SetArrayLength(thread, normalArrayLength); } else { - JSArray::SetCapacity(thread, obj, 0, normalArrayLength); + JSArray::SetCapacity(thread, obj, 0, normalArrayLength, true); } return JSHandle(obj); @@ -115,7 +117,7 @@ JSTaggedValue JSArray::ArraySpeciesCreate(JSThread *thread, const JSHandleGetJSHClass(); if (hclass->IsJSArray() && !hclass->HasConstructor()) { - return JSArray::ArrayCreate(thread, length).GetTaggedValue(); + return JSArray::ArrayCreate(thread, length, ArrayMode::LITERAL).GetTaggedValue(); } JSHandle constructorKey = globalConst->GetHandledConstructorString(); constructor = JSTaggedValue::GetProperty(thread, originalValue, constructorKey).GetValue(); @@ -133,7 +135,7 @@ JSTaggedValue JSArray::ArraySpeciesCreate(JSThread *thread, const JSHandleGetArrayFunction().GetTaggedValue(); // If SameValue(C, realmC.[[intrinsics]].[[%Array%]]) is true, let C be undefined. if (JSTaggedValue::SameValue(constructor.GetTaggedValue(), realmArrayConstructor)) { - return JSArray::ArrayCreate(thread, length).GetTaggedValue(); + return JSArray::ArrayCreate(thread, length, ArrayMode::LITERAL).GetTaggedValue(); } } } @@ -147,14 +149,14 @@ JSTaggedValue JSArray::ArraySpeciesCreate(JSThread *thread, const JSHandleIsNull()) { - return JSArray::ArrayCreate(thread, length).GetTaggedValue(); + return JSArray::ArrayCreate(thread, length, ArrayMode::LITERAL).GetTaggedValue(); } } } // If C is undefined, return ArrayCreate(length). if (constructor->IsUndefined()) { - return JSArray::ArrayCreate(thread, length).GetTaggedValue(); + return JSArray::ArrayCreate(thread, length, ArrayMode::LITERAL).GetTaggedValue(); } // If IsConstructor(C) is false, throw a TypeError exception. if (!constructor->IsConstructor()) { @@ -167,6 +169,7 @@ JSTaggedValue JSArray::ArraySpeciesCreate(JSThread *thread, const JSHandleSetCallArg(JSTaggedValue(arrayLength)); JSTaggedValue result = JSFunction::Construct(info); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // NOTEIf originalArray was created using the standard built-in Array constructor for // a Realm that is not the Realm of the running execution context, then a new Array is @@ -176,7 +179,8 @@ JSTaggedValue JSArray::ArraySpeciesCreate(JSThread *thread, const JSHandle &array, uint32_t oldLen, uint32_t newLen) +void JSArray::SetCapacity(JSThread *thread, const JSHandle &array, uint32_t oldLen, uint32_t newLen, + bool isNew) { TaggedArray *element = TaggedArray::Cast(array->GetElements().GetTaggedObject()); @@ -221,7 +225,7 @@ void JSArray::SetCapacity(JSThread *thread, const JSHandle &array, uin if (JSObject::ShouldTransToDict(oldLen, newLen)) { JSObject::ElementsToDictionary(thread, array); } else if (newLen > capacity) { - JSObject::GrowElementsCapacity(thread, array, newLen); + JSObject::GrowElementsCapacity(thread, array, newLen, isNew); } JSArray::Cast(*array)->SetArrayLength(thread, newLen); } @@ -385,30 +389,67 @@ bool JSArray::FastSetPropertyByValue(JSThread *thread, const JSHandle &obj, const JSHandle &fn) +// ecma2024 23.1.3.20 Array.prototype.sort(comparefn) +JSTaggedValue JSArray::Sort(JSThread *thread, const JSHandle &obj, const JSHandle &fn) { - if (!fn->IsUndefined() && !fn->IsCallable()) { - THROW_TYPE_ERROR(thread, "Callable is false"); + ASSERT(fn->IsUndefined() || fn->IsCallable()); + // 3. Let len be ?LengthOfArrayLike(obj). + int64_t len = ArrayHelper::GetArrayLength(thread, obj); + // ReturnIfAbrupt(len). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + // 4. Let SortCompare be a new Abstract Closure with parameters (x, y) that captures comparefn and performs + // the following steps when called: + // a. Return ? CompareArrayElements(x, y, comparefn). + // 5. Let sortedList be ? SortIndexedProperties(O, len, SortCompare, SKIP-HOLES). + JSHandle sortedList = + ArrayHelper::SortIndexedProperties(thread, obj, len, fn, base::HolesType::SKIP_HOLES); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 6. Let itemCount be the number of elements in sortedList. + uint32_t itemCount = sortedList->GetLength(); + + // 7. Let j be 0. + uint32_t j = 0; + // 8. Repeat, while j < itemCount, + // a. Perform ! Set(obj, ! ToString((j)), sortedList[j], true). + // b. Set j to j + 1. + JSMutableHandle item(thread, JSTaggedValue::Undefined()); + while (j < itemCount) { + item.Update(sortedList->Get(j)); + JSArray::FastSetPropertyByValue(thread, obj, j, item); + ASSERT_NO_ABRUPT_COMPLETION(thread); + ++j; + } + // 9. NOTE: The call to SortIndexedProperties in step 5 uses SKIP-HOLES.The remaining indices are deleted to + // preserve the number of holes that were detected and excluded from the sort. + // 10. Repeat, while j < len, + // a. Perform ? DeletePropertyOrThrow(obj, ! ToString((j))). + // b. Set j to j + 1. + while (j < len) { + item.Update(JSTaggedValue(j)); + JSTaggedValue::DeletePropertyOrThrow(thread, obj, item); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + ++j; } - // 2. Let len be ToLength(Get(obj, "length")). - int64_t len = base::ArrayHelper::GetArrayLength(thread, JSHandle(obj)); - // 3. ReturnIfAbrupt(len). - RETURN_IF_ABRUPT_COMPLETION(thread); + return obj.GetTaggedValue(); +} + +void JSArray::SortElements(JSThread *thread, const JSHandle &elements, const JSHandle &fn) +{ + ASSERT(fn->IsUndefined() || fn->IsCallable()); JSMutableHandle presentValue(thread, JSTaggedValue::Undefined()); JSMutableHandle middleValue(thread, JSTaggedValue::Undefined()); JSMutableHandle previousValue(thread, JSTaggedValue::Undefined()); - for (int64_t i = 1; i < len; i++) { - int64_t beginIndex = 0; - int64_t endIndex = i; - presentValue.Update(ObjectFastOperator::FastGetPropertyByIndex(thread, obj.GetTaggedValue(), i)); - RETURN_IF_ABRUPT_COMPLETION(thread); + uint32_t len = elements->GetLength(); + for (uint32_t i = 1; i < len; i++) { + uint32_t beginIndex = 0; + uint32_t endIndex = i; + presentValue.Update(elements->Get(i)); while (beginIndex < endIndex) { - int64_t middleIndex = (beginIndex + endIndex) / 2; // 2 : half - middleValue.Update( - ObjectFastOperator::FastGetPropertyByIndex(thread, obj.GetTaggedValue(), middleIndex)); - RETURN_IF_ABRUPT_COMPLETION(thread); + uint32_t middleIndex = (beginIndex + endIndex) / 2; // 2 : half + middleValue.Update(elements->Get(middleIndex)); int32_t compareResult = base::ArrayHelper::SortCompare(thread, fn, middleValue, presentValue); RETURN_IF_ABRUPT_COMPLETION(thread); if (compareResult > 0) { @@ -419,17 +460,11 @@ void JSArray::Sort(JSThread *thread, const JSHandle &obj, const JSHand } if (endIndex >= 0 && endIndex < i) { - for (int64_t j = i; j > endIndex; j--) { - previousValue.Update( - ObjectFastOperator::FastGetPropertyByIndex(thread, obj.GetTaggedValue(), j - 1)); - RETURN_IF_ABRUPT_COMPLETION(thread); - ObjectFastOperator::FastSetPropertyByIndex(thread, obj.GetTaggedValue(), j, - previousValue.GetTaggedValue()); - RETURN_IF_ABRUPT_COMPLETION(thread); + for (uint32_t j = i; j > endIndex; j--) { + previousValue.Update(elements->Get(j - 1)); + elements->Set(thread, j, previousValue); } - ObjectFastOperator::FastSetPropertyByIndex(thread, obj.GetTaggedValue(), endIndex, - presentValue.GetTaggedValue()); - RETURN_IF_ABRUPT_COMPLETION(thread); + elements->Set(thread, endIndex, presentValue); } } } @@ -479,15 +514,16 @@ void JSArray::CheckAndCopyArray(const JSThread *thread, JSHandle obj) JSHandle arr(thread, obj->GetElements()); // Check whether array is shared in the nonmovable space before set properties and elements. // If true, then really copy array in the semi space. + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); if (arr.GetTaggedValue().IsCOWArray()) { - auto newArray = thread->GetEcmaVM()->GetFactory()->CopyArray(arr, arr->GetLength(), arr->GetLength(), + auto newArray = factory->CopyArray(arr, arr->GetLength(), arr->GetLength(), JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE); obj->SetElements(thread, newArray.GetTaggedValue()); } JSHandle prop(thread, obj->GetProperties()); if (prop.GetTaggedValue().IsCOWArray()) { - auto newProps = thread->GetEcmaVM()->GetFactory()->CopyArray(prop, - prop->GetLength(), prop->GetLength(), JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE); + auto newProps = factory->CopyArray(prop, prop->GetLength(), prop->GetLength(), + JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE); obj->SetProperties(thread, newProps.GetTaggedValue()); } } diff --git a/ecmascript/js_array.h b/ecmascript/js_array.h index 0215afb537f061095c8fca351784ded1e3ce6464..f16406bfe901ae02075d7db1cb5b0d73831137ee 100644 --- a/ecmascript/js_array.h +++ b/ecmascript/js_array.h @@ -49,18 +49,20 @@ public: // use first inlined property slot for array length inline uint32_t GetArrayLength() const { - return GetLength().GetArrayLength(); + return GetLength(); } - inline void SetArrayLength(const JSThread *thread, uint32_t length) + inline void SetArrayLength([[maybe_unused]]const JSThread *thread, uint32_t length) { - SetLength(thread, JSTaggedValue(length)); + SetLength(length); } static constexpr size_t LENGTH_OFFSET = JSObject::SIZE; - ACCESSORS(Length, LENGTH_OFFSET, SIZE); + ACCESSORS_PRIMITIVE_FIELD(Length, uint32_t, LENGTH_OFFSET, TRACE_INDEX_OFFSET) + ACCESSORS_PRIMITIVE_FIELD(TraceIndex, uint32_t, TRACE_INDEX_OFFSET, TRACK_INFO_OFFSET) + ACCESSORS(TrackInfo, TRACK_INFO_OFFSET, SIZE) - DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, LENGTH_OFFSET, SIZE) + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, TRACK_INFO_OFFSET, SIZE) static const uint32_t MAX_ARRAY_INDEX = MAX_ELEMENT_INDEX; DECL_DUMP() @@ -89,12 +91,38 @@ public: static bool FastSetPropertyByValue(JSThread *thread, const JSHandle &obj, const JSHandle &key, const JSHandle &value); - static void Sort(JSThread *thread, const JSHandle &obj, const JSHandle &fn); + static JSTaggedValue Sort(JSThread *thread, const JSHandle &obj, const JSHandle &fn); static bool IncludeInSortedValue(JSThread *thread, const JSHandle &obj, const JSHandle &value); static JSHandle ToTaggedArray(JSThread *thread, const JSHandle &obj); static void CheckAndCopyArray(const JSThread *thread, JSHandle obj); - static void SetCapacity(JSThread *thread, const JSHandle &array, uint32_t oldLen, uint32_t newLen); + static void SetCapacity(JSThread *thread, const JSHandle &array, uint32_t oldLen, uint32_t newLen, + bool isNew = false); + static void SortElements(JSThread *thread, const JSHandle &elements, + const JSHandle &fn); +}; + +class TrackInfo : public TaggedObject { +public: + static TrackInfo *Cast(TaggedObject *object) + { + ASSERT(JSTaggedValue(object).IsTrackInfoObject()); + return static_cast(object); + } + + static constexpr size_t CACHED_HCLASS_OFFSET = TaggedObjectSize(); + ACCESSORS(CachedHClass, CACHED_HCLASS_OFFSET, CACHED_FUNC_OFFSET); + ACCESSORS(CachedFunc, CACHED_FUNC_OFFSET, BIT_FIELD_OFFSET); + ACCESSORS_BIT_FIELD(BitField, BIT_FIELD_OFFSET, LAST_OFFSET); + DEFINE_ALIGN_SIZE(LAST_OFFSET); + + // define BitField + static constexpr size_t ELEMENTS_KIND_BITS = 8; + FIRST_BIT_FIELD(BitField, ElementsKind, ElementsKind, ELEMENTS_KIND_BITS); + + DECL_DUMP() + + DECL_VISIT_OBJECT(CACHED_HCLASS_OFFSET, BIT_FIELD_OFFSET); }; } // namespace panda::ecmascript diff --git a/ecmascript/js_arraybuffer.cpp b/ecmascript/js_arraybuffer.cpp index 30f7302d34469b8bc983aa07455e78688f01a810..178d0e14a0cb9245fb9a981efe550a151a214312 100644 --- a/ecmascript/js_arraybuffer.cpp +++ b/ecmascript/js_arraybuffer.cpp @@ -18,7 +18,9 @@ #include "ecmascript/base/builtins_base.h" #include "ecmascript/ecma_macros.h" #include "ecmascript/ecma_vm.h" +#include "ecmascript/mem/barriers-inl.h" #include "ecmascript/object_factory.h" +#include "ecmascript/platform/os.h" #include "ecmascript/tagged_array.h" #include "securec.h" @@ -41,14 +43,23 @@ void JSArrayBuffer::CopyDataPointBytes(void *toBuf, void *fromBuf, int32_t fromI } } -void JSArrayBuffer::Attach(JSThread *thread, uint32_t arrayBufferByteLength, JSTaggedValue arrayBufferData) +void JSArrayBuffer::Attach(JSThread *thread, uint32_t arrayBufferByteLength, + JSTaggedValue arrayBufferData, bool transferWithNativeAreaAllocator) { - ASSERT(arrayBufferData.IsNativePointer()); + ASSERT(arrayBufferData.IsJSNativePointer()); + // only in transition, should the JSArrayBuffer with NativeAreaAllocator increase mem usage + if (transferWithNativeAreaAllocator) { + LOG_FULL(DEBUG) << "attaching for transfer"; + JSHandle np(thread, arrayBufferData.GetTaggedObject()); + NativeAreaAllocator *allocator = thread->GetEcmaVM()->GetNativeAreaAllocator(); + allocator->IncreaseNativeMemoryUsage(MallocUsableSize(np->GetExternalPointer())); + np->SetData(allocator); + } SetArrayBufferByteLength(arrayBufferByteLength); SetArrayBufferData(thread, arrayBufferData); } -void JSArrayBuffer::Detach(JSThread *thread) +void JSArrayBuffer::Detach(JSThread *thread, bool transferWithNativeAreaAllocator) { JSTaggedValue arrayBufferData = GetArrayBufferData(); // already detached. @@ -56,7 +67,15 @@ void JSArrayBuffer::Detach(JSThread *thread) return; } + // only in transition, should the JSArrayBuffer with NativeAreaAllocator decrease mem usage + if (transferWithNativeAreaAllocator) { + LOG_FULL(DEBUG) << "detaching for transfer"; + JSHandle np(thread, arrayBufferData.GetTaggedObject()); + NativeAreaAllocator *allocator = thread->GetEcmaVM()->GetNativeAreaAllocator(); + allocator->DecreaseNativeMemoryUsage(MallocUsableSize(np->GetExternalPointer())); + np->SetData(nullptr); + } SetArrayBufferData(thread, JSTaggedValue::Null()); SetArrayBufferByteLength(0); } -} // namespace panda::ecmascript +} // namespace panda::ecmascript \ No newline at end of file diff --git a/ecmascript/js_arraybuffer.h b/ecmascript/js_arraybuffer.h index 87214c2a68d2419cb9ba9ba397b95af09446f2a3..7a40236827e4c595b1a560cd41ca97295d20d7aa 100644 --- a/ecmascript/js_arraybuffer.h +++ b/ecmascript/js_arraybuffer.h @@ -27,8 +27,9 @@ public: static void CopyDataBlockBytes(JSTaggedValue toBlock, JSTaggedValue fromBlock, int32_t fromIndex, int32_t count); static void CopyDataPointBytes(void *toBuf, void *fromBuf, int32_t fromIndex, int32_t count); - void Attach(JSThread *thread, uint32_t arrayBufferByteLength, JSTaggedValue arrayBufferData); - void Detach(JSThread *thread); + void Attach(JSThread *thread, uint32_t arrayBufferByteLength, JSTaggedValue arrayBufferData, + bool transferWithNativeAreaAllocator = false); + void Detach(JSThread *thread, bool transferWithNativeAreaAllocator = false); bool IsDetach() { @@ -43,8 +44,11 @@ public: DEFINE_ALIGN_SIZE(LAST_OFFSET); // define BitField - static constexpr size_t SHARED_BITS = 2; + static constexpr size_t SHARED_BITS = 1; + static constexpr size_t WITH_NATIVE_AREA_ALLOCATOR_BITS = 1; + FIRST_BIT_FIELD(BitField, Shared, bool, SHARED_BITS) + NEXT_BIT_FIELD(BitField, WithNativeAreaAllocator, bool, WITH_NATIVE_AREA_ALLOCATOR_BITS, Shared) DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, DATA_OFFSET, BYTE_LENGTH_OFFSET) DECL_DUMP() diff --git a/ecmascript/js_async_from_sync_iterator.cpp b/ecmascript/js_async_from_sync_iterator.cpp index 821dc47e6caec7a1b18f70d28dd5c854a2b7a6c6..ec7cd891d44202ceb6dd2abdf5cf8adb3733973d 100644 --- a/ecmascript/js_async_from_sync_iterator.cpp +++ b/ecmascript/js_async_from_sync_iterator.cpp @@ -45,6 +45,7 @@ JSHandle JSAsyncFromSyncIterator::CreateAsyncFromSyncIterator(JST JSHandle nextStr = thread->GlobalConstants()->GetHandledNextString(); JSHandle tmpAsyncIterator(thread, asyncIterator.GetTaggedValue()); JSHandle nextMethod = JSTaggedValue::GetProperty(thread, tmpAsyncIterator, nextStr).GetValue(); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); // 4.Let iteratorRecord be the Record {[[Iterator]]: asyncIterator, [[NextMethod]]: nextMethod, [[Done]]: false}. JSHandle iteratorRecord = factory->NewAsyncIteratorRecord(tmpAsyncIterator, nextMethod, false); diff --git a/ecmascript/js_async_function.cpp b/ecmascript/js_async_function.cpp index 93b85197d45bcdf466ff8a9fc522bec10197b360..af1ea467950f4052892462012c8ce19e455ec71d 100644 --- a/ecmascript/js_async_function.cpp +++ b/ecmascript/js_async_function.cpp @@ -57,6 +57,7 @@ void JSAsyncFunction::AsyncFunctionAwait(JSThread *thread, const JSHandleSetCallArg(value.GetTaggedValue()); [[maybe_unused]] JSTaggedValue res = JSFunction::Call(info); + RETURN_IF_ABRUPT_COMPLETION(thread); // 4.Let onFulfilled be a new built-in function object as defined in AsyncFunction Awaited Fulfilled. JSHandle fulFunc = factory->NewJSAsyncAwaitStatusFunction( @@ -100,10 +101,12 @@ void JSAsyncFunction::AsyncFunctionAwait(JSThread *thread, const JSHandle asyncCtxt; if (asyncFuncObj->IsAsyncGeneratorObject()) { JSHandle obj = JSTaggedValue::ToObject(thread, asyncFuncObj); + RETURN_IF_ABRUPT_COMPLETION(thread); JSHandle asyncGen = JSHandle::Cast(obj); asyncCtxt = JSHandle(thread, asyncGen->GetGeneratorContext()); } else { JSHandle obj = JSTaggedValue::ToObject(thread, asyncFuncObj); + RETURN_IF_ABRUPT_COMPLETION(thread); JSHandle asyncFun = JSHandle::Cast(obj); asyncCtxt = JSHandle(thread, asyncFun->GetGeneratorContext()); } diff --git a/ecmascript/js_async_generator_object.cpp b/ecmascript/js_async_generator_object.cpp index 505299ddd7c5eee6483a3fa4ce0c648a4254998f..16cd628b02998c74752d27de1dfe9a151ae4b32e 100644 --- a/ecmascript/js_async_generator_object.cpp +++ b/ecmascript/js_async_generator_object.cpp @@ -45,6 +45,7 @@ void JSAsyncGeneratorObject::AsyncGeneratorValidate(JSThread *thread, const JSHa } // 4. If generator.[[GeneratorBrand]] is not the same value as generatorBrand, throw a TypeError exception. JSHandle obj = JSTaggedValue::ToObject(thread, gen); + RETURN_IF_ABRUPT_COMPLETION(thread); JSHandle generator = JSHandle::Cast(obj); if (!JSTaggedValue::SameValue(generator->GetGeneratorBrand(), val)) { THROW_TYPE_ERROR(thread, "Results are not equal"); @@ -75,8 +76,10 @@ JSTaggedValue JSAsyncGeneratorObject::AsyncGeneratorResolve(JSThread *thread, JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); EcmaRuntimeCallInfo* info = EcmaInterpreter::NewRuntimeCallInfo(thread, resolve, undefined, undefined, 1); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); info->SetCallArg(its.GetTaggedValue()); [[maybe_unused]] JSTaggedValue res = JSFunction::Call(info); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 9. Perform ! AsyncGeneratorResumeNext(generator). AsyncGeneratorResumeNext(thread, generator); @@ -108,8 +111,10 @@ JSTaggedValue JSAsyncGeneratorObject::AsyncGeneratorReject(JSThread *thread, const JSHandle undefined = constants->GetHandledUndefined(); EcmaRuntimeCallInfo* info = EcmaInterpreter::NewRuntimeCallInfo(thread, reject, thisArg, undefined, 1); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); info->SetCallArg(value.GetTaggedValue()); [[maybe_unused]] JSTaggedValue res = JSFunction::Call(info); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 8. Perform ! AsyncGeneratorResumeNext(generator). AsyncGeneratorResumeNext(thread, generator); // 9. Return undefined. @@ -187,6 +192,7 @@ JSTaggedValue JSAsyncGeneratorObject::AsyncGeneratorResumeNext(JSThread *thread, // 11. Perform ! PerformPromiseThen(promise, onFulfilled, onRejected). JSHandle tcap = JSPromise::NewPromiseCapability(thread, JSHandle::Cast(env->GetPromiseFunction())); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); [[maybe_unused]] JSTaggedValue pres = BuiltinsPromise::PerformPromiseThen( thread, handPromise, JSHandle::Cast(onFulfilled), JSHandle::Cast(onFulRejected), tcap); @@ -246,6 +252,7 @@ JSTaggedValue JSAsyncGeneratorObject::AsyncGeneratorEnqueue(JSThread *thread, co ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle pcap = JSPromise::NewPromiseCapability(thread, JSHandle::Cast(env->GetPromiseFunction())); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 2. Let check be AsyncGeneratorValidate(generator, generatorBrand). AsyncGeneratorValidate(thread, gen, JSTaggedValue::Undefined()); // 3. If check is an abrupt completion, then @@ -262,8 +269,10 @@ JSTaggedValue JSAsyncGeneratorObject::AsyncGeneratorEnqueue(JSThread *thread, co JSHandle undefined = constants->GetHandledUndefined(); EcmaRuntimeCallInfo* info = EcmaInterpreter::NewRuntimeCallInfo(thread, reject, thisArg, undefined, 1); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); info->SetCallArg(rstErr.GetTaggedValue()); [[maybe_unused]] JSTaggedValue res = JSFunction::Call(info); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // c. Return promiseCapability.[[Promise]]. JSHandle promise(thread, pcap->GetPromise()); @@ -271,6 +280,7 @@ JSTaggedValue JSAsyncGeneratorObject::AsyncGeneratorEnqueue(JSThread *thread, co } // 4. Let queue be generator.[[AsyncGeneratorQueue]]. JSHandle obj = JSTaggedValue::ToObject(thread, gen); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle generator = JSHandle::Cast(obj); JSHandle queue(thread, generator->GetAsyncGeneratorQueue()); // 5. Let request be AsyncGeneratorRequest { [[Completion]]: completion, [[Capability]]: promiseCapability }. @@ -316,6 +326,7 @@ JSTaggedValue JSAsyncGeneratorObject::PromiseResolve(JSThread *thread, const JSH JSHandle thisArg = globalConst->GetHandledUndefined(); EcmaRuntimeCallInfo* info = EcmaInterpreter::NewRuntimeCallInfo(thread, resolve, thisArg, undefined, 1); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); info->SetCallArg(value.GetTaggedValue()); [[maybe_unused]] JSTaggedValue res = JSFunction::Call(info); diff --git a/ecmascript/js_bigint.cpp b/ecmascript/js_bigint.cpp index 9b47af7838b742027e5b1086edcb599ea0d6845e..28239e41a24ef38b35b196992df98b1510423680 100644 --- a/ecmascript/js_bigint.cpp +++ b/ecmascript/js_bigint.cpp @@ -68,6 +68,7 @@ CString BigIntHelper::Conversion(const CString &num, uint32_t conversionToRadix, JSHandle BigInt::GetUint64MaxBigint(JSThread *thread) { JSHandle bigint = CreateBigint(thread, 3); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(BigInt, thread); bigint->SetDigit(0, 0); bigint->SetDigit(1, 0); bigint->SetDigit(2, 1); @@ -77,6 +78,7 @@ JSHandle BigInt::GetUint64MaxBigint(JSThread *thread) JSHandle BigInt::GetInt64MaxBigint(JSThread *thread) { JSHandle bigint = CreateBigint(thread, 2); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(BigInt, thread); bigint->SetDigit(0, 0); bigint->SetDigit(1, 0x80000000); // 0x80000000:Int MAX return bigint; @@ -104,10 +106,12 @@ JSHandle BigIntHelper::SetBigInt(JSThread *thread, const CString &numStr if (mod == 0) { index = static_cast(len - 1); bigint = BigInt::CreateBigint(thread, len); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(BigInt, thread); } else { len++; index = static_cast(len - 1); bigint = BigInt::CreateBigint(thread, len); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(BigInt, thread); uint32_t val = 0; for (size_t i = 0; i < mod; ++i) { val <<= 1; @@ -274,6 +278,7 @@ JSHandle OneIsNegativeAND(JSThread *thread, JSHandle x, JSHandle minLen = yLength; } JSHandle newBigint = BigInt::CreateBigint(thread, xLength); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(BigInt, thread); uint32_t i = 0; while (i < minLen) { uint32_t res = x->GetDigit(i) & ~(yVal->GetDigit(i)); @@ -343,7 +348,7 @@ JSHandle BigInt::BitwiseSubOne(JSThread *thread, JSHandle bigint ASSERT(maxLen >= bigint->GetLength()); JSHandle newBigint = BigInt::CreateBigint(thread, maxLen); - + RETURN_HANDLE_IF_ABRUPT_COMPLETION(BigInt, thread); uint32_t bigintLen = bigint->GetLength(); uint32_t carry = 1; for (uint32_t i = 0; i < bigintLen; i++) { @@ -371,7 +376,7 @@ JSHandle BigInt::BitwiseAddOne(JSThread *thread, JSHandle bigint newLength += 1; } JSHandle newBigint = BigInt::CreateBigint(thread, newLength); - + RETURN_HANDLE_IF_ABRUPT_COMPLETION(BigInt, thread); uint32_t carry = 1; for (uint32_t i = 0; i < bigintLength; i++) { uint32_t bigintCarry = 0; @@ -399,6 +404,7 @@ JSHandle OneIsNegativeOR(JSThread *thread, JSHandle x, JSHandle< minLen = yLength; } JSHandle newBigint = BigInt::CreateBigint(thread, yLength); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(BigInt, thread); uint32_t i = 0; while (i < minLen) { uint32_t res = ~(x->GetDigit(i)) & yVal->GetDigit(i); @@ -481,6 +487,7 @@ JSTaggedValue BigInt::NumberToBigInt(JSThread *thread, JSHandle n uint32_t mayNeedLen = integerDigits / DATEBITS + 1; JSHandle bigint = CreateBigint(thread, mayNeedLen); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); bigint->SetSign(num < 0); uint64_t mantissa = (bits & base::DOUBLE_SIGNIFICAND_MASK) | base::DOUBLE_HIDDEN_BIT; int mantissaSize = base::DOUBLE_SIGNIFICAND_SIZE; @@ -508,6 +515,7 @@ JSTaggedValue BigInt::NumberToBigInt(JSThread *thread, JSHandle n JSHandle BigInt::Int32ToBigInt(JSThread *thread, const int &number) { JSHandle bigint = CreateBigint(thread, 1); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(BigInt, thread); uint32_t value = 0; bool sign = number < 0; if (sign) { @@ -523,6 +531,7 @@ JSHandle BigInt::Int32ToBigInt(JSThread *thread, const int &number) JSHandle BigInt::Uint32ToBigInt(JSThread *thread, const uint32_t &number) { JSHandle bigint = CreateBigint(thread, 1); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(BigInt, thread); bigint->SetDigit(0, number); return bigint; } @@ -537,6 +546,7 @@ JSHandle BigInt::Int64ToBigInt(JSThread *thread, const int64_t &number) value = number; } JSHandle bigint = Uint64ToBigInt(thread, value); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(BigInt, thread); bigint->SetSign(sign); return BigIntHelper::RightTruncate(thread, bigint); } @@ -544,6 +554,7 @@ JSHandle BigInt::Int64ToBigInt(JSThread *thread, const int64_t &number) JSHandle BigInt::Uint64ToBigInt(JSThread *thread, const uint64_t &number) { JSHandle bigint = CreateBigint(thread, 2); // 2 : one int64_t bits need two uint32_t bits + RETURN_HANDLE_IF_ABRUPT_COMPLETION(BigInt, thread); uint32_t lowBits = static_cast(number & 0xffffffff); uint32_t highBits = static_cast((number >> DATEBITS) & 0xffffffff); bigint->SetDigit(0, lowBits); @@ -577,10 +588,15 @@ void BigInt::BigIntToInt64(JSThread *thread, JSHandle bigint, int { ASSERT(cValue != nullptr); ASSERT(lossless != nullptr); + if (bigint->IsBoolean()) { + bigint = JSHandle(thread, JSTaggedValue::ToBigInt(thread, bigint)); + } JSHandle bigInt64(thread, JSTaggedValue::ToBigInt64(thread, bigint)); RETURN_IF_ABRUPT_COMPLETION(thread); if (Equal(bigInt64.GetTaggedValue(), bigint.GetTaggedValue())) { *lossless = true; + } else { + *lossless = false; } *cValue = bigInt64->ToInt64(); } @@ -589,10 +605,15 @@ void BigInt::BigIntToUint64(JSThread *thread, JSHandle bigint, ui { ASSERT(cValue != nullptr); ASSERT(lossless != nullptr); + if (bigint->IsBoolean()) { + bigint = JSHandle(thread, JSTaggedValue::ToBigInt(thread, bigint)); + } JSHandle bigUint64(thread, JSTaggedValue::ToBigUint64(thread, bigint)); RETURN_IF_ABRUPT_COMPLETION(thread); if (Equal(bigUint64.GetTaggedValue(), bigint.GetTaggedValue())) { *lossless = true; + } else { + *lossless = false; } *cValue = bigUint64->ToUint64(); } @@ -610,6 +631,7 @@ JSHandle BigInt::CreateBigWords(JSThread *thread, bool sign, uint32_t si THROW_RANGE_ERROR_AND_RETURN(thread, "Maximum BigInt size exceeded", bigint); } JSHandle bigint = CreateBigint(thread, needLen); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(BigInt, thread); for (uint32_t index = 0; index < size; ++index) { uint32_t lowBits = static_cast(words[index] & 0xffffffff); uint32_t highBits = static_cast((words[index] >> DATEBITS) & 0xffffffff); @@ -687,6 +709,7 @@ JSHandle BigInt::BigintAdd(JSThread *thread, JSHandle x, JSHandl return BigintAdd(thread, y, x, resultSign); } JSHandle bigint = BigInt::CreateBigint(thread, x->GetLength() + 1); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(BigInt, thread); uint32_t bigintCarry = 0; uint32_t i = 0; while (i < y->GetLength()) { @@ -721,6 +744,7 @@ inline uint32_t BigIntHelper::AddHelper(uint32_t x, uint32_t y, uint32_t &bigint JSHandle BigInt::BigintSub(JSThread *thread, JSHandle x, JSHandle y, bool resultSign) { JSHandle bigint = BigInt::CreateBigint(thread, x->GetLength()); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(BigInt, thread); uint32_t bigintCarry = 0; uint32_t i = 0; while (i < y->GetLength()) { @@ -882,6 +906,7 @@ JSHandle BigInt::RightShiftHelper(JSThread *thread, JSHandle x, JudgeRoundDown(x, digitMove, bitsMove, needLen, roundDown); } JSHandle bigint = CreateBigint(thread, needLen); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(BigInt, thread); RightShift(bigint, x, digitMove, bitsMove); bigint = BigIntHelper::RightTruncate(thread, bigint); @@ -905,15 +930,17 @@ JSHandle BigInt::LeftShift(JSThread *thread, JSHandle x, JSHandl JSHandle BigInt::LeftShiftHelper(JSThread *thread, JSHandle x, JSHandle y) { - ASSERT(y->GetLength() == 1); - ASSERT(y->GetDigit(0) <= MAXBITS); + if (x->IsZero()) { + return x; + } + ASSERT(y->GetLength() > 0); uint32_t moveNum = y->GetDigit(0); uint32_t digitMove = moveNum / DATEBITS; uint32_t bitsMove = moveNum % DATEBITS; // If bitsMove is not zero, needLen needs to be increased by 1 uint32_t needLen = digitMove + x->GetLength() + static_cast(!!bitsMove); - ASSERT(needLen < MAXSIZE); JSHandle bigint = CreateBigint(thread, needLen); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(BigInt, thread); if (bitsMove == 0) { uint32_t index = digitMove; while (index < needLen) { @@ -948,6 +975,7 @@ JSHandle BigInt::Copy(JSThread *thread, JSHandle x, uint32_t len { ASSERT(x->GetLength() >= len); JSHandle newBig = CreateBigint(thread, len); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(BigInt, thread); std::copy(x->GetData(), x->GetData() + len, newBig->GetData()); newBig->SetSign(x->GetSign()); return newBig; @@ -983,20 +1011,26 @@ JSHandle BigInt::Exponentiate(JSThread *thread, JSHandle base, J JSHandle bigint(thread, JSTaggedValue::Exception()); THROW_RANGE_ERROR_AND_RETURN(thread, "Exponent must be positive", bigint); } - ASSERT(exponent->GetLength() == 1); + ASSERT(exponent->GetLength() > 0); if (exponent->IsZero()) { return Int32ToBigInt(thread, 1); } - uint32_t expValue = exponent->GetDigit(0); - if (base->IsZero() || expValue == 1) { + if (base->IsZero()) { return base; } + uint32_t expValue = exponent->GetDigit(0); if (base->GetLength() == 1 && base->GetDigit(0) == 1) { if (base->GetSign() && !(expValue & 1)) { return BigInt::UnaryMinus(thread, base); } return base; } + if (exponent->GetLength() > 1) { + // The result is at least 2n ** 2n ** 32n, which is too big. + JSHandle bigint(thread, JSTaggedValue::Exception()); + THROW_RANGE_ERROR_AND_RETURN(thread, "Maximum BigInt size exceeded", bigint); + } + if (base->GetLength() == 1 && base->GetDigit(0) == 2) { // 2 : We use fast path processing 2 ^ n uint32_t needLength = expValue / DATEBITS + 1; JSHandle bigint = CreateBigint(thread, needLength); @@ -1016,11 +1050,13 @@ JSHandle BigInt::Exponentiate(JSThread *thread, JSHandle base, J expValue >>= 1; for (; expValue; expValue >>= 1) { temp.Update(BigInt::Multiply(thread, temp, temp)); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(BigInt, thread); if (expValue & 1) { if (result.GetTaggedValue().IsNull()) { result.Update(temp); } else { result.Update(BigInt::Multiply(thread, result, temp)); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(BigInt, thread); } } } @@ -1058,7 +1094,7 @@ JSHandle BigInt::Multiply(JSThread *thread, JSHandle x, JSHandle } uint32_t needLength = x->GetLength() + y->GetLength(); JSHandle bigint = BigInt::CreateBigint(thread, needLength); - + RETURN_HANDLE_IF_ABRUPT_COMPLETION(BigInt, thread); // the algorithm here is similar to the way we use paper money to calculate multiplication. // Generally, we first calculate the partial product, and then add up to get the result. // The only difference here is that multiplication and addition are calculated synchronously @@ -1197,6 +1233,7 @@ JSHandle BigInt::FormatLeftShift(JSThread *thread, uint32_t shift, JSHan needLen += 1; } JSHandle result = CreateBigint(thread, needLen); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(BigInt, thread); if (shift == 0) { std::copy(bigint->GetData(), bigint->GetData() + len, result->GetData()); } else { @@ -1298,6 +1335,7 @@ JSHandle BigInt::DivideAndRemainderWithBigintDivisor(JSThread *thread, J JSMutableHandle quotient(thread, JSTaggedValue::Null()); if (remainder.GetTaggedValue().IsNull()) { quotient.Update(CreateBigint(thread, quotientLen + 1)); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(BigInt, thread); } // format the divisor and dividend so that the highest order of the divisor is // greater than or equal to half of uint32_t @@ -1306,6 +1344,7 @@ JSHandle BigInt::DivideAndRemainderWithBigintDivisor(JSThread *thread, J JSHandle u = FormatLeftShift(thread, leadingZeros, dividend, true); // qv is used to store the result of quotient * divisor of each round JSHandle qv = CreateBigint(thread, divisorLen + 1); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(BigInt, thread); uint32_t vHighest = v->GetDigit(divisorLen - 1); for (int i = static_cast(quotientLen); i >= 0; --i) { uint32_t currentUHighest = u->GetDigit(i + divisorLen); @@ -1354,6 +1393,7 @@ JSHandle BigInt::DivideAndRemainderWithUint32Divisor(JSThread *thread, J } } else { quotient.Update(CreateBigint(thread, dividend->GetLength())); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(BigInt, thread); for (int i = static_cast(dividend->GetLength()) - 1; i >= 0; --i) { uint32_t q = DivideAndRemainder(r, dividend->GetDigit(i), divisor, r); quotient->SetDigit(i, q); @@ -1430,6 +1470,7 @@ JSHandle BigInt::Remainder(JSThread *thread, JSHandle n, JSHandl JSHandle BigInt::FloorMod(JSThread *thread, JSHandle leftVal, JSHandle rightVal) { JSHandle remainder = Remainder(thread, leftVal, rightVal); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(BigInt, thread); if (leftVal->GetSign() && !remainder->IsZero()) { return Add(thread, remainder, rightVal); } diff --git a/ecmascript/js_collator.cpp b/ecmascript/js_collator.cpp index 31526297b9391912351fd0702017b1fd34de5c5d..4691cfb60bdeada3205892a3141ca7557aef5c06 100644 --- a/ecmascript/js_collator.cpp +++ b/ecmascript/js_collator.cpp @@ -19,6 +19,7 @@ #include "ecmascript/global_env.h" #include "ecmascript/mem/c_string.h" #include "ecmascript/mem/barriers-inl.h" +#include "ecmascript/object_factory-inl.h" #include "unicode/udata.h" @@ -37,12 +38,23 @@ const std::map JSCollator::uColAttributeVal {CaseFirstOption::UNDEFINED, UCOL_OFF} }; -JSHandle JSCollator::GetAvailableLocales(JSThread *thread) +JSHandle JSCollator::GetAvailableLocales(JSThread *thread, bool enableLocaleCache) { const char *key = nullptr; const char *path = JSCollator::uIcuDataColl.c_str(); + // key and path are const, so we can cache the result + if (enableLocaleCache) { + JSHandle cachedLocales = thread->GlobalConstants()->GetHandledCachedJSCollatorLocales(); + if (cachedLocales->IsHeapObject()) { + return JSHandle(cachedLocales); + } + } std::vector availableStringLocales = intl::LocaleHelper::GetAvailableLocales(thread, key, path); JSHandle availableLocales = JSLocale::ConstructLocaleList(thread, availableStringLocales); + if (enableLocaleCache) { + GlobalEnvConstants *constants = const_cast(thread->GlobalConstants()); + constants->SetCachedLocales(availableLocales.GetTaggedValue()); + } return availableLocales; } @@ -68,7 +80,8 @@ JSHandle JSCollator::InitializeCollator(JSThread *thread, const JSHandle &collator, const JSHandle &locales, const JSHandle &options, - bool forIcuCache) + bool forIcuCache, + bool enableLocaleCache) { EcmaVM *ecmaVm = thread->GetEcmaVM(); ObjectFactory *factory = ecmaVm->GetFactory(); @@ -143,12 +156,14 @@ JSHandle JSCollator::InitializeCollator(JSThread *thread, if (requestedLocales->GetLength() == 0) { availableLocales = factory->EmptyArray(); } else { - availableLocales = GetAvailableLocales(thread); + availableLocales = GetAvailableLocales(thread, enableLocaleCache); } ResolvedLocale r = JSLocale::ResolveLocale(thread, availableLocales, requestedLocales, matcher, relevantExtensionKeys); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSCollator, thread); icu::Locale icuLocale = r.localeData; JSHandle localeStr = intl::LocaleHelper::ToLanguageTag(thread, icuLocale); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSCollator, thread); collator->SetLocale(thread, localeStr.GetTaggedValue()); ASSERT_PRINT(!icuLocale.isBogus(), "icuLocale is bogus"); @@ -403,6 +418,7 @@ JSHandle JSCollator::ResolvedOptions(JSThread *thread, const JSHandle< JSHandle property = globalConst->GetHandledLocaleString(); JSHandle locale(thread, collator->GetLocale()); JSObject::CreateDataPropertyOrThrow(thread, options, property, locale); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread); // [[Usage]] UsageOption usageOption = collator->GetUsage(); @@ -460,4 +476,39 @@ JSTaggedValue JSCollator::CompareStrings(const icu::Collator *icuCollator, const return JSTaggedValue(result); } + +JSTaggedValue JSCollator::FastCompareStrings(JSThread *thread, const icu::Collator *icuCollator, + const JSHandle &string1, + const JSHandle &string2) +{ + if (*string1 == *string2) { + return JSTaggedValue(UCollationResult::UCOL_EQUAL); + } + + auto flatString1 = JSHandle(thread, EcmaStringAccessor::Flatten(thread->GetEcmaVM(), string1)); + auto flatString2 = JSHandle(thread, EcmaStringAccessor::Flatten(thread->GetEcmaVM(), string2)); + + UCollationResult result; + UErrorCode status = U_ZERO_ERROR; + { + DISALLOW_GARBAGE_COLLECTION; + CString str1 = ConvertToString(*flatString1, StringConvertedUsage::LOGICOPERATION); + icu::StringPiece stringPiece1(str1.c_str()); + if (!stringPiece1.empty()) { + CString str2 = ConvertToString(*flatString2, StringConvertedUsage::LOGICOPERATION); + icu::StringPiece stringPiece2(str2.c_str()); + if (!stringPiece2.empty()) { + result = icuCollator->compareUTF8(stringPiece1, stringPiece2, status); + return JSTaggedValue(result); + } + } + + icu::UnicodeString uString1 = EcmaStringToUString(flatString1); + icu::UnicodeString uString2 = EcmaStringToUString(flatString2); + + result = icuCollator->compare(uString1, uString2, status); + ASSERT(U_SUCCESS(status)); + } + return JSTaggedValue(result); +} } // namespace panda::ecmascript diff --git a/ecmascript/js_collator.h b/ecmascript/js_collator.h index f265c9fd6f88cdcdc1979a1e7efc0acda478c0fd..98c7882413396c4255eb6dc13153375aabe46d79 100644 --- a/ecmascript/js_collator.h +++ b/ecmascript/js_collator.h @@ -84,18 +84,23 @@ public: static JSHandle InitializeCollator(JSThread *thread, const JSHandle &collator, const JSHandle &locales, const JSHandle &options, - bool forIcuCache = false); + bool forIcuCache = false, + bool enableLocaleCache = false); static icu::Collator *GetCachedIcuCollator(JSThread *thread, const JSHandle &locales); // 11.3.4 Intl.Collator.prototype.resolvedOptions () static JSHandle ResolvedOptions(JSThread *thread, const JSHandle &collator); - static JSHandle GetAvailableLocales(JSThread *thread); + static JSHandle GetAvailableLocales(JSThread *thread, bool enableLocaleCache = false); static JSTaggedValue CompareStrings(const icu::Collator *icuCollator, const JSHandle &string1, const JSHandle &string2); + static JSTaggedValue FastCompareStrings(JSThread *thread, const icu::Collator *icuCollator, + const JSHandle &string1, + const JSHandle &string2); + private: static CaseFirstOption StringToCaseFirstOption(const std::string &str); diff --git a/ecmascript/js_date_time_format.cpp b/ecmascript/js_date_time_format.cpp index 29ec29db574de7af5cba303c9ca556a3b5fe75cd..8bc273c1a08d172496c518f8886610457b102fde 100644 --- a/ecmascript/js_date_time_format.cpp +++ b/ecmascript/js_date_time_format.cpp @@ -23,7 +23,7 @@ #include "ecmascript/js_intl.h" #include "ecmascript/js_locale.h" #include "ecmascript/js_object-inl.h" -#include "ecmascript/object_factory.h" +#include "ecmascript/object_factory-inl.h" namespace panda::ecmascript { struct CommonDateFormatPart { @@ -492,6 +492,7 @@ JSHandle JSDateTimeFormat::InitializeDateTimeFormat(JSThread * } } JSHandle localeStr = intl::LocaleHelper::ToLanguageTag(thread, resolvedIcuLocaleCopy); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDateTimeFormat, thread); dateTimeFormat->SetLocale(thread, localeStr.GetTaggedValue()); // Set dateTimeFormat.[[boundFormat]]. @@ -687,6 +688,7 @@ JSHandle JSDateTimeFormat::FormatDateTimeToParts(JSThread *thread, // 2. Let result be ArrayCreate(0). JSHandle result(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread); if (formattedParts.isBogus()) { return result; } @@ -843,6 +845,7 @@ void JSDateTimeFormat::ResolvedOptions(JSThread *thread, const JSHandle locale(thread, dateTimeFormat->GetLocale()); JSHandle property = globalConst->GetHandledLocaleString(); JSObject::CreateDataPropertyOrThrow(thread, options, property, locale); + RETURN_IF_ABRUPT_COMPLETION(thread); // [[Calendar]] JSMutableHandle calendarValue(thread, dateTimeFormat->GetCalendar()); icu::SimpleDateFormat *icuSimpleDateFormat = dateTimeFormat->GetIcuSimpleDateFormat(); @@ -859,6 +862,7 @@ void JSDateTimeFormat::ResolvedOptions(JSThread *thread, const JSHandleGetHandledCalendarString(); JSObject::CreateDataPropertyOrThrow(thread, options, property, calendarValue); + RETURN_IF_ABRUPT_COMPLETION(thread); // [[NumberingSystem]] JSHandle numberingSystem(thread, dateTimeFormat->GetNumberingSystem()); if (numberingSystem->IsUndefined()) { @@ -866,6 +870,7 @@ void JSDateTimeFormat::ResolvedOptions(JSThread *thread, const JSHandleGetHandledNumberingSystemString(); JSObject::CreateDataPropertyOrThrow(thread, options, property, numberingSystem); + RETURN_IF_ABRUPT_COMPLETION(thread); // [[TimeZone]] JSMutableHandle timezoneValue(thread, dateTimeFormat->GetTimeZone()); const icu::TimeZone &icuTZ = calendar->getTimeZone(); @@ -884,6 +889,7 @@ void JSDateTimeFormat::ResolvedOptions(JSThread *thread, const JSHandleGetHandledTimeZoneString(); JSObject::CreateDataPropertyOrThrow(thread, options, property, timezoneValue); + RETURN_IF_ABRUPT_COMPLETION(thread); // [[HourCycle]] // For web compatibility reasons, if the property "hourCycle" is set, the "hour12" property should be set to true // when "hourCycle" is "h11" or "h12", or to false when "hourCycle" is "h23" or "h24". @@ -894,6 +900,7 @@ void JSDateTimeFormat::ResolvedOptions(JSThread *thread, const JSHandleGetHandledHourCycleString(); hcValue = ToHourCycleEcmaString(thread, dateTimeFormat->GetHourCycle()); JSObject::CreateDataPropertyOrThrow(thread, options, property, hcValue); + RETURN_IF_ABRUPT_COMPLETION(thread); if (hc == HourCycleOption::H11 || hc == HourCycleOption::H12) { JSHandle trueValue(thread, JSTaggedValue::True()); hcValue = trueValue; @@ -903,6 +910,7 @@ void JSDateTimeFormat::ResolvedOptions(JSThread *thread, const JSHandleGetHandledHour12String(); JSObject::CreateDataPropertyOrThrow(thread, options, property, hcValue); + RETURN_IF_ABRUPT_COMPLETION(thread); } // [[DateStyle]], [[TimeStyle]]. icu::UnicodeString patternUnicode; @@ -920,6 +928,7 @@ void JSDateTimeFormat::ResolvedOptions(JSThread *thread, const JSHandle fsdValue(thread, JSTaggedValue(fsd)); property = globalConst->GetHandledFractionalSecondDigitsString(); JSObject::CreateDataPropertyOrThrow(thread, options, property, fsdValue); + RETURN_IF_ABRUPT_COMPLETION(thread); } } property = JSHandle::Cast(factory->NewFromStdString(item.property)); @@ -927,6 +936,7 @@ void JSDateTimeFormat::ResolvedOptions(JSThread *thread, const JSHandle::Cast(factory->NewFromStdString(pair.second)); JSObject::CreateDataPropertyOrThrow(thread, options, property, hcValue); + RETURN_IF_ABRUPT_COMPLETION(thread); break; } } @@ -1006,6 +1016,7 @@ JSHandle JSDateTimeFormat::NormDateTimeRangeToParts(JSThread *thread, c double x, double y) { JSHandle result(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread); // 1. Let x be TimeClip(x). x = JSDate::TimeClip(x); // 2. If x is NaN, throw a RangeError exception. @@ -1046,6 +1057,7 @@ JSHandle JSDateTimeFormat::ConstructFDateIntervalToJSArray(JSThread *th icu::UnicodeString formattedValue = formatted.toTempString(status); // Let result be ArrayCreate(0). JSHandle array(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread); // Let index be 0. int index = 0; int32_t preEndPos = 0; @@ -1117,6 +1129,7 @@ JSHandle JSDateTimeFormat::ConstructFDateIntervalToJSArray(JSThread *th JSHandle value = JSHandle::Cast( ToValueString(thread, TrackValue(part.fBeginIndex, part.fEndIndex, begin, end))); JSObject::SetProperty(thread, element, thread->GlobalConstants()->GetHandledSourceString(), value, true); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread); } return array; } @@ -1245,7 +1258,7 @@ std::unique_ptr JSDateTimeFormat::BuildCalendar(const icu::Locale UErrorCode status = U_ZERO_ERROR; std::unique_ptr calendar(icu::Calendar::createInstance(timeZone, locale, status)); if (U_FAILURE(status) || calendar == nullptr) { - return nullptr; + return nullptr; } ASSERT_PRINT(U_SUCCESS(status), "buildCalendar failed"); ASSERT_PRINT(calendar.get() != nullptr, "calendar is nullptr"); diff --git a/ecmascript/js_displaynames.cpp b/ecmascript/js_displaynames.cpp index c43377262606dcfb03e5c4d7be44dd76dd65bb57..364b79b0192061b5330fa0763a8a1c14dbdd79d5 100644 --- a/ecmascript/js_displaynames.cpp +++ b/ecmascript/js_displaynames.cpp @@ -20,6 +20,7 @@ #include "ecmascript/intl/locale_helper.h" #include "ecmascript/global_env.h" #include "ecmascript/global_env_constants.h" +#include "ecmascript/object_factory-inl.h" #include "unicode/errorcode.h" #include "unicode/locdspnm.h" @@ -193,6 +194,7 @@ JSHandle JSDisplayNames::InitializeDisplayNames(JSThread *thread // 18. Set displayNames.[[Locale]] to the value of r.[[Locale]]. JSHandle localeStr = intl::LocaleHelper::ToLanguageTag(thread, icuLocale); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSDisplayNames, thread); displayNames->SetLocale(thread, localeStr.GetTaggedValue()); // 19. Let dataLocale be r.[[dataLocale]]. // 20. Let dataLocaleData be localeData.[[]]. @@ -256,6 +258,7 @@ JSHandle JSDisplayNames::CanonicalCodeForDisplayNames(JSThread *thre THROW_TYPE_ERROR_AND_RETURN(thread, "not a structurally valid", code); } JSHandle codeStr = intl::LocaleHelper::CanonicalizeUnicodeLocaleId(thread, code); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(EcmaString, thread); icu::LocaleDisplayNames *icuLocaldisplaynames = displayNames->GetIcuLocaleDisplayNames(); icu::UnicodeString result; std::string codeString = intl::LocaleHelper::ConvertToStdString(codeStr); @@ -378,23 +381,27 @@ void JSDisplayNames::ResolvedOptions(JSThread *thread, const JSHandle propertyKey = globalConst->GetHandledLocaleString(); JSHandle locale(thread, displayNames->GetLocale()); JSObject::CreateDataPropertyOrThrow(thread, options, propertyKey, locale); + RETURN_IF_ABRUPT_COMPLETION(thread); // [[Style]] StyOption style = displayNames->GetStyle(); propertyKey = globalConst->GetHandledStyleString(); JSHandle styleString = StyOptionToEcmaString(thread, style); JSObject::CreateDataPropertyOrThrow(thread, options, propertyKey, styleString); + RETURN_IF_ABRUPT_COMPLETION(thread); // [[type]] TypednsOption type = displayNames->GetType(); propertyKey = globalConst->GetHandledTypeString(); JSHandle typeString = TypeOptionToEcmaString(thread, type); JSObject::CreateDataPropertyOrThrow(thread, options, propertyKey, typeString); + RETURN_IF_ABRUPT_COMPLETION(thread); // [[fallback]] FallbackOption fallback = displayNames->GetFallback(); propertyKey = globalConst->GetHandledFallbackString(); JSHandle fallbackString = FallbackOptionToEcmaString(thread, fallback); JSObject::CreateDataPropertyOrThrow(thread, options, propertyKey, fallbackString); + RETURN_IF_ABRUPT_COMPLETION(thread); } } // namespace panda::ecmascript diff --git a/ecmascript/js_for_in_iterator.cpp b/ecmascript/js_for_in_iterator.cpp index a39828abe89bfe2cb0210b7c17abddcfb5d072fe..cbb3477de877e94460c613d6514cb66f5e4b5fe6 100644 --- a/ecmascript/js_for_in_iterator.cpp +++ b/ecmascript/js_for_in_iterator.cpp @@ -143,6 +143,7 @@ std::pair JSForInIterator::NextInternal(JSThread *thread, c visited.Update(JSTaggedValue(newQueue)); it->SetVisitedObjs(thread, visited); JSTaggedValue proto = JSTaggedValue::GetPrototype(thread, object); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, std::make_pair(JSTaggedValue::Exception(), false)); it->SetObject(thread, proto); it->SetWasVisited(false); it->SetHasVisitObjs(true); diff --git a/ecmascript/js_function.cpp b/ecmascript/js_function.cpp index 6b4cf5686f35780d53ae7e865b1ef137569c30e8..41e7facbf4b56287c657057f4b45cc259f6f9f71 100644 --- a/ecmascript/js_function.cpp +++ b/ecmascript/js_function.cpp @@ -24,7 +24,6 @@ #include "ecmascript/jspandafile/class_info_extractor.h" #include "ecmascript/js_handle.h" #include "ecmascript/js_promise.h" -#include "ecmascript/js_proxy.h" #include "ecmascript/js_tagged_value-inl.h" #include "ecmascript/mem/c_containers.h" #include "ecmascript/module/js_module_source_text.h" @@ -38,7 +37,6 @@ void JSFunction::InitializeJSFunction(JSThread *thread, const JSHandleSetProtoOrHClass(thread, JSTaggedValue::Hole(), SKIP_BARRIER); func->SetHomeObject(thread, JSTaggedValue::Undefined(), SKIP_BARRIER); func->SetLexicalEnv(thread, JSTaggedValue::Undefined(), SKIP_BARRIER); - func->SetModule(thread, JSTaggedValue::Undefined(), SKIP_BARRIER); func->SetMethod(thread, JSTaggedValue::Undefined(), SKIP_BARRIER); auto globalConst = thread->GlobalConstants(); @@ -49,15 +47,21 @@ void JSFunction::InitializeJSFunction(JSThread *thread, const JSHandleSetPropertyInlinedProps(thread, PROTOTYPE_INLINE_PROPERTY_INDEX, accessor.GetTaggedValue()); accessor = globalConst->GetHandledFunctionNameAccessor(); func->SetPropertyInlinedProps(thread, NAME_INLINE_PROPERTY_INDEX, accessor.GetTaggedValue()); - JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - JSHandle objFun(env->GetObjectFunction()); - JSHandle initialGeneratorFuncPrototype = factory->NewJSObjectByConstructor(objFun); if (kind == FunctionKind::ASYNC_GENERATOR_FUNCTION) { + // Not duplicate codes, it will slow the performace if combining and put outside! + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle objFun(env->GetObjectFunction()); + JSHandle initialGeneratorFuncPrototype = factory->NewJSObjectByConstructor(objFun); JSObject::SetPrototype(thread, initialGeneratorFuncPrototype, env->GetAsyncGeneratorPrototype()); func->SetProtoOrHClass(thread, initialGeneratorFuncPrototype); } if (kind == FunctionKind::GENERATOR_FUNCTION) { + // Not duplicate codes, it will slow the performace if combining and put outside! + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle objFun(env->GetObjectFunction()); + JSHandle initialGeneratorFuncPrototype = factory->NewJSObjectByConstructor(objFun); JSObject::SetPrototype(thread, initialGeneratorFuncPrototype, env->GetGeneratorPrototype()); func->SetProtoOrHClass(thread, initialGeneratorFuncPrototype); } @@ -238,6 +242,7 @@ bool JSFunction::MakeConstructor(JSThread *thread, const JSHandle &f PropertyDescriptor constructorDesc(thread, JSHandle::Cast(func), writable, false, true); status = JSTaggedValue::DefinePropertyOrThrow(thread, proto, constructorKey, constructorDesc); } + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); ASSERT_PRINT(status, "DefineProperty construct failed"); // func.prototype = proto @@ -262,11 +267,13 @@ JSTaggedValue JSFunction::Call(EcmaRuntimeCallInfo *info) // 2. If argumentsList was not passed, let argumentsList be a new empty List. // 3. If IsCallable(F) is false, throw a TypeError exception. if (!func->IsCallable()) { + RETURN_STACK_BEFORE_THROW_IF_ASM(thread); THROW_TYPE_ERROR_AND_RETURN(thread, "Callable is false", JSTaggedValue::Exception()); } auto *hclass = func->GetTaggedObject()->GetClass(); if (hclass->IsClassConstructor()) { + RETURN_STACK_BEFORE_THROW_IF_ASM(thread); THROW_TYPE_ERROR_AND_RETURN(thread, "class constructor cannot call", JSTaggedValue::Exception()); } return EcmaInterpreter::Execute(info); @@ -286,6 +293,7 @@ JSTaggedValue JSFunction::Construct(EcmaRuntimeCallInfo *info) info->SetNewTarget(target.GetTaggedValue()); } if (!(func->IsConstructor() && target->IsConstructor())) { + RETURN_STACK_BEFORE_THROW_IF_ASM(thread); THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor is false", JSTaggedValue::Exception()); } @@ -399,6 +407,7 @@ JSTaggedValue JSFunction::InvokeOptimizedEntrypoint(JSThread *thread, JSHandleIsFastCall()) { if (needPushUndefined) { info = EcmaInterpreter::ReBuildRuntimeCallInfo(thread, info, numArgs); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); } JSTaggedType *stackArgs = info->GetArgs(); stackArgs[1] = stackArgs[0]; @@ -425,6 +434,7 @@ JSTaggedValue JSFunction::ConstructInternal(EcmaRuntimeCallInfo *info) JSHandle newTarget(info->GetNewTarget()); ASSERT(newTarget->IsECMAObject()); if (!func->IsConstructor()) { + RETURN_STACK_BEFORE_THROW_IF_ASM(thread); THROW_TYPE_ERROR_AND_RETURN(thread, "Constructor is false", JSTaggedValue::Exception()); } @@ -464,6 +474,7 @@ JSTaggedValue JSFunction::ConstructInternal(EcmaRuntimeCallInfo *info) } if (!resultValue.IsUndefined()) { + RETURN_STACK_BEFORE_THROW_IF_ASM(thread); THROW_TYPE_ERROR_AND_RETURN(thread, "function is non-constructor", JSTaggedValue::Exception()); } return obj.GetTaggedValue(); @@ -585,6 +596,7 @@ void JSProxyRevocFunction::ProxyRevocFunctions(const JSThread *thread, const JSH // 5 ~ 6 Set internal slot of p to null. proxyHandle->SetTarget(thread, JSTaggedValue::Null()); proxyHandle->SetHandler(thread, JSTaggedValue::Null()); + proxyHandle->SetIsRevoked(true); } JSTaggedValue JSFunction::AccessCallerArgumentsThrowTypeError(EcmaRuntimeCallInfo *argv) @@ -792,22 +804,4 @@ JSTaggedValue JSFunction::GetNativeFunctionExtraInfo() const } return JSTaggedValue::Undefined(); } - -JSTaggedValue JSFunction::GetRecordName() const -{ - JSTaggedValue module = GetModule(); - if (module.IsSourceTextModule()) { - JSTaggedValue recordName = SourceTextModule::Cast(module.GetTaggedObject())->GetEcmaModuleRecordName(); - if (!recordName.IsString()) { - LOG_INTERPRETER(DEBUG) << "module record name is undefined"; - return JSTaggedValue::Hole(); - } - return recordName; - } - if (module.IsString()) { - return module; - } - LOG_INTERPRETER(DEBUG) << "record name is undefined"; - return JSTaggedValue::Hole(); -} } // namespace panda::ecmascript diff --git a/ecmascript/js_function.h b/ecmascript/js_function.h index 52dbd2ba63cfcfa217657dbb85c8484e6e99f717..588ecc2d8a0fcf2de0120ee6caf155a4645f1460 100644 --- a/ecmascript/js_function.h +++ b/ecmascript/js_function.h @@ -20,6 +20,7 @@ #include "ecmascript/ecma_macros.h" #include "ecmascript/js_object-inl.h" #include "ecmascript/lexical_env.h" +#include "ecmascript/js_proxy.h" namespace panda::ecmascript { class JSThread; @@ -57,6 +58,12 @@ public: return Method::ConstCast(method.GetTaggedObject())->GetFunctionKind(); } + JSTaggedValue GetModule() const + { + JSTaggedValue method = GetMethod(); + return Method::ConstCast(method.GetTaggedObject())->GetModule(); + } + static constexpr size_t METHOD_OFFSET = JSObject::SIZE; ACCESSORS(Method, METHOD_OFFSET, LAST_OFFSET) DEFINE_ALIGN_SIZE(LAST_OFFSET); @@ -66,6 +73,7 @@ public: }; static_assert((JSFunctionBase::SIZE % static_cast(MemAlignment::MEM_ALIGN_OBJECT)) == 0); +static_assert(JSFunctionBase::METHOD_OFFSET == JSProxy::METHOD_OFFSET); class JSFunction : public JSFunctionBase { public: @@ -225,8 +233,7 @@ public: static constexpr size_t PROTO_OR_DYNCLASS_OFFSET = JSFunctionBase::SIZE; ACCESSORS(ProtoOrHClass, PROTO_OR_DYNCLASS_OFFSET, LEXICAL_ENV_OFFSET) ACCESSORS(LexicalEnv, LEXICAL_ENV_OFFSET, HOME_OBJECT_OFFSET) - ACCESSORS(HomeObject, HOME_OBJECT_OFFSET, ECMA_MODULE_OFFSET) - ACCESSORS(Module, ECMA_MODULE_OFFSET, LAST_OFFSET) + ACCESSORS(HomeObject, HOME_OBJECT_OFFSET, LAST_OFFSET) DEFINE_ALIGN_SIZE(LAST_OFFSET); DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSFunctionBase, PROTO_OR_DYNCLASS_OFFSET, SIZE) diff --git a/ecmascript/js_hclass-inl.h b/ecmascript/js_hclass-inl.h index dae4710f10de62f91bf3a935b5d895de3b130aea..034d72982c2f58156360e3a87522410e05e4a04f 100644 --- a/ecmascript/js_hclass-inl.h +++ b/ecmascript/js_hclass-inl.h @@ -33,6 +33,7 @@ inline JSHClass *JSHClass::Cast(const TaggedObject *object) void JSHClass::AddTransitions(const JSThread *thread, const JSHandle &parent, const JSHandle &child, const JSHandle &key, PropertyAttributes attributes) { + UpdateRootHClass(thread, parent, child); JSTaggedValue transitions = parent->GetTransitions(); if (transitions.IsUndefined()) { JSTaggedValue weakChild = JSTaggedValue(child.GetTaggedValue().CreateAndGetWeakRef()); @@ -68,6 +69,7 @@ void JSHClass::AddProtoTransitions(const JSThread *thread, const JSHandle &child, const JSHandle &key, const JSHandle &proto) { + UpdateRootHClass(thread, parent, child); JSTaggedValue transitions = parent->GetTransitions(); JSMutableHandle dict(thread, JSTaggedValue::Undefined()); if (transitions.IsUndefined()) { @@ -186,6 +188,7 @@ inline size_t JSHClass::SizeFromJSHClass(TaggedObject *header) case JSType::AOT_LITERAL_INFO: case JSType::VTABLE: case JSType::COW_TAGGED_ARRAY: + case JSType::PROFILE_TYPE_INFO: size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), reinterpret_cast(header)->GetLength()); break; @@ -206,6 +209,10 @@ inline size_t JSHClass::SizeFromJSHClass(TaggedObject *header) size = TreeEcmaString::SIZE; size = AlignUp(size, static_cast(MemAlignment::MEM_ALIGN_OBJECT)); break; + case JSType::SLICED_STRING: + size = SlicedString::SIZE; + size = AlignUp(size, static_cast(MemAlignment::MEM_ALIGN_OBJECT)); + break; case JSType::MACHINE_CODE_OBJECT: size = reinterpret_cast(header)->GetMachineCodeObjectSize(); size = AlignUp(size, static_cast(MemAlignment::MEM_ALIGN_OBJECT)); @@ -230,9 +237,32 @@ inline void JSHClass::Copy(const JSThread *thread, const JSHClass *jshclass) // copy jshclass SetPrototype(thread, jshclass->GetPrototype()); SetBitField(jshclass->GetBitField()); + SetIsAllTaggedProp(jshclass->IsAllTaggedProp()); SetNumberOfProps(jshclass->NumberOfProps()); } +inline JSHClass *JSHClass::FindRootHClass(JSHClass *hclass) +{ + auto root = hclass; + auto parent = hclass->GetParent(); + while (parent.IsJSHClass()) { + root = JSHClass::Cast(parent.GetTaggedObject()); + parent = root->GetParent(); + } + return root; +} + +inline void JSHClass::UpdateRootHClass(const JSThread *thread, const JSHandle &parent, + const JSHandle &child) +{ + auto rootHClass = parent->GetParent(); + if (rootHClass.IsJSHClass()) { + child->SetParent(thread, rootHClass); + } else { + child->SetParent(thread, parent); + } +} + inline int JSHClass::FindPropertyEntry(const JSThread *thread, JSHClass *hclass, JSTaggedValue key) { DISALLOW_GARBAGE_COLLECTION; diff --git a/ecmascript/js_hclass.cpp b/ecmascript/js_hclass.cpp index 326623f3dea2d3e268f170a3a05047c7623d5ef2..05f3fd41895ee4ecaef76ff748dd64ab8d33bba0 100644 --- a/ecmascript/js_hclass.cpp +++ b/ecmascript/js_hclass.cpp @@ -13,12 +13,15 @@ * limitations under the License. */ +#include "ecmascript/elements.h" #include "ecmascript/js_hclass-inl.h" #include #include "ecmascript/base/config.h" #include "ecmascript/global_env.h" +#include "ecmascript/pgo_profiler/pgo_profiler.h" +#include "ecmascript/tagged_array.h" #include "ecmascript/vtable.h" #include "ecmascript/ic/proto_change_details.h" #include "ecmascript/js_object-inl.h" @@ -148,8 +151,10 @@ void JSHClass::Initialize(const JSThread *thread, uint32_t size, JSType type, ui SetExtensible(true); SetIsPrototype(false); SetHasDeleteProperty(false); - SetElementRepresentation(Representation::NONE); + SetIsAllTaggedProp(true); + SetElementsKind(ElementsKind::GENERIC); SetTransitions(thread, JSTaggedValue::Undefined()); + SetParent(thread, JSTaggedValue::Undefined()); SetProtoChangeMarker(thread, JSTaggedValue::Null()); SetProtoChangeDetails(thread, JSTaggedValue::Null()); SetEnumCache(thread, JSTaggedValue::Null()); @@ -181,6 +186,7 @@ JSHandle JSHClass::Clone(const JSThread *thread, const JSHandleCopy(thread, *jshclass); newJsHClass->SetTransitions(thread, JSTaggedValue::Undefined()); + newJsHClass->SetParent(thread, JSTaggedValue::Undefined()); newJsHClass->SetProtoChangeDetails(thread, JSTaggedValue::Null()); newJsHClass->SetEnumCache(thread, JSTaggedValue::Null()); // reuse Attributes first. @@ -207,6 +213,7 @@ void JSHClass::TransitionElementsToDictionary(const JSThread *thread, const JSHa } obj->GetJSHClass()->SetIsDictionaryElement(true); obj->GetJSHClass()->SetIsStableElements(false); + obj->GetJSHClass()->SetElementsKind(ElementsKind::GENERIC); } JSHandle JSHClass::SetPropertyOfObjHClass(const JSThread *thread, JSHandle &jshclass, @@ -245,7 +252,7 @@ void JSHClass::AddProperty(const JSThread *thread, const JSHandle &obj JSHandle jshclass(thread, obj->GetJSHClass()); JSHClass *newClass = jshclass->FindTransitions(key.GetTaggedValue(), JSTaggedValue(attr.GetPropertyMetaData())); if (newClass != nullptr) { - obj->SetClass(newClass); + obj->SynchronizedSetClass(newClass); #if ECMASCRIPT_ENABLE_IC JSHClass::NotifyHclassChanged(thread, jshclass, JSHandle(thread, newClass), key.GetTaggedValue()); #endif @@ -279,7 +286,7 @@ void JSHClass::AddProperty(const JSThread *thread, const JSHandle &obj #if ECMASCRIPT_ENABLE_IC JSHClass::NotifyHclassChanged(thread, jshclass, newJsHClass, key.GetTaggedValue()); #endif - obj->SetClass(*newJsHClass); + obj->SynchronizedSetClass(*newJsHClass); // Maintaining subtyping is no longer required when transition succeeds. if (jshclass->HasTSSubtyping()) { @@ -406,7 +413,7 @@ void JSHClass::ShouldUpdateProtoClass(const JSThread *thread, const JSHandleGetTaggedObject())->SetClass(*newProtoClass); + JSObject::Cast(proto->GetTaggedObject())->SynchronizedSetClass(*newProtoClass); newProtoClass->SetIsPrototype(true); } } @@ -428,10 +435,99 @@ void JSHClass::TransitionToDictionary(const JSThread *thread, const JSHandle(thread, obj->GetJSHClass()), newJsHClass); #endif - obj->SetClass(newJsHClass); + obj->SynchronizedSetClass(*newJsHClass); } } +void JSHClass::TransitionForRepChange(const JSThread *thread, const JSHandle &receiver, + const JSHandle &key, PropertyAttributes attr) +{ + JSHandle oldHClass(thread, receiver->GetJSHClass()); + + // 1. Create hclass and copy layout + JSHandle newHClass = JSHClass::Clone(thread, oldHClass); + + JSHandle oldLayout(thread, newHClass->GetLayout()); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle newLayout(factory->CopyLayoutInfo(oldLayout)); + newHClass->SetLayout(thread, newLayout); + + // 2. update attr + auto hclass = JSHClass::Cast(newHClass.GetTaggedValue().GetTaggedObject()); + int entry = JSHClass::FindPropertyEntry(thread, hclass, key.GetTaggedValue()); + ASSERT(entry != -1); + newLayout->SetNormalAttr(thread, entry, attr); + + // 3. update hclass in object. +#if ECMASCRIPT_ENABLE_IC + JSHClass::NotifyHclassChanged(thread, oldHClass, newHClass, key.GetTaggedValue()); +#endif + + receiver->SynchronizedSetClass(*newHClass); + // 4. Maybe Transition And Maintain subtypeing check +} + +void JSHClass::TransitToElementsKind(const JSThread *thread, const JSHandle &array) +{ + JSTaggedValue elements = array->GetElements(); + if (!elements.IsTaggedArray()) { + return; + } + ElementsKind newKind = ElementsKind::NONE; + auto elementArray = TaggedArray::Cast(elements); + uint32_t length = elementArray->GetLength(); + for (uint32_t i = 0; i < length; i++) { + JSTaggedValue value = elementArray->Get(i); + newKind = Elements::ToElementsKind(value, newKind); + } + ElementsKind current = array->GetJSHClass()->GetElementsKind(); + if (newKind == current) { + return; + } + const auto &arrayHClassIndexMap = thread->GetArrayHClassIndexMap(); + auto newKindIter = arrayHClassIndexMap.find(newKind); + if (newKindIter != arrayHClassIndexMap.end()) { + auto index = static_cast(newKindIter->second); + auto hclassVal = thread->GlobalConstants()->GetGlobalConstantObject(index); + JSHClass *hclass = JSHClass::Cast(hclassVal.GetTaggedObject()); + array->SetClass(hclass); + } +} + +bool JSHClass::TransitToElementsKind( + const JSThread *thread, const JSHandle &object, const JSHandle &value, ElementsKind kind) +{ + if (!object->IsJSArray()) { + return false; + } + ElementsKind current = object->GetJSHClass()->GetElementsKind(); + if (Elements::IsGeneric(current)) { + return false; + } + auto newKind = Elements::ToElementsKind(value.GetTaggedValue(), kind); + // Merge current kind and new kind + newKind = Elements::MergeElementsKind(current, newKind); + if (newKind == current) { + return false; + } + const auto &arrayHClassIndexMap = thread->GetArrayHClassIndexMap(); + auto newKindIter = arrayHClassIndexMap.find(newKind); + if (newKindIter != arrayHClassIndexMap.end()) { + auto index = static_cast(newKindIter->second); + auto hclassVal = thread->GlobalConstants()->GetGlobalConstantObject(index); + JSHClass *hclass = JSHClass::Cast(hclassVal.GetTaggedObject()); + object->SetClass(hclass); + // Update TrackInfo + if (!thread->IsPGOProfilerEnable()) { + return true; + } + auto trackInfoVal = JSHandle(object)->GetTrackInfo(); + thread->GetEcmaVM()->GetPGOProfiler()->UpdateTrackInfo(trackInfoVal, newKind); + return true; + } + return false; +} + JSHandle JSHClass::EnableProtoChangeMarker(const JSThread *thread, const JSHandle &jshclass) { JSTaggedValue proto = jshclass->GetPrototype(); @@ -574,11 +670,14 @@ void JSHClass::MarkProtoChanged(const JSThread *thread, const JSHandle protoChangeMarker->SetHasChanged(true); } - if (jshclass->HasTSSubtyping() && addedKey.IsString()) { - JSHandle key(thread, addedKey); - if (!SubtypingOperator::TryMaintainTSSubtypingOnPrototype(thread, jshclass, key)) { - jshclass->InitTSInheritInfo(thread); + if (jshclass->HasTSSubtyping()) { + if (addedKey.IsString()) { + JSHandle key(thread, addedKey); + if (SubtypingOperator::TryMaintainTSSubtypingOnPrototype(thread, jshclass, key)) { + return; + } } + jshclass->InitTSInheritInfo(thread); } } @@ -611,7 +710,10 @@ void JSHClass::RefreshUsers(const JSThread *thread, const JSHandle &ol ASSERT(newHclass->IsPrototype()); bool onceRegistered = UnregisterOnProtoChain(thread, oldHclass); - newHclass->SetProtoChangeDetails(thread, oldHclass->GetProtoChangeDetails()); + // oldHclass is already marked. Only update newHclass.protoChangeDetails if it doesn't exist for further use. + if (!newHclass->GetProtoChangeDetails().IsProtoChangeDetails()) { + newHclass->SetProtoChangeDetails(thread, oldHclass->GetProtoChangeDetails()); + } oldHclass->SetProtoChangeDetails(thread, JSTaggedValue::Undefined()); if (onceRegistered) { if (newHclass->GetProtoChangeDetails().IsProtoChangeDetails()) { @@ -654,6 +756,7 @@ PropertyLookupResult JSHClass::LookupPropertyInAotHClass(const JSThread *thread, if (attr.IsAccessor()) { result.SetIsAccessor(true); } + result.SetRepresentation(attr.GetRepresentation()); result.SetIsWritable(attr.IsWritable()); return result; } @@ -680,6 +783,36 @@ PropertyLookupResult JSHClass::LookupPropertyInAotHClass(const JSThread *thread, return result; } +PropertyLookupResult JSHClass::LookupPropertyInBuiltinPrototypeHClass(const JSThread *thread, JSHClass *hclass, + JSTaggedValue key) +{ + DISALLOW_GARBAGE_COLLECTION; + ASSERT(hclass->IsPrototype()); + + PropertyLookupResult result; + int entry = JSHClass::FindPropertyEntry(thread, hclass, key); + // When the property is not found, the value of 'entry' is -1. + // Currently, not all methods on the prototype of 'builtin' have been changed to inlined. + // Therefore, when a non-inlined method is encountered, it is also considered not found. + if (entry == -1 || static_cast(entry) >= hclass->GetInlinedProperties()) { + result.SetIsFound(false); + return result; + } + + result.SetIsFound(true); + result.SetIsLocal(true); + uint32_t offset = hclass->GetInlinedPropertiesOffset(entry); + result.SetOffset(offset); + PropertyAttributes attr = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject())->GetAttr(entry); + result.SetIsNotHole(true); + if (attr.IsAccessor()) { + result.SetIsAccessor(true); + } + result.SetRepresentation(attr.GetRepresentation()); + result.SetIsWritable(attr.IsWritable()); + return result; +} + void JSHClass::CopyTSInheritInfo(const JSThread *thread, const JSHandle &oldHClass, JSHandle &newHClass) { @@ -695,12 +828,15 @@ void JSHClass::CopyTSInheritInfo(const JSThread *thread, const JSHandleSetVTable(thread, copyVtable); } -bool JSHClass::DumpForProfile(const JSHClass *hclass, PGOHClassLayoutDesc &desc, PGOObjLayoutKind kind) +bool JSHClass::DumpForProfile(const JSHClass *hclass, PGOHClassLayoutDesc &desc, PGOObjKind kind) { DISALLOW_GARBAGE_COLLECTION; if (hclass->IsDictionaryMode()) { return false; } + if (kind == PGOObjKind::ELEMENT) { + desc.UpdateElementKind(hclass->GetElementsKind()); + } LayoutInfo *layout = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject()); int element = static_cast(hclass->NumberOfProps()); @@ -709,4 +845,33 @@ bool JSHClass::DumpForProfile(const JSHClass *hclass, PGOHClassLayoutDesc &desc, } return true; } + +uint32_t JSHClass::ComputeHashcode(const JSHClass *hclass) +{ + DISALLOW_GARBAGE_COLLECTION; + if (hclass->IsDictionaryMode()) { + return 0; + } + + CString result; + LayoutInfo *layout = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject()); + int element = static_cast(hclass->NumberOfProps()); + for (int i = 0; i < element; i++) { + auto key = layout->GetKey(i); + if (key.IsString()) { + result += EcmaStringAccessor(key).ToCString(); + auto attr = layout->GetAttr(i); + result += static_cast(attr.GetTrackType()); + result += attr.IsAccessor(); + } + } + + uint32_t hash = 0; + Span sp(result.c_str(), result.size()); + for (auto c : sp) { + constexpr size_t SHIFT = 5; + hash = (hash << SHIFT) - hash + c; + } + return hash; +} } // namespace panda::ecmascript diff --git a/ecmascript/js_hclass.h b/ecmascript/js_hclass.h index 79d482b9e0343ae9233cb866b819a34d3a8fa583..5fbc528e6157f969d34b349266c31c7850ca6c57 100644 --- a/ecmascript/js_hclass.h +++ b/ecmascript/js_hclass.h @@ -17,12 +17,12 @@ #define ECMASCRIPT_JS_HCLASS_H #include "ecmascript/ecma_macros.h" +#include "ecmascript/elements.h" #include "ecmascript/js_tagged_value.h" #include "ecmascript/mem/tagged_object.h" #include "ecmascript/mem/barriers.h" #include "ecmascript/mem/slots.h" #include "ecmascript/mem/visitor.h" -#include "ecmascript/pgo_profiler/pgo_profiler_layout.h" #include "ecmascript/property_attributes.h" #include "libpandabase/utils/bit_field.h" @@ -62,6 +62,14 @@ namespace panda::ecmascript { class ProtoChangeDetails; class PropertyLookupResult; +namespace pgo { + class PGOHClassLayoutDesc; + enum class PGOObjKind; +} // namespace pgo +using PGOHClassLayoutDesc = pgo::PGOHClassLayoutDesc; +using PGOObjKind = pgo::PGOObjKind; + +struct Reference; // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define JSTYPE_DECL /* //////////////////////////////////////////////////////////////////////////////-PADDING */ \ @@ -186,6 +194,7 @@ class PropertyLookupResult; HCLASS, /* //////////////////////////////////////////////////////////////////////////////////-PADDING */ \ LINE_STRING, /* //////////////////////////////////////////////////////////////////////////////////-PADDING */\ CONSTANT_STRING, /* ///////////////////////////////////////////////////////////////////////////////-PADDING */\ + SLICED_STRING, /* ////////////////////////////////////////////////////////////////////////////////-PADDING */ \ TREE_STRING, /* //////////////////////////////////////////////////////////////////////////////////-PADDING */ \ BIGINT, /* //////////////////////////////////////////////////////////////////////////////////-PADDING */ \ TAGGED_ARRAY, /* //////////////////////////////////////////////////////////////////////////////////-PADDING */ \ @@ -193,6 +202,7 @@ class PropertyLookupResult; LEXICAL_ENV, /* //////////////////////////////////////////////////////////////////////////////////-PADDING */ \ TAGGED_DICTIONARY, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ CONSTANT_POOL, /* /////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + PROFILE_TYPE_INFO, /* /////////////////////////////////////////////////////////////////////////////-PADDING */ \ COW_TAGGED_ARRAY, /* //////////////////////////////////////////////////////////////////////////////-PADDING */ \ LINKED_NODE, /* //////////////////////////////////////////////////////////////////////////////////-PADDING */ \ RB_TREENODE, /* //////////////////////////////////////////////////////////////////////////////////-PADDING */ \ @@ -211,6 +221,8 @@ class PropertyLookupResult; STORE_TS_HANDLER, /* ////////////////////////////////////////////////////////////////////////-PADDING */ \ PROPERTY_BOX, /* /////////////////////////////////////////////////////////////////////////////////-PADDING */ \ PROTO_CHANGE_MARKER, /* ///////////////////////////////////////////////////////////////////////////-PADDING */ \ + MARKER_CELL, /* ///////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + TRACK_INFO, /* ///////////////////////////////////////////////////////////////////////////////////-PADDING */ \ PROTOTYPE_INFO, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ TEMPLATE_MAP, /* ////////////////////////////////////////////////////////////////////////////-PADDING */ \ PROGRAM, /* /////////////////////////////////////////////////////////////////////////////////-PADDING */ \ @@ -290,39 +302,45 @@ class JSHClass : public TaggedObject { public: static constexpr int TYPE_BITFIELD_NUM = 8; static constexpr int LEVEL_BTTFIELD_NUM = 5; - using ObjectTypeBits = BitField; // 8 - using CallableBit = ObjectTypeBits::NextFlag; - using ConstructorBit = CallableBit::NextFlag; // 10 - using ExtensibleBit = ConstructorBit::NextFlag; - using IsPrototypeBit = ExtensibleBit::NextFlag; - using ElementRepresentationBits = IsPrototypeBit::NextField; // 3 means next 3 bit - using DictionaryElementBits = ElementRepresentationBits::NextFlag; // 16 - using IsDictionaryBit = DictionaryElementBits::NextFlag; // 17 - using IsStableElementsBit = IsDictionaryBit::NextFlag; // 18 - using HasConstructorBits = IsStableElementsBit::NextFlag; // 19 - using IsLiteralBit = HasConstructorBits::NextFlag; // 20 - using ClassConstructorBit = IsLiteralBit::NextFlag; // 21 - using ClassPrototypeBit = ClassConstructorBit::NextFlag; // 22 - using GlobalConstOrBuiltinsObjectBit = ClassPrototypeBit::NextFlag; // 23 - using IsTSBit = GlobalConstOrBuiltinsObjectBit::NextFlag; // 24 - using LevelBit = IsTSBit::NextField; // 29 - using IsJSFunctionBit = LevelBit::NextFlag; // 30 - using IsOptimizedBit = IsJSFunctionBit::NextFlag; // 31 - using CanFastCallBit = IsOptimizedBit::NextFlag; // 32 + static constexpr int ELEMENTS_KIND_BITFIELD_NUM = 5; + static constexpr unsigned BITS_PER_BYTE = 8; + using ObjectTypeBits = BitField; // 8 + using CallableBit = ObjectTypeBits::NextFlag; // 9 + using ConstructorBit = CallableBit::NextFlag; // 10 + using ExtensibleBit = ConstructorBit::NextFlag; // 11 + using IsPrototypeBit = ExtensibleBit::NextFlag; // 12 + using ElementsKindBits = IsPrototypeBit::NextField; // 13-17 + using DictionaryElementBits = ElementsKindBits::NextFlag; // 18 + using IsDictionaryBit = DictionaryElementBits::NextFlag; // 19 + using IsStableElementsBit = IsDictionaryBit::NextFlag; // 20 + using HasConstructorBits = IsStableElementsBit::NextFlag; // 21 + using IsClassConstructorOrPrototypeBit = HasConstructorBits::NextFlag; // 22 + using GlobalConstOrBuiltinsObjectBit = IsClassConstructorOrPrototypeBit::NextFlag; // 23 + using IsTSBit = GlobalConstOrBuiltinsObjectBit::NextFlag; // 24 + using LevelBit = IsTSBit::NextField; // 25-29 + using IsJSFunctionBit = LevelBit::NextFlag; // 30 + using IsOptimizedBit = IsJSFunctionBit::NextFlag; // 31 + using CanFastCallBit = IsOptimizedBit::NextFlag; // 32 + using BitFieldLastBit = CanFastCallBit; + static_assert(BitFieldLastBit::START_BIT + BitFieldLastBit::SIZE <= sizeof(uint32_t) * BITS_PER_BYTE, "Invalid"); static constexpr int DEFAULT_CAPACITY_OF_IN_OBJECTS = 4; - static constexpr int MAX_CAPACITY_OF_OUT_OBJECTS = - PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES - DEFAULT_CAPACITY_OF_IN_OBJECTS; static constexpr int OFFSET_MAX_OBJECT_SIZE_IN_WORDS_WITHOUT_INLINED = 5; static constexpr int OFFSET_MAX_OBJECT_SIZE_IN_WORDS = PropertyAttributes::OFFSET_BITFIELD_NUM + OFFSET_MAX_OBJECT_SIZE_IN_WORDS_WITHOUT_INLINED; static constexpr int MAX_OBJECT_SIZE_IN_WORDS = (1U << OFFSET_MAX_OBJECT_SIZE_IN_WORDS) - 1; + static constexpr uint64_t OPTIMIZED_BIT = 1LU << IsOptimizedBit::START_BIT; + static constexpr uint64_t FASTCALL_BIT = 1LU << CanFastCallBit::START_BIT; + static constexpr uint64_t OPTIMIZED_FASTCALL_BITS = OPTIMIZED_BIT | FASTCALL_BIT; - using NumberOfPropsBits = BitField; // 10 + using NumberOfPropsBits = BitField; // 10 using InlinedPropsStartBits = NumberOfPropsBits::NextField; // 15 + OFFSET_MAX_OBJECT_SIZE_IN_WORDS_WITHOUT_INLINED>; // 15 using ObjectSizeInWordsBits = InlinedPropsStartBits::NextField; // 30 - using HasDeletePropertyBit = ObjectSizeInWordsBits::NextFlag; + using HasDeletePropertyBit = ObjectSizeInWordsBits::NextFlag; // + using IsAllTaggedPropBit = HasDeletePropertyBit::NextFlag; // 32 + using BitField1LastBit = IsAllTaggedPropBit; + static_assert(BitField1LastBit::START_BIT + BitField1LastBit::SIZE <= sizeof(uint32_t) * BITS_PER_BYTE, "Invalid"); static JSHClass *Cast(const TaggedObject *object); @@ -350,6 +368,11 @@ public: static JSHandle TransProtoWithoutLayout(const JSThread *thread, const JSHandle &jshclass, const JSHandle &proto); static void TransitionToDictionary(const JSThread *thread, const JSHandle &obj); + static void TransitionForRepChange(const JSThread *thread, const JSHandle &receiver, + const JSHandle &key, PropertyAttributes attr); + static void TransitToElementsKind(const JSThread *thread, const JSHandle &array); + static bool TransitToElementsKind(const JSThread *thread, const JSHandle &object, + const JSHandle &value, ElementsKind kind = ElementsKind::NONE); static JSHandle EnableProtoChangeMarker(const JSThread *thread, const JSHandle &jshclass); @@ -425,19 +448,16 @@ public: IsPrototypeBit::Set(flag, GetBitFieldAddr()); } - inline void SetIsLiteral(bool flag) const - { - IsLiteralBit::Set(flag, GetBitFieldAddr()); - } - inline void SetClassConstructor(bool flag) const { - ClassConstructorBit::Set(flag, GetBitFieldAddr()); + IsClassConstructorOrPrototypeBit::Set(flag, GetBitFieldAddr()); + SetConstructor(flag); } inline void SetClassPrototype(bool flag) const { - ClassPrototypeBit::Set(flag, GetBitFieldAddr()); + IsClassConstructorOrPrototypeBit::Set(flag, GetBitFieldAddr()); + SetIsPrototype(flag); } inline void SetGlobalConstOrBuiltinsObject(bool flag) const @@ -514,6 +534,11 @@ public: return GetObjectType() == JSType::CONSTANT_STRING; } + inline bool IsSlicedString() const + { + return GetObjectType() == JSType::SLICED_STRING; + } + inline bool IsTreeString() const { return GetObjectType() == JSType::TREE_STRING; @@ -543,6 +568,7 @@ public: case JSType::TAGGED_DICTIONARY: case JSType::LEXICAL_ENV: case JSType::CONSTANT_POOL: + case JSType::PROFILE_TYPE_INFO: case JSType::AOT_LITERAL_INFO: case JSType::VTABLE: case JSType::COW_TAGGED_ARRAY: @@ -1114,6 +1140,16 @@ public: return GetObjectType() == JSType::PROTO_CHANGE_MARKER; } + inline bool IsMarkerCell() const + { + return GetObjectType() == JSType::MARKER_CELL; + } + + inline bool IsTrackInfoObject() const + { + return GetObjectType() == JSType::TRACK_INFO; + } + inline bool IsProtoChangeDetails() const { return GetObjectType() == JSType::PROTOTYPE_INFO; @@ -1153,16 +1189,10 @@ public: return IsPrototypeBit::Decode(bits); } - inline bool IsLiteral() const - { - uint32_t bits = GetBitField(); - return IsLiteralBit::Decode(bits); - } - inline bool IsClassConstructor() const { uint32_t bits = GetBitField(); - return ClassConstructorBit::Decode(bits); + return IsClassConstructorOrPrototypeBit::Decode(bits) && IsConstructor(); } inline bool IsJSGlobalObject() const @@ -1173,7 +1203,7 @@ public: inline bool IsClassPrototype() const { uint32_t bits = GetBitField(); - return ClassPrototypeBit::Decode(bits); + return IsClassConstructorOrPrototypeBit::Decode(bits) && IsPrototype(); } inline bool IsGlobalConstOrBuiltinsObject() const @@ -1467,17 +1497,17 @@ public: return GetObjectType() == JSType::JS_MODULE_NAMESPACE; } - inline void SetElementRepresentation(Representation representation) + inline void SetElementsKind(ElementsKind kind) { uint32_t bits = GetBitField(); - uint32_t newVal = ElementRepresentationBits::Update(bits, representation); + uint32_t newVal = ElementsKindBits::Update(bits, kind); SetBitField(newVal); } - inline Representation GetElementRepresentation() const + inline ElementsKind GetElementsKind() const { uint32_t bits = GetBitField(); - return ElementRepresentationBits::Decode(bits); + return ElementsKindBits::Decode(bits); } inline void SetLevel(uint8_t level) @@ -1493,12 +1523,6 @@ public: return LevelBit::Decode(bits); } - inline void UpdateRepresentation(JSTaggedValue value) - { - Representation rep = PropertyAttributes::UpdateRepresentation(GetElementRepresentation(), value); - SetElementRepresentation(rep); - } - inline void SetIsDictionaryElement(bool value) { uint32_t newVal = DictionaryElementBits::Update(GetBitField(), value); @@ -1548,7 +1572,7 @@ public: inline void IncNumberOfProps() { - ASSERT(NumberOfProps() < PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES); + ASSERT(NumberOfProps() < PropertyAttributes::MAX_FAST_PROPS_CAPACITY); SetNumberOfProps(NumberOfProps() + 1); } @@ -1640,14 +1664,32 @@ public: return HasDeletePropertyBit::Decode(bits); } + inline void SetIsAllTaggedProp(bool flag) const + { + IsAllTaggedPropBit::Set(flag, GetBitField1Addr()); + } + + inline bool IsAllTaggedProp() const + { + uint32_t bits = GetBitField1(); + return IsAllTaggedPropBit::Decode(bits); + } + + inline static JSHClass *FindRootHClass(JSHClass *hclass); + inline static void UpdateRootHClass(const JSThread *thread, const JSHandle &parent, + const JSHandle &child); + inline static int FindPropertyEntry(const JSThread *thread, JSHClass *hclass, JSTaggedValue key); static PropertyLookupResult LookupPropertyInAotHClass(const JSThread *thread, JSHClass *hclass, JSTaggedValue key); + static PropertyLookupResult LookupPropertyInBuiltinPrototypeHClass(const JSThread *thread, JSHClass *hclass, + JSTaggedValue key); static constexpr size_t PROTOTYPE_OFFSET = TaggedObjectSize(); ACCESSORS(Proto, PROTOTYPE_OFFSET, LAYOUT_OFFSET); ACCESSORS(Layout, LAYOUT_OFFSET, TRANSTIONS_OFFSET); - ACCESSORS(Transitions, TRANSTIONS_OFFSET, PROTO_CHANGE_MARKER_OFFSET); + ACCESSORS(Transitions, TRANSTIONS_OFFSET, PARENT_OFFSET); + ACCESSORS(Parent, PARENT_OFFSET, PROTO_CHANGE_MARKER_OFFSET); ACCESSORS(ProtoChangeMarker, PROTO_CHANGE_MARKER_OFFSET, PROTO_CHANGE_DETAILS_OFFSET); ACCESSORS(ProtoChangeDetails, PROTO_CHANGE_DETAILS_OFFSET, ENUM_CACHE_OFFSET); ACCESSORS(EnumCache, ENUM_CACHE_OFFSET, SUPERS_OFFSET); @@ -1670,7 +1712,8 @@ public: DECL_DUMP() static CString DumpJSType(JSType type); - static bool DumpForProfile(const JSHClass *hclass, PGOHClassLayoutDesc &desc, PGOObjLayoutKind kind); + static bool DumpForProfile(const JSHClass *hclass, PGOHClassLayoutDesc &desc, PGOObjKind kind); + static uint32_t ComputeHashcode(const JSHClass *hclass); DECL_VISIT_OBJECT(PROTOTYPE_OFFSET, BIT_FIELD_OFFSET); @@ -1711,6 +1754,7 @@ public: using IsAccessorBit = IsNotHoleBit::NextFlag; using OffsetBits = IsAccessorBit::NextField; using WritableField = OffsetBits::NextFlag; + using RepresentationBits = WritableField::NextField; explicit PropertyLookupResult(uint32_t data = 0) : data_(data) {} ~PropertyLookupResult() = default; @@ -1793,6 +1837,16 @@ public: OffsetBits::Set(offset, &data_); } + inline void SetRepresentation(Representation rep) + { + RepresentationBits::Set(rep, &data_); + } + + inline Representation GetRepresentation() + { + return RepresentationBits::Get(data_); + } + inline uint32_t GetData() const { return data_; @@ -1802,7 +1856,7 @@ private: uint32_t data_ {0}; }; static_assert(PropertyLookupResult::OffsetBits::MaxValue() > - (PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES * JSTaggedValue::TaggedTypeSize())); + (PropertyAttributes::MAX_FAST_PROPS_CAPACITY * JSTaggedValue::TaggedTypeSize())); } // namespace panda::ecmascript #endif // ECMASCRIPT_JS_HCLASS_H diff --git a/ecmascript/js_iterator.cpp b/ecmascript/js_iterator.cpp index 751e89a9da7222e4665c9fd2269fd4650b87f7a9..cd92e32a52604c04bcba49e70b416f4d3c86da93 100644 --- a/ecmascript/js_iterator.cpp +++ b/ecmascript/js_iterator.cpp @@ -34,6 +34,7 @@ JSTaggedValue JSIterator::IteratorCloseAndReturn(JSThread *thread, const JSHandl JSHandle record = JSHandle(factory->NewCompletionRecord(CompletionRecordType::THROW, JSHandle(thread, exception))); JSHandle result = JSIterator::IteratorClose(thread, iter, record); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); if (result->IsCompletionRecord()) { return CompletionRecord::Cast(result->GetTaggedObject())->GetValue(); } @@ -88,6 +89,7 @@ JSHandle JSIterator::GetAsyncIterator(JSThread *thread, const JSH JSHandle syncIterator = GetIterator(thread, obj, func); JSHandle nextStr = thread->GlobalConstants()->GetHandledNextString(); JSHandle nextMethod = JSTaggedValue::GetProperty(thread, syncIterator, nextStr).GetValue(); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); JSHandle syncIteratorRecord = factory->NewAsyncIteratorRecord(syncIterator, nextMethod, false); JSHandle asyncIterator = @@ -99,6 +101,7 @@ JSHandle JSIterator::GetAsyncIterator(JSThread *thread, const JSH JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, method, obj, undefined, 0); JSTaggedValue ret = JSFunction::Call(info); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); JSHandle iterator(thread, ret); // 5.If Type(iterator) is not Object, throw a TypeError exception if (!iterator->IsECMAObject()) { @@ -116,6 +119,7 @@ JSHandle JSIterator::IteratorNext(JSThread *thread, const JSHandl // 1.If value was not passed, then Let result be Invoke(iterator, "next", «‍ »). JSHandle key(globalConst->GetHandledNextString()); JSHandle next(JSObject::GetMethod(thread, iter, key)); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); JSHandle undefined = globalConst->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, next, iter, undefined, 0); JSTaggedValue ret = JSFunction::Call(info); @@ -136,8 +140,10 @@ JSHandle JSIterator::IteratorNext(JSThread *thread, const JSHandl // 2.Let result be Invoke(iterator, "next", «‍value»). JSHandle key(globalConst->GetHandledNextString()); JSHandle next(JSObject::GetMethod(thread, iter, key)); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); JSHandle undefined = globalConst->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, next, iter, undefined, 1); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, undefined); info->SetCallArg(value.GetTaggedValue()); JSTaggedValue ret = JSFunction::Call(info); // 3.ReturnIfAbrupt(result) @@ -159,6 +165,7 @@ JSHandle JSIterator::IteratorNext(JSThread *thread, const JSHandl JSHandle next(thread, iter->GetNextMethod()); JSHandle undefined = globalConst->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, next, iterator, undefined, 1); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, undefined); info->SetCallArg(value.GetTaggedValue()); JSTaggedValue ret = JSFunction::Call(info); // 3.ReturnIfAbrupt(result) @@ -206,6 +213,7 @@ JSHandle JSIterator::IteratorValue(JSThread *thread, const JSHand // Return Get(iterResult, "value"). JSHandle valueStr = thread->GlobalConstants()->GetHandledValueString(); JSHandle value = JSTaggedValue::GetProperty(thread, iterResult, valueStr).GetValue(); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); return value; } // 7.4.5 @@ -261,6 +269,7 @@ JSHandle JSIterator::IteratorClose(JSThread *thread, const JSHand JSHandle undefined = globalConst->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, returnFunc, iter, undefined, 0); JSTaggedValue ret = JSFunction::Call(info); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); if (!exceptionOnThread.IsEmpty()) { thread->SetException(exceptionOnThread.GetTaggedValue()); } diff --git a/ecmascript/js_list_format.cpp b/ecmascript/js_list_format.cpp index aaefca407f35bfb51de80dcb5a02365cf9d2f6e2..80dfbf467349342622230cf65d74f5421c202fd7 100644 --- a/ecmascript/js_list_format.cpp +++ b/ecmascript/js_list_format.cpp @@ -25,6 +25,7 @@ #include "ecmascript/js_array.h" #include "ecmascript/js_locale.h" #include "ecmascript/js_iterator.h" +#include "ecmascript/object_factory-inl.h" #include "unicode/fieldpos.h" #include "unicode/fpositer.h" @@ -134,6 +135,7 @@ JSHandle JSListFormat::InitializeListFormat(JSThread *thread, // 10. Set listFormat.[[Locale]] to r.[[locale]]. icu::Locale icuLocale = r.localeData; JSHandle localeStr = intl::LocaleHelper::ToLanguageTag(thread, icuLocale); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSListFormat, thread); listFormat->SetLocale(thread, localeStr.GetTaggedValue()); // 11. Let type be ? GetOption(options, "type", "string", « "conjunction", "disjunction", "unit" », "conjunction"). @@ -215,6 +217,7 @@ JSHandle JSListFormat::InitializeListFormat(JSThread *thread, JSHandle JSListFormat::StringListFromIterable(JSThread *thread, const JSHandle &iterable) { JSHandle array = JSHandle::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); JSHandle arrayList = JSHandle::Cast(array); // 1. If iterable is undefined, then // a. Return a new empty List. @@ -394,6 +397,7 @@ JSHandle JSListFormat::FormatListToParts(JSThread *thread, const JSHand THROW_RANGE_ERROR_AND_RETURN(thread, "formatted list toString failed", listArray); } JSHandle array = JSHandle::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread); FormatListToArray(thread, formatted, array, status, result); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread); return array; @@ -408,17 +412,20 @@ void JSListFormat::ResolvedOptions(JSThread *thread, const JSHandle propertyKey = globalConst->GetHandledLocaleString(); JSHandle locale(thread, listFormat->GetLocale()); JSObject::CreateDataPropertyOrThrow(thread, options, propertyKey, locale); + RETURN_IF_ABRUPT_COMPLETION(thread); // [[type]] ListTypeOption type = listFormat->GetType(); propertyKey = globalConst->GetHandledTypeString(); JSHandle typeString = ListOptionTypeToEcmaString(thread, type); JSObject::CreateDataPropertyOrThrow(thread, options, propertyKey, typeString); + RETURN_IF_ABRUPT_COMPLETION(thread); // [[Style]] ListStyleOption style = listFormat->GetStyle(); propertyKey = globalConst->GetHandledStyleString(); JSHandle styleString = ListOptionStyleToEcmaString(thread, style); JSObject::CreateDataPropertyOrThrow(thread, options, propertyKey, styleString); + RETURN_IF_ABRUPT_COMPLETION(thread); } } // namespace panda::ecmascript diff --git a/ecmascript/js_locale.cpp b/ecmascript/js_locale.cpp index c0d3c8f819c0d219fdec4d5bf7d3ac98cec52a97..1a1269af744f5eca92983d89a45598e5efb85017 100644 --- a/ecmascript/js_locale.cpp +++ b/ecmascript/js_locale.cpp @@ -20,7 +20,7 @@ #include "ecmascript/ecma_macros.h" #include "ecmascript/ecma_vm.h" #include "ecmascript/global_env.h" -#include "ecmascript/object_factory.h" +#include "ecmascript/object_factory-inl.h" #if defined(__clang__) #pragma clang diagnostic push @@ -642,11 +642,14 @@ JSHandle JSLocale::PutElement(JSThread *thread, int index, const JSHan auto globalConst = thread->GlobalConstants(); // obj.type = field_type_string JSObject::CreateDataPropertyOrThrow(thread, record, globalConst->GetHandledTypeString(), fieldTypeString); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread); // obj.value = value JSObject::CreateDataPropertyOrThrow(thread, record, globalConst->GetHandledValueString(), value); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread); JSTaggedValue::SetProperty(thread, JSHandle::Cast(array), index, JSHandle::Cast(record), true); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread); return record; } diff --git a/ecmascript/js_locale.h b/ecmascript/js_locale.h index b3125463b603eafb972a214b648005e95e2eb2f5..896d7c8b2fe1347de3b74571b2d7b714660073ac 100644 --- a/ecmascript/js_locale.h +++ b/ecmascript/js_locale.h @@ -524,18 +524,22 @@ public: // 6. Let mnfd be ? Get(options, "minimumFractionDigits"). JSHandle mnfdKey = globalConst->GetHandledMinimumFractionDigitsString(); JSHandle mnfd = JSTaggedValue::GetProperty(thread, options, mnfdKey).GetValue(); + intlObj->SetMinimumIntegerDigits(thread, JSTaggedValue(mnid)); + RETURN_IF_ABRUPT_COMPLETION(thread); // 7. Let mxfd be ? Get(options, "maximumFractionDigits"). JSHandle mxfdKey = globalConst->GetHandledMaximumFractionDigitsString(); JSHandle mxfd = JSTaggedValue::GetProperty(thread, options, mxfdKey).GetValue(); + RETURN_IF_ABRUPT_COMPLETION(thread); // 8. Let mnsd be ? Get(options, "minimumSignificantDigits"). JSHandle mnsdKey = globalConst->GetHandledMinimumSignificantDigitsString(); JSHandle mnsd = JSTaggedValue::GetProperty(thread, options, mnsdKey).GetValue(); + RETURN_IF_ABRUPT_COMPLETION(thread); // 9. Let mxsd be ? Get(options, "maximumSignificantDigits"). JSHandle mxsdKey = globalConst->GetHandledMaximumSignificantDigitsString(); JSHandle mxsd = JSTaggedValue::GetProperty(thread, options, mxsdKey).GetValue(); + RETURN_IF_ABRUPT_COMPLETION(thread); // 10. Set intlObj.[[MinimumIntegerDigits]] to mnid. - intlObj->SetMinimumIntegerDigits(thread, JSTaggedValue(mnid)); // 11. If mnsd is not undefined or mxsd is not undefined, then if (!mnsd->IsUndefined() || !mxsd->IsUndefined()) { // a. Set intlObj.[[RoundingType]] to significantDigits. diff --git a/ecmascript/js_map.cpp b/ecmascript/js_map.cpp index 7ac825873cce5e3745cff5fa5c26556534610934..b10502cae9cda41e09afac5ae1d5055b09b52add 100644 --- a/ecmascript/js_map.cpp +++ b/ecmascript/js_map.cpp @@ -61,18 +61,18 @@ JSTaggedValue JSMap::Get(JSTaggedValue key) const return LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject())->Get(key); } -int JSMap::GetSize() const +uint32_t JSMap::GetSize() const { return LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject())->NumberOfElements(); } -JSTaggedValue JSMap::GetKey(int entry) const +JSTaggedValue JSMap::GetKey(uint32_t entry) const { ASSERT_PRINT(entry >= 0 && entry < GetSize(), "entry must be non-negative integer less than capacity"); return LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject())->GetKey(entry); } -JSTaggedValue JSMap::GetValue(int entry) const +JSTaggedValue JSMap::GetValue(uint32_t entry) const { ASSERT_PRINT(entry >= 0 && entry < GetSize(), "entry must be non-negative integer less than capacity"); return LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject())->GetValue(entry); diff --git a/ecmascript/js_map.h b/ecmascript/js_map.h index 3fb8c1dc5e0fa9ef58b13a815145e4699ded3f9c..57f7420f35c62a4a13e266e39e6b1588260966c7 100644 --- a/ecmascript/js_map.h +++ b/ecmascript/js_map.h @@ -34,11 +34,11 @@ public: JSTaggedValue Get(JSTaggedValue key) const; - int GetSize() const; + uint32_t GetSize() const; - JSTaggedValue GetKey(int entry) const; + JSTaggedValue GetKey(uint32_t entry) const; - JSTaggedValue GetValue(int entry) const; + JSTaggedValue GetValue(uint32_t entry) const; static constexpr size_t LINKED_MAP_OFFSET = JSObject::SIZE; ACCESSORS(LinkedMap, LINKED_MAP_OFFSET, SIZE) diff --git a/ecmascript/js_map_iterator.cpp b/ecmascript/js_map_iterator.cpp index 0307114bc50cf52bda03ad1cffc8a4630fe98b75..fa3a3cac7510c658ca8afdb3487864ba36e347a4 100644 --- a/ecmascript/js_map_iterator.cpp +++ b/ecmascript/js_map_iterator.cpp @@ -115,4 +115,54 @@ JSHandle JSMapIterator::CreateMapIterator(JSThread *thread, const JSHandle iter(factory->NewJSMapIterator(JSHandle(obj), kind)); return iter; } + +JSTaggedValue JSMapIterator::MapIteratorToList(JSThread *thread, JSHandle &items, + JSHandle &method) +{ + JSTaggedValue newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle newArrayHandle(thread, newArray); + JSHandle iterator = JSIterator::GetIterator(thread, items, method); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + JSHandle iter(iterator); + JSHandle iteratedMap(thread, iter->GetIteratedMap()); + if (iteratedMap->IsUndefined()) { + return newArrayHandle.GetTaggedValue(); + } + IterationKind itemKind = iter->GetIterationKind(); + JSHandle map(iteratedMap); + int totalElements = map->NumberOfElements() + map->NumberOfDeletedElements(); + int index = static_cast(iter->GetNextIndex()); + int k = 0; + + JSMutableHandle keyHandle(thread, JSTaggedValue::Undefined()); + JSMutableHandle valueHandle(thread, JSTaggedValue::Undefined()); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle oldElements(thread, newArrayHandle->GetElements()); + JSHandle elements = factory->ExtendArray(oldElements, totalElements); + while (index < totalElements) { + JSTaggedValue key = map->GetKey(index); + if (!key.IsHole()) { + keyHandle.Update(key); + valueHandle.Update(map->GetValue(index)); + if (itemKind == IterationKind::KEY) { + elements->Set(thread, k, keyHandle); + } else if (itemKind == IterationKind::VALUE) { + elements->Set(thread, k, valueHandle); + } else { + JSHandle array(factory->NewTaggedArray(2)); // 2 means the length of array + array->Set(thread, 0, keyHandle); + array->Set(thread, 1, valueHandle); + JSHandle keyAndValue(JSArray::CreateArrayFromList(thread, array)); + elements->Set(thread, k, keyAndValue); + } + k++; + } + index++; + } + JSHandle(newArrayHandle)->SetArrayLength(thread, k); + newArrayHandle->SetElements(thread, elements); + return newArrayHandle.GetTaggedValue(); +} } // namespace panda::ecmascript diff --git a/ecmascript/js_map_iterator.h b/ecmascript/js_map_iterator.h index 9e39ee68ad7b582a2581911f0991aac14fb9d64b..b005c15c3420236c711acadbf8a7cc10b201760c 100644 --- a/ecmascript/js_map_iterator.h +++ b/ecmascript/js_map_iterator.h @@ -32,6 +32,8 @@ public: static JSTaggedValue Next(EcmaRuntimeCallInfo *argv); void Update(const JSThread *thread); + static JSTaggedValue MapIteratorToList(JSThread *thread, JSHandle &items, + JSHandle &method); static constexpr size_t ITERATED_MAP_OFFSET = JSObject::SIZE; ACCESSORS(IteratedMap, ITERATED_MAP_OFFSET, NEXT_INDEX_OFFSET); diff --git a/ecmascript/js_native_pointer.h b/ecmascript/js_native_pointer.h index fc8fcdcebf1796eb35e1382f2d91628672d75ba1..46f1229b1eb3999388cceca189d6fb32dc1dfdb0 100644 --- a/ecmascript/js_native_pointer.h +++ b/ecmascript/js_native_pointer.h @@ -40,14 +40,15 @@ public: inline void Destroy() { DeleteExternalPointer(); - Detach(); + SetExternalPointer(nullptr); + SetDeleter(nullptr); + SetData(nullptr); } inline void Detach() { - SetExternalPointer(nullptr); + // Keep other fields accessible after detached SetDeleter(nullptr); - SetData(nullptr); } static constexpr size_t POINTER_OFFSET = TaggedObjectSize(); diff --git a/ecmascript/js_number_format.cpp b/ecmascript/js_number_format.cpp index 16968c1a098021a737d27e4ff1661af9cfe1c451..3e533fe963dd0456155ecec8ea5303f39a1a98bf 100644 --- a/ecmascript/js_number_format.cpp +++ b/ecmascript/js_number_format.cpp @@ -14,6 +14,7 @@ */ #include "ecmascript/js_number_format.h" +#include "ecmascript/object_factory-inl.h" namespace panda::ecmascript { constexpr uint32_t DEFAULT_FRACTION_DIGITS = 2; @@ -504,6 +505,7 @@ void JSNumberFormat::InitializeNumberFormat(JSThread *thread, const JSHandle localeStr = intl::LocaleHelper::ToLanguageTag(thread, icuLocale); + RETURN_IF_ABRUPT_COMPLETION(thread); numberFormat->SetLocale(thread, localeStr.GetTaggedValue()); // Set numberingSystemStr to UnicodeKeyWord "nu" @@ -857,6 +859,7 @@ JSHandle JSNumberFormat::FormatNumericToParts(JSThread *thread, const J } JSHandle arr = JSArray::ArrayCreate(thread, JSTaggedNumber(0)); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread); JSHandle result = JSHandle::Cast(arr); GroupToParts(thread, formattedNumber, result, numberFormat, x); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread); @@ -935,6 +938,7 @@ void JSNumberFormat::ResolvedOptions(JSThread *thread, const JSHandle property = globalConst->GetHandledLocaleString(); JSHandle locale(thread, numberFormat->GetLocale()); JSObject::CreateDataPropertyOrThrow(thread, options, property, locale); + RETURN_IF_ABRUPT_COMPLETION(thread); // [[NumberingSystem]] JSHandle numberingSystem(thread, numberFormat->GetNumberingSystem()); @@ -943,12 +947,14 @@ void JSNumberFormat::ResolvedOptions(JSThread *thread, const JSHandleGetHandledNumberingSystemString(); JSObject::CreateDataPropertyOrThrow(thread, options, property, numberingSystem); + RETURN_IF_ABRUPT_COMPLETION(thread); // [[Style]] StyleOption style = numberFormat->GetStyle(); property = globalConst->GetHandledStyleString(); JSHandle styleString = OptionToEcmaString(thread, style); JSObject::CreateDataPropertyOrThrow(thread, options, property, styleString); + RETURN_IF_ABRUPT_COMPLETION(thread); // [[currency]] JSHandle currency(thread, JSTaggedValue::Undefined()); @@ -959,18 +965,21 @@ void JSNumberFormat::ResolvedOptions(JSThread *thread, const JSHandleIsUndefined()) { // NOLINT(readability-implicit-bool-conversion) property = globalConst->GetHandledCurrencyString(); JSObject::CreateDataPropertyOrThrow(thread, options, property, currency); + RETURN_IF_ABRUPT_COMPLETION(thread); // [[CurrencyDisplay]] property = globalConst->GetHandledCurrencyDisplayString(); CurrencyDisplayOption currencyDisplay = numberFormat->GetCurrencyDisplay(); JSHandle currencyDisplayString = OptionToEcmaString(thread, currencyDisplay); JSObject::CreateDataPropertyOrThrow(thread, options, property, currencyDisplayString); + RETURN_IF_ABRUPT_COMPLETION(thread); // [[CurrencySign]] property = globalConst->GetHandledCurrencySignString(); CurrencySignOption currencySign = numberFormat->GetCurrencySign(); JSHandle currencySignString = OptionToEcmaString(thread, currencySign); JSObject::CreateDataPropertyOrThrow(thread, options, property, currencySignString); + RETURN_IF_ABRUPT_COMPLETION(thread); } if (style == StyleOption::UNIT) { @@ -979,17 +988,20 @@ void JSNumberFormat::ResolvedOptions(JSThread *thread, const JSHandleGetHandledUnitString(); JSObject::CreateDataPropertyOrThrow(thread, options, property, unit); + RETURN_IF_ABRUPT_COMPLETION(thread); } // [[UnitDisplay]] property = globalConst->GetHandledUnitDisplayString(); UnitDisplayOption unitDisplay = numberFormat->GetUnitDisplay(); JSHandle unitDisplayString = OptionToEcmaString(thread, unitDisplay); JSObject::CreateDataPropertyOrThrow(thread, options, property, unitDisplayString); + RETURN_IF_ABRUPT_COMPLETION(thread); } // [[MinimumIntegerDigits]] property = globalConst->GetHandledMinimumIntegerDigitsString(); JSHandle minimumIntegerDigits(thread, numberFormat->GetMinimumIntegerDigits()); JSObject::CreateDataPropertyOrThrow(thread, options, property, minimumIntegerDigits); + RETURN_IF_ABRUPT_COMPLETION(thread); RoundingType roundingType = numberFormat->GetRoundingType(); if (roundingType == RoundingType::SIGNIFICANTDIGITS) { @@ -997,31 +1009,37 @@ void JSNumberFormat::ResolvedOptions(JSThread *thread, const JSHandleGetHandledMinimumSignificantDigitsString(); JSHandle minimumSignificantDigits(thread, numberFormat->GetMinimumSignificantDigits()); JSObject::CreateDataPropertyOrThrow(thread, options, property, minimumSignificantDigits); + RETURN_IF_ABRUPT_COMPLETION(thread); // [[MaximumSignificantDigits]] property = globalConst->GetHandledMaximumSignificantDigitsString(); JSHandle maximumSignificantDigits(thread, numberFormat->GetMaximumSignificantDigits()); JSObject::CreateDataPropertyOrThrow(thread, options, property, maximumSignificantDigits); + RETURN_IF_ABRUPT_COMPLETION(thread); } else { // [[MinimumFractionDigits]] property = globalConst->GetHandledMinimumFractionDigitsString(); JSHandle minimumFractionDigits(thread, numberFormat->GetMinimumFractionDigits()); JSObject::CreateDataPropertyOrThrow(thread, options, property, minimumFractionDigits); + RETURN_IF_ABRUPT_COMPLETION(thread); // [[MaximumFractionDigits]] property = globalConst->GetHandledMaximumFractionDigitsString(); JSHandle maximumFractionDigits(thread, numberFormat->GetMaximumFractionDigits()); JSObject::CreateDataPropertyOrThrow(thread, options, property, maximumFractionDigits); + RETURN_IF_ABRUPT_COMPLETION(thread); } // [[UseGrouping]] property = globalConst->GetHandledUserGroupingString(); JSObject::CreateDataPropertyOrThrow(thread, options, property, JSHandle(thread, numberFormat->GetUseGrouping())); + RETURN_IF_ABRUPT_COMPLETION(thread); // [[Notation]] property = globalConst->GetHandledNotationString(); NotationOption notation = numberFormat->GetNotation(); JSHandle notationString = OptionToEcmaString(thread, notation); JSObject::CreateDataPropertyOrThrow(thread, options, property, notationString); + RETURN_IF_ABRUPT_COMPLETION(thread); // Only output compactDisplay when notation is compact. if (notation == NotationOption::COMPACT) { @@ -1030,6 +1048,7 @@ void JSNumberFormat::ResolvedOptions(JSThread *thread, const JSHandleGetCompactDisplay(); JSHandle compactDisplayString = OptionToEcmaString(thread, compactDisplay); JSObject::CreateDataPropertyOrThrow(thread, options, property, compactDisplayString); + RETURN_IF_ABRUPT_COMPLETION(thread); } // [[SignDisplay]] diff --git a/ecmascript/js_object-inl.h b/ecmascript/js_object-inl.h index a95529a45fa7aa021827497b7a534ed53053ec57..d63ba411b7cbc2f0ca08e078d9059f5f5070644b 100644 --- a/ecmascript/js_object-inl.h +++ b/ecmascript/js_object-inl.h @@ -58,6 +58,15 @@ inline JSHClass *JSObject::GetJSHClass() const return GetClass(); } +inline uint32_t JSObject::GetNonInlinedFastPropsCapacity() const +{ + uint32_t inlineProps = GetJSHClass()->GetInlinedProperties(); + if (inlineProps < JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS) { + return PropertyAttributes::MAX_FAST_PROPS_CAPACITY - JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS; + } + return PropertyAttributes::MAX_FAST_PROPS_CAPACITY - inlineProps; +} + inline bool JSObject::IsJSGlobalObject() const { return GetJSHClass()->IsJSGlobalObject(); @@ -223,9 +232,62 @@ inline bool JSObject::IsTypedArray() const return GetJSHClass()->IsTypedArray(); } +std::pair JSObject::ConvertValueWithRep(uint32_t index, JSTaggedValue value) +{ + auto layout = LayoutInfo::Cast(GetJSHClass()->GetLayout().GetTaggedObject()); + auto attr = layout->GetAttr(index); + if (attr.IsDoubleRep()) { + if (value.IsInt()) { + double doubleValue = value.GetInt(); + return std::pair(true, JSTaggedValue(bit_cast(doubleValue))); + } else if (value.IsDouble()) { + return std::pair(true, JSTaggedValue(bit_cast(value.GetDouble()))); + } else { + return std::pair(false, value); + } + } else if (attr.IsIntRep()) { + if (value.IsInt()) { + int intValue = value.GetInt(); + return std::pair(true, JSTaggedValue(static_cast(intValue))); + } else { + return std::pair(false, value); + } + } + return std::pair(true, value); +} + +void JSObject::SetPropertyInlinedPropsWithRep(const JSThread *thread, uint32_t index, JSTaggedValue value) +{ + auto layout = LayoutInfo::Cast(GetJSHClass()->GetLayout().GetTaggedObject()); + auto attr = layout->GetAttr(index); + if (attr.IsTaggedRep()) { + SetPropertyInlinedProps(thread, index, value); + } else { + SetPropertyInlinedProps(thread, index, value); + } +} + +template void JSObject::SetPropertyInlinedProps(const JSThread *thread, uint32_t index, JSTaggedValue value) { - SetPropertyInlinedProps(thread, GetJSHClass(), index, value); + SetPropertyInlinedProps(thread, GetJSHClass(), index, value); +} + +JSTaggedValue JSObject::GetPropertyInlinedPropsWithRep(uint32_t index, PropertyAttributes attr) const +{ + return GetPropertyInlinedPropsWithRep(GetJSHClass(), index, attr); +} + +JSTaggedValue JSObject::GetPropertyInlinedPropsWithRep(const JSHClass *hclass, uint32_t index, + PropertyAttributes attr) const +{ + auto value = GetPropertyInlinedProps(hclass, index); + if (attr.IsDoubleRep()) { + value = JSTaggedValue(bit_cast(value.GetRawData())); + } else if (attr.IsIntRep()) { + value = JSTaggedValue(static_cast(value.GetRawData())); + } + return value; } JSTaggedValue JSObject::GetPropertyInlinedProps(uint32_t index) const @@ -233,11 +295,16 @@ JSTaggedValue JSObject::GetPropertyInlinedProps(uint32_t index) const return GetPropertyInlinedProps(GetJSHClass(), index); } +template void JSObject::SetPropertyInlinedProps(const JSThread *thread, const JSHClass *hclass, uint32_t index, JSTaggedValue value) { uint32_t offset = hclass->GetInlinedPropertiesOffset(index); - SET_VALUE_WITH_BARRIER(thread, this, offset, value); + if (needBarrier) { + SET_VALUE_WITH_BARRIER(thread, this, offset, value); + } else { + SET_VALUE_PRIMITIVE(this, offset, value); + } } JSTaggedValue JSObject::GetPropertyInlinedProps(const JSHClass *hclass, uint32_t index) const @@ -249,19 +316,20 @@ JSTaggedValue JSObject::GetPropertyInlinedProps(const JSHClass *hclass, uint32_t JSTaggedValue JSObject::GetProperty(const JSHClass *hclass, PropertyAttributes attr) const { if (attr.IsInlinedProps()) { - return GetPropertyInlinedProps(hclass, attr.GetOffset()); + return GetPropertyInlinedPropsWithRep(hclass, attr.GetOffset(), attr); } TaggedArray *array = TaggedArray::Cast(GetProperties().GetTaggedObject()); return array->Get(attr.GetOffset() - hclass->GetInlinedProperties()); } +template void JSObject::SetProperty(const JSThread *thread, const JSHClass *hclass, PropertyAttributes attr, JSTaggedValue value) { if (attr.IsInlinedProps()) { - SetPropertyInlinedProps(thread, hclass, attr.GetOffset(), value); + SetPropertyInlinedProps(thread, hclass, attr.GetOffset(), value); } else { TaggedArray *array = TaggedArray::Cast(GetProperties().GetTaggedObject()); - array->Set(thread, attr.GetOffset() - hclass->GetInlinedProperties(), value); + array->Set(thread, attr.GetOffset() - hclass->GetInlinedProperties(), value); } } @@ -270,7 +338,7 @@ inline bool JSObject::ShouldTransToDict(uint32_t capacity, uint32_t index) if (index < capacity) { return false; } - + if (index - capacity > MAX_GAP) { return true; } @@ -286,17 +354,23 @@ inline bool JSObject::ShouldTransToDict(uint32_t capacity, uint32_t index) return false; } -inline uint32_t JSObject::ComputeElementCapacity(uint32_t oldCapacity) +inline uint32_t JSObject::ComputeElementCapacity(uint32_t oldCapacity, bool isNew) +{ + uint32_t newCapacity = isNew ? oldCapacity : (oldCapacity + (oldCapacity >> 1U)); + return newCapacity > MIN_ELEMENTS_LENGTH ? newCapacity : MIN_ELEMENTS_LENGTH; +} + +inline uint32_t JSObject::ComputeElementCapacityHighGrowth(uint32_t oldCapacity) { - uint32_t newCapacity = oldCapacity + (oldCapacity >> 1U); + uint32_t newCapacity = oldCapacity * 2; return newCapacity > MIN_ELEMENTS_LENGTH ? newCapacity : MIN_ELEMENTS_LENGTH; } -inline uint32_t JSObject::ComputePropertyCapacity(uint32_t oldCapacity) +inline uint32_t JSObject::ComputeNonInlinedFastPropsCapacity(uint32_t oldCapacity, + uint32_t maxNonInlinedFastPropsCapacity) { uint32_t newCapacity = static_cast(oldCapacity + PROPERTIES_GROW_SIZE); - return newCapacity > JSHClass::MAX_CAPACITY_OF_OUT_OBJECTS ? JSHClass::MAX_CAPACITY_OF_OUT_OBJECTS - : newCapacity; + return newCapacity > maxNonInlinedFastPropsCapacity ? maxNonInlinedFastPropsCapacity : newCapacity; } // static diff --git a/ecmascript/js_object.cpp b/ecmascript/js_object.cpp index 21d014666b8cdebd7c2930318d2c264f58b3653c..b5b8717748b516452864945c5315053922c6393a 100644 --- a/ecmascript/js_object.cpp +++ b/ecmascript/js_object.cpp @@ -26,12 +26,14 @@ #include "ecmascript/js_iterator.h" #include "ecmascript/js_primitive_ref.h" #include "ecmascript/js_thread.h" -#include "ecmascript/object_factory.h" +#include "ecmascript/object_factory-inl.h" #include "ecmascript/object_fast_operator-inl.h" +#include "ecmascript/pgo_profiler/pgo_profiler.h" #include "ecmascript/property_attributes.h" #include "ecmascript/tagged_array-inl.h" namespace panda::ecmascript { +using PGOProfiler = pgo::PGOProfiler; PropertyAttributes::PropertyAttributes(const PropertyDescriptor &desc) { DISALLOW_GARBAGE_COLLECTION; @@ -71,9 +73,14 @@ Method *ECMAObject::GetCallTarget() const } JSHandle JSObject::GrowElementsCapacity(const JSThread *thread, const JSHandle &obj, - uint32_t capacity) + uint32_t capacity, bool highGrowth, bool isNew) { - uint32_t newCapacity = ComputeElementCapacity(capacity); + uint32_t newCapacity; + if (highGrowth) { + newCapacity = ComputeElementCapacityHighGrowth(capacity); + } else { + newCapacity = ComputeElementCapacity(capacity, isNew); + } ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle oldElements(thread, obj->GetElements()); uint32_t oldLength = oldElements->GetLength(); @@ -102,6 +109,7 @@ JSHandle JSObject::IterableToList(JSThread *thread, const JSHandl // 3. Let values be a new empty List. // 4. Let next be true. JSHandle array = JSHandle::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); JSHandle valuesList = JSHandle::Cast(array); JSMutableHandle next(thread, JSTaggedValue::True()); // 5. Repeat, while next is not false, @@ -162,7 +170,7 @@ JSHandle JSObject::TransitionToDictionary(const JSThread *thread JSTaggedValue value; if (i < numberInlinedProps) { - value = receiver->GetPropertyInlinedProps(i); + value = receiver->GetPropertyInlinedPropsWithRep(i, attr); // If delete a property in hclass which has subtyping info and not prototype, only set value as hole and // not remove. When transition to dictionary, exclude it. if (value.IsHole()) { @@ -235,6 +243,7 @@ bool JSObject::AddElementInternal(JSThread *thread, const JSHandle &re const JSHandle &value, PropertyAttributes attr) { bool isDictionary = receiver->GetJSHClass()->IsDictionaryElement(); + ElementsKind kind = ElementsKind::NONE; if (receiver->IsJSArray()) { DISALLOW_GARBAGE_COLLECTION; JSArray *arr = JSArray::Cast(*receiver); @@ -244,9 +253,12 @@ bool JSObject::AddElementInternal(JSThread *thread, const JSHandle &re return false; } arr->SetArrayLength(thread, index + 1); + if (index > oldLength) { + kind = ElementsKind::HOLE; + } } } - thread->NotifyStableArrayElementsGuardians(receiver); + thread->NotifyStableArrayElementsGuardians(receiver, StableArrayChangeKind::NOT_PROTO); TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject()); if (isDictionary) { @@ -271,7 +283,7 @@ bool JSObject::AddElementInternal(JSThread *thread, const JSHandle &re elements = *JSObject::GrowElementsCapacity(thread, receiver, index + 1); } elements->Set(thread, index, value); - receiver->GetJSHClass()->UpdateRepresentation(value.GetTaggedValue()); + JSHClass::TransitToElementsKind(thread, receiver, value, kind); return true; } @@ -331,7 +343,7 @@ void JSObject::GetAllKeys(const JSThread *thread, const JSHandle &obj, } void JSObject::GetAllKeysByFilter(const JSThread *thread, const JSHandle &obj, - uint32_t& keyArrayEffectivelength, + uint32_t &keyArrayEffectivelength, const JSHandle &keyArray, uint32_t filter) { @@ -339,8 +351,8 @@ void JSObject::GetAllKeysByFilter(const JSThread *thread, const JSHandleIsDictionaryMode()) { uint32_t numberOfProps = obj->GetJSHClass()->NumberOfProps(); if (numberOfProps > 0) { - LayoutInfo::Cast(obj->GetJSHClass()->GetLayout().GetTaggedObject()) - ->GetAllKeysByFilter(thread, numberOfProps, keyArrayEffectivelength, *keyArray, obj, filter); + LayoutInfo::Cast(obj->GetJSHClass()->GetLayout().GetTaggedObject())-> + GetAllKeysByFilter(thread, numberOfProps, keyArrayEffectivelength, *keyArray, obj, filter); } return; } @@ -355,7 +367,7 @@ void JSObject::GetAllKeysByFilter(const JSThread *thread, const JSHandle &obj, std::vector &keyVector) +void JSObject::GetAllKeysForSerialization(const JSHandle &obj, std::vector &keyVector) { DISALLOW_GARBAGE_COLLECTION; ASSERT_PRINT(!obj->IsJSGlobalObject(), "Do not support get key of JSGlobal Object"); @@ -363,7 +375,8 @@ void JSObject::GetAllKeys(const JSHandle &obj, std::vectorIsDictionaryMode()) { int end = static_cast(obj->GetJSHClass()->NumberOfProps()); if (end > 0) { - LayoutInfo::Cast(obj->GetJSHClass()->GetLayout().GetTaggedObject())->GetAllKeys(end, keyVector, obj); + LayoutInfo::Cast(obj->GetJSHClass()->GetLayout().GetTaggedObject())->GetAllKeysForSerialization(end, + keyVector); } } else { NameDictionary *dict = NameDictionary::Cast(obj->GetProperties().GetTaggedObject()); @@ -387,8 +400,9 @@ JSHandle JSObject::GetAllEnumKeys(const JSThread *thread, const JSH JSHClass *jsHclass = obj->GetJSHClass(); JSTaggedValue enumCache = jsHclass->GetEnumCache(); if (!enumCache.IsNull()) { - auto keyArray = JSHandle(thread, enumCache); - *keys = keyArray->GetLength(); + JSHandle cacheArray = JSHandle(thread, enumCache); + *keys = cacheArray->GetLength(); + JSHandle keyArray = factory->CopyArray(cacheArray, *keys, *keys); return keyArray; } JSHandle keyArray = factory->NewTaggedArray(numOfKeys); @@ -398,6 +412,8 @@ JSHandle JSObject::GetAllEnumKeys(const JSThread *thread, const JSH ->GetAllEnumKeys(thread, end, offset, *keyArray, keys, obj); if (*keys == keyArray->GetLength()) { jsHclass->SetEnumCache(thread, keyArray.GetTaggedValue()); + JSHandle newkeyArray = factory->CopyArray(keyArray, *keys, *keys); + return newkeyArray; } } return keyArray; @@ -437,11 +453,10 @@ void JSObject::GetAllElementKeys(JSThread *thread, const JSHandle &obj const JSHandle &keyArray) { uint32_t elementIndex = 0; - if (obj->IsJSPrimitiveRef() && JSPrimitiveRef::Cast(*obj)->IsString()) { elementIndex = JSPrimitiveRef::Cast(*obj)->GetStringLength() + static_cast(offset); for (uint32_t i = static_cast(offset); i < elementIndex; ++i) { - auto key = base::NumberHelper::NumberToString(thread, JSTaggedValue(i)); + auto key = base::NumberHelper::IntToEcmaString(thread, i); keyArray->Set(thread, i, key); } } @@ -451,7 +466,7 @@ void JSObject::GetAllElementKeys(JSThread *thread, const JSHandle &obj uint32_t elementsLen = elements->GetLength(); for (uint32_t i = 0, j = elementIndex; i < elementsLen; ++i) { if (!elements->Get(i).IsHole()) { - auto key = base::NumberHelper::NumberToString(thread, JSTaggedValue(i)); + auto key = base::NumberHelper::IntToEcmaString(thread, i); keyArray->Set(thread, j++, key); } } @@ -463,7 +478,7 @@ void JSObject::GetAllElementKeys(JSThread *thread, const JSHandle &obj void JSObject::GetAllElementKeysByFilter(JSThread *thread, const JSHandle &obj, const JSHandle &keyArray, - uint32_t &keyArrayEffectivelength, + uint32_t &keyArrayEffectiveLength, uint32_t filter) { ASSERT_PRINT(obj->IsECMAObject(), "obj is not object"); @@ -473,14 +488,14 @@ void JSObject::GetAllElementKeysByFilter(JSThread *thread, if ((filter & NATIVE_ENUMERABLE) && obj->IsJSPrimitiveRef() && JSPrimitiveRef::Cast(*obj)->IsString()) { elementIndex = JSPrimitiveRef::Cast(*obj)->GetStringLength(); for (uint32_t i = 0; i < elementIndex; ++i) { - keyArray->Set(thread, keyArrayEffectivelength, JSTaggedValue(i)); - keyArrayEffectivelength++; + keyArray->Set(thread, keyArrayEffectiveLength, JSTaggedValue(i)); + keyArrayEffectiveLength++; } } JSHandle elements(thread, obj->GetElements()); JSHandle objValue(obj); - + if (!elements->IsDictionaryMode()) { uint32_t elementsLen = elements->GetLength(); for (uint32_t i = 0; i < elementsLen; ++i) { @@ -490,13 +505,13 @@ void JSObject::GetAllElementKeysByFilter(JSThread *thread, if (bIgnore) { continue; } - keyArray->Set(thread, keyArrayEffectivelength, JSTaggedValue(i)); - keyArrayEffectivelength++; + keyArray->Set(thread, keyArrayEffectiveLength, JSTaggedValue(i)); + keyArrayEffectiveLength++; } } } else { NumberDictionary::GetAllKeysByFilter(thread, JSHandle(elements), - keyArrayEffectivelength, keyArray, filter); + keyArrayEffectiveLength, keyArray, filter); } } @@ -530,9 +545,8 @@ JSHandle JSObject::GetEnumElementKeys(JSThread *thread, const JSHan *keys += elementIndex; elementIndex += static_cast(offset); for (uint32_t i = static_cast(offset); i < elementIndex; ++i) { - keyHandle.Update(JSTaggedValue(i)); - auto key = JSTaggedValue::ToString(thread, keyHandle); - elementArray->Set(thread, i, key); + keyHandle.Update(base::NumberHelper::IntToEcmaString(thread, i)); + elementArray->Set(thread, i, keyHandle); } } @@ -542,7 +556,7 @@ JSHandle JSObject::GetEnumElementKeys(JSThread *thread, const JSHan uint32_t preElementIndex = elementIndex; for (uint32_t i = 0; i < elementsLen; ++i) { if (!arr->Get(i).IsHole()) { - keyHandle.Update(factory->NewFromASCII(ToCString(i)).GetTaggedValue()); + keyHandle.Update(base::NumberHelper::IntToEcmaString(thread, i)); elementArray->Set(thread, elementIndex++, keyHandle); } } @@ -560,7 +574,7 @@ void JSObject::GetEnumElementKeys(JSThread *thread, const JSHandle &ob if (obj->IsJSPrimitiveRef() && JSPrimitiveRef::Cast(*obj)->IsString()) { elementIndex = JSPrimitiveRef::Cast(*obj)->GetStringLength() + static_cast(offset); for (uint32_t i = static_cast(offset); i < elementIndex; ++i) { - auto key = base::NumberHelper::NumberToString(thread, JSTaggedValue(i)); + auto key = base::NumberHelper::IntToEcmaString(thread, i); keyArray->Set(thread, i, key); } } @@ -570,7 +584,7 @@ void JSObject::GetEnumElementKeys(JSThread *thread, const JSHandle &ob uint32_t elementsLen = elements->GetLength(); for (uint32_t i = 0, j = elementIndex; i < elementsLen; ++i) { if (!elements->Get(i).IsHole()) { - auto key = base::NumberHelper::NumberToString(thread, JSTaggedValue(i)); + auto key = base::NumberHelper::IntToEcmaString(thread, i); keyArray->Set(thread, j++, key); } } @@ -673,6 +687,7 @@ bool JSObject::SetProperty(JSThread *thread, const JSHandle &obj, bool JSObject::SetProperty(ObjectOperator *op, const JSHandle &value, bool mayThrow) { JSThread *thread = op->GetThread(); + op->UpdateDetector(); JSHandle receiver = op->GetReceiver(); JSHandle holder = op->GetHolder(); @@ -799,6 +814,10 @@ bool JSObject::CallSetter(JSThread *thread, const AccessorData &accessor, const } JSHandle func(thread, setter); + if (thread->IsPGOProfilerEnable()) { + auto profiler = thread->GetEcmaVM()->GetPGOProfiler(); + profiler->ProfileCall(func.GetTaggedType()); + } JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, receiver, undefined, 1); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); @@ -821,6 +840,10 @@ JSTaggedValue JSObject::CallGetter(JSThread *thread, const AccessorData *accesso } JSHandle func(thread, getter); + if (thread->IsPGOProfilerEnable()) { + auto profiler = thread->GetEcmaVM()->GetPGOProfiler(); + profiler->ProfileCall(func.GetTaggedType()); + } JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, receiver, undefined, 0); JSTaggedValue res = JSFunction::Call(info); @@ -881,7 +904,8 @@ JSTaggedValue JSObject::GetProperty(JSThread *thread, ObjectOperator *op) JSHandle holder = op->GetHolder(); if (holder->IsJSProxy()) { if (op->IsElement()) { - return JSProxy::GetProperty(thread, JSHandle::Cast(holder), op->GetKey(), receiver) + JSHandle key(thread, JSTaggedValue(op->GetElementIndex())); + return JSProxy::GetProperty(thread, JSHandle::Cast(holder), key, receiver) .GetValue() .GetTaggedValue(); } @@ -973,9 +997,12 @@ bool JSObject::OrdinaryGetOwnProperty(JSThread *thread, const JSHandle op.ToPropertyDescriptor(desc); if (desc.HasValue() && obj->IsJSGlobalObject()) { - PropertyBox *cell = PropertyBox::Cast(desc.GetValue().GetTaggedValue().GetTaggedObject()); - JSHandle valueHandle(thread, cell->GetValue()); - desc.SetValue(valueHandle); + JSTaggedValue val = desc.GetValue().GetTaggedValue(); + if (val.IsPropertyBox()) { + PropertyBox *cell = PropertyBox::Cast(val.GetTaggedObject()); + JSHandle valueHandle(thread, cell->GetValue()); + desc.SetValue(valueHandle); + } } return true; @@ -1039,6 +1066,7 @@ bool JSObject::ValidateAndApplyPropertyDescriptor(ObjectOperator *op, bool exten PropertyAttributes attr(desc); bool success = false; if (!desc.IsAccessorDescriptor()) { + op->UpdateDetector(); success = op->AddPropertyInHolder(desc.GetValue(), attr); } else { // is AccessorDescriptor // may GC in NewAccessorData, so we need to handle getter and setter. @@ -1051,6 +1079,7 @@ bool JSObject::ValidateAndApplyPropertyDescriptor(ObjectOperator *op, bool exten if (desc.HasSetter()) { accessor->SetSetter(thread, desc.GetSetter()); } + op->UpdateDetector(); success = op->AddPropertyInHolder(JSHandle::Cast(accessor), attr); } @@ -1139,6 +1168,7 @@ bool JSObject::ValidateAndApplyPropertyDescriptor(ObjectOperator *op, bool exten // 10. If O is not undefined, then // a. For each field of Desc that is present, set the corresponding attribute of the property named P of object // O to the value of the field. + op->UpdateDetector(); return op->WriteDataPropertyInHolder(desc); } return true; @@ -1187,26 +1217,11 @@ bool JSObject::SetPrototype(JSThread *thread, const JSHandle &obj, con JSHandle hclass(thread, obj->GetJSHClass()); JSHandle newClass = JSHClass::TransitionProto(thread, hclass, proto); JSHClass::NotifyHclassChanged(thread, hclass, newClass); - obj->SetClass(newClass); - thread->NotifyStableArrayElementsGuardians(obj); + obj->SynchronizedSetClass(*newClass); + thread->NotifyStableArrayElementsGuardians(obj, StableArrayChangeKind::PROTO); return true; } -JSTaggedValue JSObject::GetCtorFromPrototype(JSThread *thread, JSTaggedValue prototype) -{ - if (!prototype.IsJSObject()) { - return JSTaggedValue::Undefined(); - } - JSHandle object(thread, prototype); - JSHandle ctorKey = thread->GlobalConstants()->GetHandledConstructorString(); - JSHandle ctorObj(JSObject::GetProperty(thread, object, ctorKey).GetValue()); - if (thread->HasPendingException()) { - thread->ClearException(); - return JSTaggedValue::Undefined(); - } - return ctorObj.GetTaggedValue(); -} - bool JSObject::HasProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key) { ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key"); @@ -1240,7 +1255,7 @@ bool JSObject::PreventExtensions(JSThread *thread, const JSHandle &obj if (obj->IsExtensible()) { JSHandle jshclass(thread, obj->GetJSHClass()); JSHandle newHclass = JSHClass::TransitionExtension(thread, jshclass); - obj->SetClass(newHclass); + obj->SynchronizedSetClass(*newHclass); } return true; @@ -1263,33 +1278,36 @@ JSHandle JSObject::GetOwnPropertyKeys(JSThread *thread, const JSHan JSHandle JSObject::GetAllPropertyKeys(JSThread *thread, const JSHandle &obj, uint32_t filter) { - bool isInculdePrototypes = (filter & NATIVE_KEY_INCLUDE_PROTOTYPES); JSMutableHandle currentObj(thread, obj); JSMutableHandle currentObjValue(thread, currentObj); - + uint32_t curObjNumberOfElements = currentObj->GetNumberOfElements(); uint32_t curObjNumberOfKeys = currentObj->GetNumberOfKeys(); uint32_t curObjectKeysLength = curObjNumberOfElements + curObjNumberOfKeys; - uint32_t retArraylength = curObjectKeysLength; + uint32_t retArrayLength = curObjectKeysLength; ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - JSMutableHandle retArray(thread, factory->NewTaggedArray(retArraylength)); + JSMutableHandle retArray(thread, factory->NewTaggedArray(retArrayLength)); uint32_t retArrayEffectivelength = 0; - + do { curObjNumberOfElements = currentObj->GetNumberOfElements(); curObjNumberOfKeys = currentObj->GetNumberOfKeys(); curObjectKeysLength = curObjNumberOfElements + curObjNumberOfKeys; uint32_t minRequireLength = curObjectKeysLength + retArrayEffectivelength; - if (retArraylength < minRequireLength) { + if (retArrayLength < minRequireLength) { // expand retArray - retArray.Update(factory->NewAndCopyTaggedArray(retArray, minRequireLength, retArraylength)); - retArraylength = minRequireLength; + if (retArrayLength != 0) { + retArray.Update(factory->NewAndCopyTaggedArray(retArray, minRequireLength, retArrayLength)); + } else { + retArray.Update(factory->NewTaggedArray(minRequireLength)); + } + retArrayLength = minRequireLength; } GetAllElementKeysByFilter(thread, currentObj, retArray, retArrayEffectivelength, filter); GetAllKeysByFilter(thread, currentObj, retArrayEffectivelength, retArray, filter); - + bool isInculdePrototypes = (filter & NATIVE_KEY_INCLUDE_PROTOTYPES); if (!isInculdePrototypes) { break; } @@ -1297,6 +1315,9 @@ JSHandle JSObject::GetAllPropertyKeys(JSThread *thread, const JSHan currentObjValue.Update(currentObj); } while (currentObjValue->IsHeapObject()); + if (retArrayEffectivelength == 0 && (filter & NATIVE_KEY_OWN_ONLY)) { + return retArray; + } JSMutableHandle element(thread, JSTaggedValue::Undefined()); if (filter & NATIVE_KEY_NUMBERS_TO_STRINGS) { for (uint32_t i = 0; i < retArrayEffectivelength; i++) { @@ -1308,16 +1329,19 @@ JSHandle JSObject::GetAllPropertyKeys(JSThread *thread, const JSHan } } uint32_t elementIndex = 0; - while ((filter & NATIVE_KEY_SKIP_STRINGS) && (retArrayEffectivelength > 0) && - (elementIndex < retArrayEffectivelength)) { - if (retArray->Get(elementIndex).IsString()) { - TaggedArray::RemoveElementByIndex(thread, retArray, elementIndex, retArrayEffectivelength); - retArrayEffectivelength--; - } else { - elementIndex++; + if (filter & NATIVE_KEY_SKIP_STRINGS) { + while ((retArrayEffectivelength > 0) && (elementIndex < retArrayEffectivelength)) { + if (retArray->Get(elementIndex).IsString()) { + TaggedArray::RemoveElementByIndex(thread, retArray, elementIndex, retArrayEffectivelength); + retArrayEffectivelength--; + } else { + elementIndex++; + } } } - retArray->Trim(thread, retArrayEffectivelength); + if (retArray->GetLength() > retArrayEffectivelength) { + retArray->Trim(thread, retArrayEffectivelength); + } return retArray; } @@ -1512,13 +1536,14 @@ bool JSObject::TestIntegrityLevel(JSThread *thread, const JSHandle &ob handleKey.Update(taggedKey); PropertyDescriptor currentDesc(thread); bool curDescStatus = - JSTaggedValue::GetOwnProperty(thread,JSHandle(obj), handleKey, currentDesc); + JSTaggedValue::GetOwnProperty(thread, JSHandle(obj), handleKey, currentDesc); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); if (curDescStatus) { if (currentDesc.IsConfigurable()) { return false; } - if (level == IntegrityLevel::FROZEN && currentDesc.IsDataDescriptor() && currentDesc.IsWritable()) { + if (level == IntegrityLevel::FROZEN && + currentDesc.IsDataDescriptor() && currentDesc.IsWritable()) { return false; } } @@ -1543,9 +1568,11 @@ JSHandle JSObject::EnumerableOwnNames(JSThread *thread, const JSHan if (copyLengthOfKeys != 0 && copyLengthOfElements != 0) { keys = TaggedArray::AppendSkipHole(thread, elementArray, keyArray, copyLengthOfKeys + copyLengthOfElements); } else if (copyLengthOfKeys != 0) { - keys = factory->CopyArray(keyArray, copyLengthOfKeys, copyLengthOfKeys); + keyArray->SetLength(copyLengthOfKeys); // keyArray will skip nonEnumerable properties, need re-set length. + return keyArray; } else if (copyLengthOfElements != 0) { - keys = factory->CopyArray(elementArray, copyLengthOfElements, copyLengthOfElements); + elementArray->SetLength(copyLengthOfElements); // elementArray will skip hole value, need re-set length. + return elementArray; } else { keys = factory->EmptyArray(); } @@ -1746,6 +1773,7 @@ bool JSObject::InstanceOf(JSThread *thread, const JSHandle &objec JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, instOfHandler, target, undefined, 1); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); info->SetCallArg(object.GetTaggedValue()); JSTaggedValue tagged = JSFunction::Call(info); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); @@ -2099,7 +2127,8 @@ void JSObject::DefineGetter(JSThread *thread, const JSHandle &obj op.DefineGetter(value); } -JSHandle JSObject::CreateObjectFromProperties(const JSThread *thread, const JSHandle &properties) +JSHandle JSObject::CreateObjectFromProperties(const JSThread *thread, const JSHandle &properties, + JSTaggedValue ihcVal) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); size_t length = properties->GetLength(); @@ -2110,13 +2139,10 @@ JSHandle JSObject::CreateObjectFromProperties(const JSThread *thread, } propsLen++; } - if (propsLen <= PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES) { + if (propsLen <= PropertyAttributes::MAX_FAST_PROPS_CAPACITY) { JSHandle obj = factory->NewOldSpaceObjLiteralByHClass(properties, propsLen); ASSERT_PRINT(obj->IsECMAObject(), "Obj is not a valid object"); - for (size_t i = 0; i < propsLen; i++) { - // 2: literal contains a pair of key-value - obj->SetPropertyInlinedProps(thread, i, properties->Get(i * 2 + 1)); - } + SetAllPropertys(thread, obj, properties, propsLen, ihcVal); return obj; } else { JSHandle obj = factory->NewEmptyJSObject(); @@ -2140,6 +2166,52 @@ JSHandle JSObject::CreateObjectFromProperties(const JSThread *thread, } } +void JSObject::SetAllPropertys(const JSThread *thread, JSHandle &obj, const JSHandle &properties, + uint32_t propsLen, JSTaggedValue ihcVal) +{ + // AOT runtime + if (ihcVal.IsJSHClass()) { + bool isSuccess = true; + JSHClass *ihc = JSHClass::Cast(ihcVal.GetTaggedObject()); + JSHClass *oldHC = obj->GetJSHClass(); + ihc->SetPrototype(thread, oldHC->GetPrototype()); + obj->SetClass(ihc); + for (size_t i = 0; i < propsLen; i++) { + auto value = obj->ConvertValueWithRep(i, properties->Get(i * 2 + 1)); + // If value.first is false, indicating that value cannot be converted to the expected value of + // representation. For example, the representation is INT, but the value type is string. + if (!value.first) { + isSuccess = false; + break; + } + obj->SetPropertyInlinedPropsWithRep(thread, i, value.second); + } + if (isSuccess) { + return; + } + // If conversion fails, it needs to be rolled back to the old HClass and reset the value. + obj->SetClass(oldHC); + } else if (thread->IsPGOProfilerEnable()) { + // PGO need to track TrackType + JSHClass *oldHC = obj->GetJSHClass(); + LayoutInfo *layoutInfo = LayoutInfo::Cast(oldHC->GetLayout().GetTaggedObject()); + for (size_t i = 0; i < propsLen; i++) { + auto value = properties->Get(i * 2 + 1); + auto attr = layoutInfo->GetAttr(i); + if (attr.UpdateTrackType(value)) { + layoutInfo->SetNormalAttr(thread, i, attr); + } + obj->SetPropertyInlinedProps(thread, i, value); + } + return; + } + // Interpreter runtime or track field initialized fail. + for (size_t i = 0; i < propsLen; i++) { + // 2: literal contains a pair of key-value + obj->SetPropertyInlinedProps(thread, i, properties->Get(i * 2 + 1)); + } +} + void JSObject::AddAccessor(JSThread *thread, const JSHandle &obj, const JSHandle &key, const JSHandle &value, PropertyAttributes attr) { @@ -2203,9 +2275,7 @@ int32_t ECMAObject::GetHash() const return 0; } } - JSThread *thread = this->GetJSThread(); - JSHandle valueHandle(thread, value); - return JSTaggedValue::ToInt32(thread, valueHandle); + return value.GetInt(); } bool ECMAObject::HasHash() const diff --git a/ecmascript/js_object.h b/ecmascript/js_object.h index ef5855a3b270f6da07820714ce7eaf752fc87e68..bd8c15bfbdcd01a42db9e9580ffcc667f2347d78 100644 --- a/ecmascript/js_object.h +++ b/ecmascript/js_object.h @@ -427,8 +427,6 @@ public: // [[SetPrototypeOf]] static bool SetPrototype(JSThread *thread, const JSHandle &obj, const JSHandle &proto); - static JSTaggedValue GetCtorFromPrototype(JSThread *thread, JSTaggedValue prototype); - // [[IsExtensible]] bool IsExtensible() const; @@ -527,6 +525,7 @@ public: void FillElementsWithHoles(const JSThread *thread, uint32_t start, uint32_t end); JSHClass *GetJSHClass() const; + uint32_t GetNonInlinedFastPropsCapacity() const; bool IsJSGlobalObject() const; bool IsConstructor() const; bool IsECMAObject() const; @@ -569,13 +568,14 @@ public: static void DefineGetter(JSThread *thread, const JSHandle &obj, const JSHandle &key, const JSHandle &value); static JSHandle CreateObjectFromProperties(const JSThread *thread, - const JSHandle &properties); + const JSHandle &properties, + JSTaggedValue ihc = JSTaggedValue::Undefined()); static void GetAllKeys(const JSThread *thread, const JSHandle &obj, int offset, const JSHandle &keyArray); - static void GetAllKeys(const JSHandle &obj, std::vector &keyVector); + static void GetAllKeysForSerialization(const JSHandle &obj, std::vector &keyVector); static void GetAllKeysByFilter(const JSThread *thread, const JSHandle &obj, - uint32_t& keyArrayEffectivelength, + uint32_t &keyArrayEffectivelength, const JSHandle &keyArray, uint32_t filter); static void GetAllElementKeys(JSThread *thread, const JSHandle &obj, int offset, @@ -583,7 +583,7 @@ public: static void GetAllElementKeysByFilter(JSThread *thread, const JSHandle &obj, const JSHandle &keyArray, - uint32_t &keyArrayEffectivelength, + uint32_t &keyArrayEffectiveLength, uint32_t filter); static void GetALLElementKeysIntoVector(const JSThread *thread, const JSHandle &obj, @@ -614,12 +614,20 @@ public: static JSHandle TransitionToDictionary(const JSThread *thread, const JSHandle &receiver); + inline std::pair ConvertValueWithRep(uint32_t index, JSTaggedValue value); + inline void SetPropertyInlinedPropsWithRep(const JSThread *thread, uint32_t index, JSTaggedValue value); + template inline void SetPropertyInlinedProps(const JSThread *thread, uint32_t index, JSTaggedValue value); + template inline void SetPropertyInlinedProps(const JSThread *thread, const JSHClass *hclass, uint32_t index, JSTaggedValue value); + inline JSTaggedValue GetPropertyInlinedPropsWithRep(uint32_t index, PropertyAttributes attr) const; + inline JSTaggedValue GetPropertyInlinedPropsWithRep(const JSHClass *hclass, uint32_t index, + PropertyAttributes attr) const; inline JSTaggedValue GetPropertyInlinedProps(uint32_t index) const; inline JSTaggedValue GetPropertyInlinedProps(const JSHClass *hclass, uint32_t index) const; inline JSTaggedValue GetProperty(const JSHClass *hclass, PropertyAttributes attr) const; + template inline void SetProperty(const JSThread *thread, const JSHClass *hclass, PropertyAttributes attr, JSTaggedValue value); @@ -627,7 +635,7 @@ public: bool UpdatePropertyInDictionary(const JSThread *thread, JSTaggedValue key, JSTaggedValue value); static bool ShouldTransToDict(uint32_t capacity, uint32_t index); static JSHandle GrowElementsCapacity(const JSThread *thread, const JSHandle &obj, - uint32_t capacity); + uint32_t capacity, bool highGrowth = false, bool isNew = false); static JSHandle IterableToList(JSThread *thread, const JSHandle &items, JSTaggedValue method = JSTaggedValue::Undefined()); @@ -651,10 +659,13 @@ private: static bool SetProperty(ObjectOperator *op, const JSHandle &value, bool mayThrow); static void DeletePropertyInternal(JSThread *thread, const JSHandle &obj, const JSHandle &key, uint32_t index); + static void SetAllPropertys(const JSThread *thread, JSHandle &obj, + const JSHandle &properties, uint32_t propsLen, JSTaggedValue ihcVal); int FindProperty(const JSHandle &key); - static uint32_t ComputeElementCapacity(uint32_t oldCapacity); - static uint32_t ComputePropertyCapacity(uint32_t oldCapacity); + static uint32_t ComputeElementCapacity(uint32_t oldCapacity, bool isNew = false); + static uint32_t ComputeElementCapacityHighGrowth(uint32_t oldCapacity); + static uint32_t ComputeNonInlinedFastPropsCapacity(uint32_t oldCapacity, uint32_t maxNonInlinedFastPropsCapacity); static JSTaggedValue ShouldGetValueFromBox(ObjectOperator *op); static std::pair, JSHandle> GetOwnEnumerableNamesInFastMode( diff --git a/ecmascript/js_plural_rules.cpp b/ecmascript/js_plural_rules.cpp index f95e0d28adab9492ec3a64522728630fed169842..bbf14ec131f0a893f7bd745fe14feeeb22efb979 100644 --- a/ecmascript/js_plural_rules.cpp +++ b/ecmascript/js_plural_rules.cpp @@ -19,6 +19,7 @@ #include "ecmascript/ecma_macros.h" #include "ecmascript/global_env.h" #include "ecmascript/global_env_constants.h" +#include "ecmascript/object_factory-inl.h" #include "ecmascript/js_number_format.h" namespace panda::ecmascript { @@ -256,6 +257,7 @@ JSHandle JSPluralRules::InitializePluralRules(JSThread *thread, // 12. Set pluralRules.[[Locale]] to the value of r.[[locale]]. JSHandle localeStr = intl::LocaleHelper::ToLanguageTag(thread, icuLocale); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSPluralRules, thread); pluralRules->SetLocale(thread, localeStr.GetTaggedValue()); // 13. Return pluralRules. @@ -325,6 +327,7 @@ void JSPluralRules::ResolvedOptions(JSThread *thread, const JSHandle::Cast(globalConst->GetHandledMinimumIntegerDigitsString()); JSHandle minimumIntegerDigits(thread, pluralRules->GetMinimumIntegerDigits()); JSObject::CreateDataPropertyOrThrow(thread, options, property, minimumIntegerDigits); + RETURN_IF_ABRUPT_COMPLETION(thread); RoundingType roundingType = pluralRules->GetRoundingType(); if (roundingType == RoundingType::SIGNIFICANTDIGITS) { @@ -332,19 +335,23 @@ void JSPluralRules::ResolvedOptions(JSThread *thread, const JSHandleGetHandledMinimumSignificantDigitsString(); JSHandle minimumSignificantDigits(thread, pluralRules->GetMinimumSignificantDigits()); JSObject::CreateDataPropertyOrThrow(thread, options, property, minimumSignificantDigits); + RETURN_IF_ABRUPT_COMPLETION(thread); // [[MaximumSignificantDigits]] property = globalConst->GetHandledMaximumSignificantDigitsString(); JSHandle maximumSignificantDigits(thread, pluralRules->GetMaximumSignificantDigits()); JSObject::CreateDataPropertyOrThrow(thread, options, property, maximumSignificantDigits); + RETURN_IF_ABRUPT_COMPLETION(thread); } else { // [[MinimumFractionDigits]] property = globalConst->GetHandledMinimumFractionDigitsString(); JSHandle minimumFractionDigits(thread, pluralRules->GetMinimumFractionDigits()); JSObject::CreateDataPropertyOrThrow(thread, options, property, minimumFractionDigits); + RETURN_IF_ABRUPT_COMPLETION(thread); // [[MaximumFractionDigits]] property = globalConst->GetHandledMaximumFractionDigitsString(); JSHandle maximumFractionDigits(thread, pluralRules->GetMaximumFractionDigits()); JSObject::CreateDataPropertyOrThrow(thread, options, property, maximumFractionDigits); + RETURN_IF_ABRUPT_COMPLETION(thread); } // 5. Let pluralCategories be a List of Strings representing the possible results of PluralRuleSelect diff --git a/ecmascript/js_primitive_ref.cpp b/ecmascript/js_primitive_ref.cpp index df91649167afaf577521e42da45860548de50180..3a867c91da0a2e810a18f9e418dc126ca05bb339 100644 --- a/ecmascript/js_primitive_ref.cpp +++ b/ecmascript/js_primitive_ref.cpp @@ -42,6 +42,7 @@ JSHandle JSPrimitiveRef::StringCreate(JSThread *thread, const JS // [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }). PropertyDescriptor desc(thread, JSHandle(thread, JSTaggedValue(length)), false, false, false); [[maybe_unused]] bool status = JSTaggedValue::DefinePropertyOrThrow(thread, str, lengthStr, desc); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSPrimitiveRef, thread); ASSERT(status); // 9. Return S. return JSHandle(str); diff --git a/ecmascript/js_proxy.cpp b/ecmascript/js_proxy.cpp index f9d841e659880984602f24524108cd58d71c40bc..51e80831285e346f6e78693335251773d472d26d 100644 --- a/ecmascript/js_proxy.cpp +++ b/ecmascript/js_proxy.cpp @@ -132,13 +132,13 @@ bool JSProxy::SetPrototype(JSThread *thread, const JSHandle &proxy, con return JSTaggedValue::SetPrototype(thread, targetHandle, proto); } JSHandle handlerTag(thread, proxy->GetHandler()); - const int32_t argsLength = 2; // 2: target and proto + const uint32_t argsLength = 2; // 2: target and proto JSHandle undefined = globalConst->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handlerTag, undefined, argsLength); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); info->SetCallArg(targetHandle.GetTaggedValue(), proto.GetTaggedValue()); JSTaggedValue trapResult = JSFunction::Call(info); - + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); // 9. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target, V»)). // If booleanTrapResult is false, return false bool booleanTrapResult = trapResult.ToBoolean(); @@ -199,7 +199,7 @@ bool JSProxy::IsExtensible(JSThread *thread, const JSHandle &proxy) RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); info->SetCallArg(targetHandle.GetTaggedValue()); JSTaggedValue trapResult = JSFunction::Call(info); - + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); bool booleanTrapResult = trapResult.ToBoolean(); // 9. ReturnIfAbrupt(booleanTrapResult). RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); @@ -249,7 +249,7 @@ bool JSProxy::PreventExtensions(JSThread *thread, const JSHandle &proxy RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); info->SetCallArg(targetHandle.GetTaggedValue()); JSTaggedValue trapResult = JSFunction::Call(info); - + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); bool booleanTrapResult = trapResult.ToBoolean(); // 9. ReturnIfAbrupt(booleanTrapResult). RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); @@ -297,12 +297,13 @@ bool JSProxy::GetOwnProperty(JSThread *thread, const JSHandle &proxy, c return JSTaggedValue::GetOwnProperty(thread, targetHandle, key, desc); } JSHandle handlerTag(thread, proxy->GetHandler()); - const int32_t argsLength = 2; // 2: target and key + const uint32_t argsLength = 2; // 2: target and key JSHandle undefined = globalConst->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handlerTag, undefined, argsLength); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); info->SetCallArg(targetHandle.GetTaggedValue(), key.GetTaggedValue()); JSTaggedValue trapResultObj = JSFunction::Call(info); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); JSHandle resultHandle(thread, trapResultObj); @@ -395,13 +396,13 @@ bool JSProxy::DefineOwnProperty(JSThread *thread, const JSHandle &proxy // 9. Let descObj be FromPropertyDescriptor(Desc). JSHandle descObj = JSObject::FromPropertyDescriptor(thread, desc); JSHandle handlerTag(thread, proxy->GetHandler()); - const int32_t argsLength = 3; // 3: target, key and desc + const uint32_t argsLength = 3; // 3: target, key and desc JSHandle undefined = globalConst->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handlerTag, undefined, argsLength); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); info->SetCallArg(targetHandle.GetTaggedValue(), key.GetTaggedValue(), descObj.GetTaggedValue()); JSTaggedValue trapResult = JSFunction::Call(info); - + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); bool booleanTrapResult = trapResult.ToBoolean(); // 11. ReturnIfAbrupt(booleanTrapResult). RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); @@ -483,13 +484,13 @@ bool JSProxy::HasProperty(JSThread *thread, const JSHandle &proxy, cons // 9. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target, P»)). JSHandle handlerTag(thread, proxy->GetHandler()); - const int32_t argsLength = 2; // 2: target and key + const uint32_t argsLength = 2; // 2: target and key JSHandle undefined = globalConst->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handlerTag, undefined, argsLength); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); info->SetCallArg(targetHandle.GetTaggedValue(), key.GetTaggedValue()); JSTaggedValue trapResult = JSFunction::Call(info); - + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); bool booleanTrapResult = trapResult.ToBoolean(); // 10. ReturnIfAbrupt(booleanTrapResult). RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); @@ -522,6 +523,18 @@ bool JSProxy::HasProperty(JSThread *thread, const JSHandle &proxy, cons OperationResult JSProxy::GetProperty(JSThread *thread, const JSHandle &proxy, const JSHandle &key, const JSHandle &receiver) { + // check stack overflow because infinite recursion may occur + if (thread->IsAsmInterpreter() && UNLIKELY(thread->GetCurrentStackPosition() < thread->GetStackLimit())) { + LOG_ECMA(ERROR) << "Stack overflow! current:" << thread->GetCurrentStackPosition() + << " limit:" << thread->GetStackLimit(); + if (LIKELY(!thread->HasPendingException())) { + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle error = factory->GetJSError(base::ErrorType::RANGE_ERROR, "Stack overflow!", false); + thread->SetException(error.GetTaggedValue()); + } + return OperationResult(thread, thread->GetException(), PropertyMetaData(false)); + } + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); // step 1 ~ 10 are almost same as GetOwnProperty ASSERT(JSTaggedValue::IsPropertyKey(key)); @@ -544,7 +557,7 @@ OperationResult JSProxy::GetProperty(JSThread *thread, const JSHandle & } // 9. Let trapResult be Call(trap, handler, «target, P, Receiver»). JSHandle handlerTag(thread, proxy->GetHandler()); - const int32_t argsLength = 3; // 3: «target, P, Receiver» + const uint32_t argsLength = 3; // 3: «target, P, Receiver» JSHandle undefined = globalConst->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handlerTag, undefined, argsLength); RETURN_VALUE_IF_ABRUPT_COMPLETION( @@ -615,14 +628,14 @@ bool JSProxy::SetProperty(JSThread *thread, const JSHandle &proxy, cons // 9. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target, P, V, Receiver»)) JSHandle handlerTag(thread, proxy->GetHandler()); - const int32_t argsLength = 4; // 4: «target, P, V, Receiver» + const uint32_t argsLength = 4; // 4: «target, P, V, Receiver» JSHandle undefined = globalConst->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handlerTag, undefined, argsLength); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); info->SetCallArg( targetHandle.GetTaggedValue(), key.GetTaggedValue(), value.GetTaggedValue(), receiver.GetTaggedValue()); JSTaggedValue trapResult = JSFunction::Call(info); - + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); bool booleanTrapResult = trapResult.ToBoolean(); // 11. ReturnIfAbrupt(booleanTrapResult). RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); @@ -675,13 +688,13 @@ bool JSProxy::DeleteProperty(JSThread *thread, const JSHandle &proxy, c // 9. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target, P»)). JSHandle newTgt(thread, JSTaggedValue::Undefined()); JSHandle handlerTag(thread, proxy->GetHandler()); - const int32_t argsLength = 2; // 2: target and key + const uint32_t argsLength = 2; // 2: target and key JSHandle undefined = globalConst->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handlerTag, undefined, argsLength); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); info->SetCallArg(targetHandle.GetTaggedValue(), key.GetTaggedValue()); JSTaggedValue trapResult = JSFunction::Call(info); - + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); bool booleanTrapResult = trapResult.ToBoolean(); // 11. ReturnIfAbrupt(booleanTrapResult). RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); @@ -856,6 +869,72 @@ JSHandle JSProxy::OwnPropertyKeys(JSThread *thread, const JSHandle< return trapRes; } +JSHandle JSProxy::GetAllPropertyKeys(JSThread *thread, const JSHandle &proxy, uint32_t filter) +{ + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + + JSTaggedValue handler = proxy->GetHandler(); + if (handler.IsNull()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "OwnPropertyKeys: handler is null", + JSHandle(thread, JSTaggedValue::Exception())); + } + + ASSERT(handler.IsECMAObject()); + JSHandle targetHandle(thread, proxy->GetTarget()); + + JSHandle key = globalConst->GetHandledOwnKeysString(); + JSHandle handlerHandle(thread, handler); + JSHandle trap(JSObject::GetMethod(thread, handlerHandle, key)); + + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + + if (trap->IsUndefined()) { + return JSTaggedValue::GetAllPropertyKeys(thread, targetHandle, filter); + } + + JSHandle undefined = globalConst->GetHandledUndefined(); + EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, trap, handlerHandle, undefined, 1); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + info->SetCallArg(targetHandle.GetTaggedValue()); + JSTaggedValue res = JSFunction::Call(info); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); + JSHandle trapResArr(thread, res); + + JSHandle trapRes( + JSObject::CreateListFromArrayLike(thread, trapResArr)); + JSHandle ownKeys = JSTaggedValue::GetOwnPropertyKeys(thread, targetHandle); + JSHandle reciveArray = JSTaggedValue::GetAllPropertyKeys(thread, targetHandle, filter); + + uint32_t trapResLength = trapRes->GetLength(); + uint32_t ownKeysLength = ownKeys->GetLength(); + uint32_t reciveArrayLength = reciveArray->GetLength(); + uint32_t newArrayLength = reciveArrayLength + trapResLength - ownKeysLength; + + JSHandle resArray = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(newArrayLength); + + uint32_t elementIndex = 0; + if (filter & NATIVE_KEY_SKIP_SYMBOLS) { + for (uint32_t index = 0; index < reciveArrayLength; index++) { + if (!ownKeys->Get(index).IsSymbol()) { + resArray->Set(thread, elementIndex, reciveArray->Get(index)); + elementIndex++; + } + } + return resArray; + } + + for (uint32_t i = 0; i < trapResLength; i++) { + resArray->Set(thread, i, trapRes->Get(i)); + } + + uint32_t index = ownKeysLength; + for (uint32_t j = 0; j < reciveArrayLength - ownKeysLength; j++) { + resArray->Set(thread, trapResLength + j, reciveArray->Get(index)); + index++; + } + return resArray; +} + // ES6 9.5.13 [[Call]] (thisArgument, argumentsList) JSTaggedValue JSProxy::CallInternal(EcmaRuntimeCallInfo *info) { @@ -953,7 +1032,7 @@ JSTaggedValue JSProxy::ConstructInternal(EcmaRuntimeCallInfo *info) // step 8 ~ 9 Call(trap, handler, «target, argArray, newTarget »). JSHandle newTarget(info->GetNewTarget()); - const int32_t argsLength = 3; // 3: «target, argArray, newTarget » + const uint32_t argsLength = 3; // 3: «target, argArray, newTarget » JSHandle undefined = globalConst->GetHandledUndefined(); EcmaRuntimeCallInfo *runtimeInfo = EcmaInterpreter::NewRuntimeCallInfo(thread, method, handler, undefined, argsLength); diff --git a/ecmascript/js_proxy.h b/ecmascript/js_proxy.h index 6745a2f15161b1e052e9890376ca845a76df9acd..15cad4bb1cb22a0cf6df33a0d2d2b2a4a11ea216 100644 --- a/ecmascript/js_proxy.h +++ b/ecmascript/js_proxy.h @@ -67,6 +67,8 @@ public: // ES6 9.5.12 [[OwnPropertyKeys]] () static JSHandle OwnPropertyKeys(JSThread *thread, const JSHandle &proxy); + static JSHandle GetAllPropertyKeys(JSThread *thread, const JSHandle &proxy, uint32_t filter); + void SetCallable(bool callable) const { GetClass()->SetCallable(callable); @@ -89,12 +91,17 @@ public: static constexpr size_t TARGET_OFFSET = ECMAObject::SIZE; ACCESSORS(Target, TARGET_OFFSET, HANDLER_OFFSET) ACCESSORS(Handler, HANDLER_OFFSET, METHOD_OFFSET) - ACCESSORS(Method, METHOD_OFFSET, LAST_OFFSET) + ACCESSORS(Method, METHOD_OFFSET, BIT_FIELD_OFFSET) + ACCESSORS_BIT_FIELD(BitField, BIT_FIELD_OFFSET, LAST_OFFSET) DEFINE_ALIGN_SIZE(LAST_OFFSET); + // define BitField + static constexpr size_t IS_REVOKED_BITS = 1; + FIRST_BIT_FIELD(BitField, IsRevoked, bool, IS_REVOKED_BITS) + DECL_DUMP() - DECL_VISIT_OBJECT(TARGET_OFFSET, LAST_OFFSET) + DECL_VISIT_OBJECT(TARGET_OFFSET, BIT_FIELD_OFFSET) }; } // namespace panda::ecmascript diff --git a/ecmascript/js_relative_time_format.cpp b/ecmascript/js_relative_time_format.cpp index c52d3e46411a75762e1a8f2ff47fcc867899ace7..8821aa2fcb31260710c87703ac4afff05b1cce3c 100644 --- a/ecmascript/js_relative_time_format.cpp +++ b/ecmascript/js_relative_time_format.cpp @@ -14,6 +14,7 @@ */ #include "ecmascript/js_relative_time_format.h" +#include "ecmascript/object_factory-inl.h" #include "unicode/decimfmt.h" #include "unicode/numfmt.h" @@ -88,6 +89,7 @@ JSHandle JSRelativeTimeFormat::InitializeRelativeTimeForma // 12. Let locale be r.[[Locale]]. JSHandle localeStr = intl::LocaleHelper::ToLanguageTag(thread, icuLocale); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSRelativeTimeFormat, thread); // 13. Set relativeTimeFormat.[[Locale]] to locale. relativeTimeFormat->SetLocale(thread, localeStr.GetTaggedValue()); @@ -478,6 +480,7 @@ JSHandle JSRelativeTimeFormat::FormatToParts(JSThread *thread, double v RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread); JSHandle singularUnit = SingularUnitString(thread, unit); JSHandle array = JSHandle::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0))); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSArray, thread); FormatToArray(thread, array, formatted, value, singularUnit); return array; } diff --git a/ecmascript/js_runtime_options.cpp b/ecmascript/js_runtime_options.cpp index f0195400c673432ec46e5ff7b43033d1bbb2e4a9..d2e0e88d95a9ba4263b5e3b18a79c7a4c1ce3dfe 100644 --- a/ecmascript/js_runtime_options.cpp +++ b/ecmascript/js_runtime_options.cpp @@ -76,12 +76,14 @@ const std::string PUBLIC_API HELP_OPTION_MSG = "--enable-force-gc: Enable force gc when allocating object. Default: 'true'\n" "--enable-ic: Switch of inline cache. Default: 'true'\n" "--enable-runtime-stat: Enable statistics of runtime state. Default: 'false'\n" + "--compiler-opt-array-bounds-check-elimination: Enable Index Check elimination. Default: 'false'\n" "--compiler-opt-type-lowering: Enable all type optimization pass for aot compiler. Default: 'true'\n" "--compiler-opt-early-elimination: Enable EarlyElimination for aot compiler. Default: 'true'\n" "--compiler-opt-later-elimination: Enable LaterElimination for aot compiler. Default: 'true'\n" "--compiler-opt-value-numbering: Enable ValueNumbering for aot compiler. Default: 'true'\n" "--compiler-opt-inlining: Enable inlining function for aot compiler: Default: 'true'\n" "--compiler-opt-pgotype: Enable pgo type for aot compiler: Default: 'true'\n" + "--compiler-opt-track-field: Enable track field for aot compiler: Default: 'false'\n" "--entry-point: Full name of entrypoint function. Default: '_GLOBAL::func_main_0'\n" "--force-full-gc: If true trigger full gc, else trigger semi and old gc. Default: 'true'\n" "--framework-abc-file: Snapshot file. Default: 'strip.native.min.abc'\n" @@ -137,9 +139,15 @@ const std::string PUBLIC_API HELP_OPTION_MSG = " Format:--compile-skip-methods=record1:m1,m2,record2:m3\n" "--target-compiler-mode The compilation mode at the device side, including partial, full and none." " Default: ''\n" - "--hap-path The path of the app hap. Default: ''\n" - "--hap-abc-offset The offset of the abc file in app hap. Default: '0'\n" - "--hap-abc-size The size of the abc file in app hap. Default: '0'\n\n"; + "--hap-path(Deprecated) The path of the app hap. Default: ''\n" + "--hap-abc-offset(Deprecated) The offset of the abc file in app hap. Default: '0'\n" + "--hap-abc-size(Deprecated) The size of the abc file in app hap. Default: '0'\n" + "--compiler-fast-compile Disable some time-consuming pass. Default: 'true'\n" + "--compiler-no-check Enable remove checks for aot compiler. Default: 'false'\n" + "--compiler-opt-loop-peeling: Enable loop peeling for aot compiler: Default: 'false'\n" + "--compiler-pkg-info Specify the package json info for ark aot compiler\n" + "--compiler-external-pkg-info Specify the external package json info for ark aot compiler\n" + "--compiler-opt-array-onheap-check: Enable TypedArray on heap check for aot compiler: Default: 'false'\n\n"; bool JSRuntimeOptions::ParseCommand(const int argc, const char **argv) { @@ -169,12 +177,15 @@ bool JSRuntimeOptions::ParseCommand(const int argc, const char **argv) {"enable-force-gc", required_argument, nullptr, OPTION_ENABLE_FORCE_GC}, {"enable-ic", required_argument, nullptr, OPTION_ENABLE_IC}, {"enable-runtime-stat", required_argument, nullptr, OPTION_ENABLE_RUNTIME_STAT}, + {"compiler-opt-array-bounds-check-elimination", required_argument, nullptr, + OPTION_COMPILER_OPT_ARRAY_BOUNDS_CHECK_ELIMINATION}, {"compiler-opt-type-lowering", required_argument, nullptr, OPTION_COMPILER_OPT_TYPE_LOWERING}, {"compiler-opt-early-elimination", required_argument, nullptr, OPTION_COMPILER_OPT_EARLY_ELIMINATION}, {"compiler-opt-later-elimination", required_argument, nullptr, OPTION_COMPILER_OPT_LATER_ELIMINATION}, {"compiler-opt-value-numbering", required_argument, nullptr, OPTION_COMPILER_OPT_VALUE_NUMBERING}, {"compiler-opt-inlining", required_argument, nullptr, OPTION_COMPILER_OPT_INLINING}, {"compiler-opt-pgotype", required_argument, nullptr, OPTION_COMPILER_OPT_PGOTYPE}, + {"compiler-opt-track-field", required_argument, nullptr, OPTION_COMPILER_OPT_TRACK_FIELD}, {"entry-point", required_argument, nullptr, OPTION_ENTRY_POINT}, {"force-full-gc", required_argument, nullptr, OPTION_FORCE_FULL_GC}, {"gc-thread-num", required_argument, nullptr, OPTION_GC_THREADNUM}, @@ -213,6 +224,12 @@ bool JSRuntimeOptions::ParseCommand(const int argc, const char **argv) {"hap-path", required_argument, nullptr, OPTION_HAP_PATH}, {"hap-abc-offset", required_argument, nullptr, OPTION_HAP_ABC_OFFSET}, {"hap-abc-size", required_argument, nullptr, OPTION_HAP_ABC_SIZE}, + {"compiler-no-check", required_argument, nullptr, OPTION_COMPILER_NOCHECK}, + {"compiler-fast-compile", required_argument, nullptr, OPTION_FAST_AOT_COMPILE_MODE}, + {"compiler-opt-loop-peeling", required_argument, nullptr, OPTION_COMPILER_OPT_LOOP_PEELING}, + {"compiler-opt-array-onheap-check", required_argument, nullptr, OPTION_COMPILER_OPT_ON_HEAP_CHECK}, + {"compiler-pkg-info", required_argument, nullptr, OPTION_COMPILER_PKG_INFO}, + {"compiler-external-pkg-info", required_argument, nullptr, OPTION_COMPILER_EXTERNAL_PKG_INFO}, {nullptr, 0, nullptr, 0}, }; @@ -608,6 +625,14 @@ bool JSRuntimeOptions::ParseCommand(const int argc, const char **argv) return false; } break; + case OPTION_COMPILER_OPT_ARRAY_BOUNDS_CHECK_ELIMINATION: + ret = ParseBoolParam(&argBool); + if (ret) { + SetEnableArrayBoundsCheckElimination(argBool); + } else { + return false; + } + break; case OPTION_COMPILER_OPT_EARLY_ELIMINATION: ret = ParseBoolParam(&argBool); if (ret) { @@ -648,6 +673,14 @@ bool JSRuntimeOptions::ParseCommand(const int argc, const char **argv) return false; } break; + case OPTION_COMPILER_OPT_TRACK_FIELD: + ret = ParseBoolParam(&argBool); + if (ret) { + SetEnableOptTrackField(argBool); + } else { + return false; + } + break; case OPTION_COMPILER_OPT_GLOBAL_TYPEINFER: ret = ParseBoolParam(&argBool); if (ret) { @@ -690,6 +723,42 @@ bool JSRuntimeOptions::ParseCommand(const int argc, const char **argv) } SetHapAbcSize(argUint32); break; + case OPTION_COMPILER_NOCHECK: + ret = ParseBoolParam(&argBool); + if (!ret) { + return false; + } + SetCompilerNoCheck(argBool); + break; + case OPTION_FAST_AOT_COMPILE_MODE: + ret = ParseBoolParam(&argBool); + if (!ret) { + return false; + } + SetFastAOTCompileMode(argBool); + break; + case OPTION_COMPILER_OPT_LOOP_PEELING: + ret = ParseBoolParam(&argBool); + if (ret) { + SetEnableOptLoopPeeling(argBool); + } else { + return false; + } + break; + case OPTION_COMPILER_OPT_ON_HEAP_CHECK: + ret = ParseBoolParam(&argBool); + if (ret) { + SetEnableOptOnHeapCheck(argBool); + } else { + return false; + } + break; + case OPTION_COMPILER_PKG_INFO: + SetCompilerPkgJsonInfo(optarg); + break; + case OPTION_COMPILER_EXTERNAL_PKG_INFO: + SetCompilerExternalPkgJsonInfo(optarg); + break; default: LOG_ECMA(ERROR) << "Invalid option\n"; return false; diff --git a/ecmascript/js_runtime_options.h b/ecmascript/js_runtime_options.h index 246049540e1a5268c0a03f6a50c40d59bcb0606b..8167b9740ec9254057a908ce15f5129653676591 100644 --- a/ecmascript/js_runtime_options.h +++ b/ecmascript/js_runtime_options.h @@ -119,6 +119,7 @@ enum CommandValues { OPTION_COMPILER_OPT_VALUE_NUMBERING, OPTION_COMPILER_OPT_INLINING, OPTION_COMPILER_OPT_PGOTYPE, + OPTION_COMPILER_OPT_TRACK_FIELD, OPTION_COMPILER_OPT_GLOBAL_TYPEINFER, OPTION_HELP, OPTION_COMPILER_PGO_PROFILER_PATH, @@ -132,7 +133,15 @@ enum CommandValues { OPTION_TARGET_COMPILER_MODE, OPTION_HAP_PATH, OPTION_HAP_ABC_OFFSET, - OPTION_HAP_ABC_SIZE + OPTION_HAP_ABC_SIZE, + OPTION_COMPILER_NOCHECK, + OPTION_FAST_AOT_COMPILE_MODE, + OPTION_COMPILER_OPT_LOOP_PEELING, + OPTION_COMPILER_OPT_ON_HEAP_CHECK, + OPTION_COMPILER_PKG_INFO, + OPTION_COMPILER_EXTERNAL_PKG_INFO, + OPTION_COMPILER_OPT_ARRAY_BOUNDS_CHECK_ELIMINATION, + OPTION_COMPILER_OPT_LOOP_INVARIANT_CODE_MOTION, }; class PUBLIC_API JSRuntimeOptions { @@ -185,6 +194,26 @@ public: stubFile_ = std::move(value); } + void SetCompilerPkgJsonInfo(std::string pkgJsonInfo) + { + compilerPkgInfo_ = std::move(pkgJsonInfo); + } + + const std::string &GetCompilerPkgJsonInfo() const + { + return compilerPkgInfo_; + } + + void SetCompilerExternalPkgJsonInfo(std::string pkgJsonInfo) + { + compilerExternalPkgInfo_ = std::move(pkgJsonInfo); + } + + const std::string &GetCompilerExternalPkgJsonInfo() const + { + return compilerExternalPkgInfo_; + } + bool WasStubFileSet() const { return WasOptionSet(OPTION_STUB_FILE); @@ -896,6 +925,16 @@ public: enableTypeLowering_ = value; } + bool IsEnableArrayBoundsCheckElimination() const + { + return enableArrayBoundsCheckElimination_; + } + + void SetEnableArrayBoundsCheckElimination(bool value) + { + enableArrayBoundsCheckElimination_ = value; + } + bool IsEnableTypeLowering() const { return enableTypeLowering_; @@ -951,6 +990,16 @@ public: return enableOptPGOType_; } + void SetEnableOptTrackField(bool value) + { + enableOptTrackField_ = value; + } + + bool IsEnableOptTrackField() const + { + return enableOptTrackField_; + } + void SetEnableGlobalTypeInfer(bool value) { enableGlobalTypeInfer_ = value; @@ -1121,10 +1170,60 @@ public: return hapAbcSize_; } + void SetCompilerNoCheck(bool value) + { + compilerNoCheck_ = value; + } + + bool IsCompilerNoCheck() const + { + return compilerNoCheck_; + } + void SetTargetBuiltinsDtsPath(); void SetOptionsForTargetCompilation(); + void SetFastAOTCompileMode(bool value) + { + fastAOTCompileMode_ = value; + } + + bool GetFastAOTCompileMode() const + { + return fastAOTCompileMode_; + } + + void SetEnableOptLoopPeeling(bool value) + { + enableOptLoopPeeling_ = value; + } + + bool IsEnableOptLoopPeeling() const + { + return enableOptLoopPeeling_; + } + + void SetEnableOptLoopInvariantCodeMotion(bool value) + { + enableOptLoopInvariantCodeMotion_ = value; + } + + bool IsEnableOptLoopInvariantCodeMotion() const + { + return enableOptLoopInvariantCodeMotion_; + } + + void SetEnableOptOnHeapCheck(bool value) + { + enableOptOnHeapCheck_ = value; + } + + bool IsEnableOptOnHeapCheck() const + { + return enableOptOnHeapCheck_; + } + private: static bool StartsWith(const std::string &haystack, const std::string &needle) { @@ -1145,6 +1244,8 @@ private: bool enableArkTools_ {true}; std::string stubFile_ {"stub.an"}; + std::string compilerPkgInfo_ {}; + std::string compilerExternalPkgInfo_ {}; bool enableForceGc_ {true}; bool forceFullGc_ {true}; int arkProperties_ = GetDefaultProperties(); @@ -1153,7 +1254,7 @@ private: uint32_t longPauseTime_ {40}; // 40: default pause time std::string aotOutputFile_ {""}; std::string targetTriple_ {TARGET_X64}; - uint32_t asmOptLevel_ {3}; // 3: default opt level + uint32_t asmOptLevel_ {2}; uint32_t relocationMode_ {2}; // 2: default relocation mode uint32_t maxNonmovableSpaceCapacity_ {4_MB}; bool enableAsmInterpreter_ {true}; @@ -1187,6 +1288,7 @@ private: double typeThreshold_ {-1}; std::string entryPoint_ {"_GLOBAL::func_main_0"}; bool mergeAbc_ {false}; + bool enableArrayBoundsCheckElimination_ {false}; bool enableTypeLowering_ {true}; bool enableEarlyElimination_ {true}; bool enableLaterElimination_ {true}; @@ -1194,6 +1296,7 @@ private: bool enableOptInlining_ {true}; bool enableOptPGOType_ {true}; bool enableGlobalTypeInfer_ {false}; + bool enableOptTrackField_ {true}; uint32_t compilerModuleMethods_ {100}; uint64_t wasSet_ {0}; bool enablePrintExecuteTime_ {false}; @@ -1210,11 +1313,16 @@ private: std::string compilerSelectMethods_ {""}; std::string compilerSkipMethods_ {""}; bool traceInline_ {false}; - size_t maxInlineBytecodes_ {25}; + size_t maxInlineBytecodes_ {45}; std::string targetCompilerMode_ {""}; std::string hapPath_ {""}; uint32_t hapAbcOffset_ {0}; uint32_t hapAbcSize_ {0}; + bool compilerNoCheck_ {false}; + bool fastAOTCompileMode_ {false}; + bool enableOptLoopPeeling_ {true}; + bool enableOptOnHeapCheck_ {true}; + bool enableOptLoopInvariantCodeMotion_ {false}; }; } // namespace panda::ecmascript diff --git a/ecmascript/js_serializer.cpp b/ecmascript/js_serializer.cpp index 6cb54899b01cda0219574ee839244f1e18af5353..179e6b68f63892c015bda63f2f3a34739e5d2374 100644 --- a/ecmascript/js_serializer.cpp +++ b/ecmascript/js_serializer.cpp @@ -26,6 +26,7 @@ #include "ecmascript/base/array_helper.h" #include "ecmascript/base/typed_array_helper-inl.h" #include "ecmascript/base/typed_array_helper.h" +#include "ecmascript/ecma_string-inl.h" #include "ecmascript/global_env.h" #include "ecmascript/js_array.h" #include "ecmascript/js_arraybuffer.h" @@ -34,6 +35,7 @@ #include "ecmascript/js_set.h" #include "ecmascript/js_typed_array.h" #include "ecmascript/linked_hash_table.h" +#include "ecmascript/object_factory-inl.h" #include "ecmascript/shared_mm/shared_mm.h" #include "securec.h" @@ -329,6 +331,7 @@ bool JSSerializer::WriteTaggedObject(const JSHandle &value) case JSType::LINE_STRING: case JSType::CONSTANT_STRING: case JSType::TREE_STRING: + case JSType::SLICED_STRING: return WriteEcmaString(value); case JSType::JS_OBJECT: return WritePlainObject(value); @@ -485,6 +488,12 @@ bool JSSerializer::WriteMethod(const JSHandle &value) if (!WriteString(desc)) { return false; } + if (method->IsAotWithCallField()) { + uintptr_t codeEntry = method->GetCodeEntryOrLiteral(); + if (!WriteRawData(&codeEntry, sizeof(uintptr_t))) { + return false; + } + } } return true; } @@ -581,7 +590,7 @@ bool JSSerializer::WriteJSArray(const JSHandle &value) if (!WritePlainObject(value)) { return false; } - uint32_t arrayLength = static_cast(array->GetLength().GetInt()); + uint32_t arrayLength = array->GetLength(); if (!WriteInt(arrayLength)) { return false; } @@ -591,12 +600,12 @@ bool JSSerializer::WriteJSArray(const JSHandle &value) bool JSSerializer::WriteEcmaString(const JSHandle &value) { JSHandle strHandle = JSHandle::Cast(value); - auto string = JSHandle(thread_, EcmaStringAccessor::Flatten(thread_->GetEcmaVM(), strHandle)); + auto string = EcmaStringAccessor::FlattenAllString(thread_->GetEcmaVM(), strHandle); if (!WriteType(SerializationUID::ECMASTRING)) { return false; } - size_t length = EcmaStringAccessor(string).GetLength(); + size_t length = string.GetLength(); if (!WriteInt(static_cast(length))) { return false; } @@ -605,19 +614,19 @@ bool JSSerializer::WriteEcmaString(const JSHandle &value) return true; } - bool isUtf8 = EcmaStringAccessor(string).IsUtf8(); + bool isUtf8 = string.IsUtf8(); // write utf encode flag if (!WriteBoolean(isUtf8)) { return false; } if (isUtf8) { - const uint8_t *data = EcmaStringAccessor(string).GetDataUtf8(); + const uint8_t *data = string.GetDataUtf8(); const uint8_t strEnd = '\0'; if (!WriteRawData(data, length) || !WriteRawData(&strEnd, sizeof(uint8_t))) { return false; } } else { - const uint16_t *data = EcmaStringAccessor(string).GetDataUtf16(); + const uint16_t *data = string.GetDataUtf16(); if (!WriteRawData(data, length * sizeof(uint16_t))) { return false; } @@ -634,13 +643,13 @@ bool JSSerializer::WriteJSMap(const JSHandle &value) if (!WritePlainObject(value)) { return false; } - int size = map->GetSize(); - if (!WriteInt(size)) { + uint32_t size = map->GetSize(); + if (!WriteInt(static_cast(size))) { return false; } JSMutableHandle key(thread_, JSTaggedValue::Undefined()); JSMutableHandle val(thread_, JSTaggedValue::Undefined()); - for (int i = 0; i < size; i++) { + for (uint32_t i = 0; i < size; i++) { key.Update(map->GetKey(i)); if (!SerializeJSTaggedValue(key)) { return false; @@ -662,12 +671,12 @@ bool JSSerializer::WriteJSSet(const JSHandle &value) if (!WritePlainObject(value)) { return false; } - int size = set->GetSize(); - if (!WriteInt(size)) { + uint32_t size = set->GetSize(); + if (!WriteInt(static_cast(size))) { return false; } JSMutableHandle val(thread_, JSTaggedValue::Undefined()); - for (int i = 0; i < size; i++) { + for (uint32_t i = 0; i < size; i++) { val.Update(set->GetValue(i)); if (!SerializeJSTaggedValue(val)) { return false; @@ -767,7 +776,7 @@ bool JSSerializer::WriteJSTypedArray(const JSHandle &value, Seria return true; } -bool JSSerializer::TransferJSNativePointer(const JSHandle &nativePtr) +bool JSSerializer::WriteJSNativePointer(const JSHandle &nativePtr) { uintptr_t externalPtr = reinterpret_cast(nativePtr->GetExternalPointer()); if (!WriteRawData(&externalPtr, sizeof(uintptr_t))) { @@ -785,7 +794,6 @@ bool JSSerializer::TransferJSNativePointer(const JSHandle &nati if (!WriteInt(bindingSize)) { return false; } - // detach c buffer nativePtr->Detach(); return true; } @@ -798,15 +806,15 @@ bool JSSerializer::WriteJSArrayBuffer(const JSHandle &value) } bool shared = arrayBuffer->GetShared(); bool transfer = transferDataSet_.find(static_cast(value.GetTaggedType())) != transferDataSet_.end(); - if (shared && transfer) { - LOG_ECMA(ERROR) << "Can't transfer a shared JSArrayBuffer"; - return false; - } if (shared) { + if (transfer) { + LOG_ECMA(ERROR) << "Can't transfer a shared JSArrayBuffer"; + return false; + } if (!WriteType(SerializationUID::JS_SHARED_ARRAY_BUFFER)) { return false; } - } else if (transfer) { + } else if (defaultTransfer_ || transfer) { if (!WriteType(SerializationUID::JS_TRANSFER_ARRAY_BUFFER)) { return false; } @@ -816,6 +824,11 @@ bool JSSerializer::WriteJSArrayBuffer(const JSHandle &value) } } + bool withNativeAreaAllocator = arrayBuffer->GetWithNativeAreaAllocator(); + if (!WriteBoolean(withNativeAreaAllocator)) { + return false; + } + // Write Accessors(ArrayBufferByteLength) uint32_t arrayLength = arrayBuffer->GetArrayBufferByteLength(); if (!WriteInt(arrayLength)) { @@ -832,12 +845,12 @@ bool JSSerializer::WriteJSArrayBuffer(const JSHandle &value) if (!WriteRawData(&bufferAddr, sizeof(uint64_t))) { return false; } - } else if (transfer) { + } else if (defaultTransfer_ || transfer) { // Write Accessors(ArrayBufferData) which is a pointer to a Buffer - if (!TransferJSNativePointer(np)) { + if (!WriteJSNativePointer(np)) { return false; } - arrayBuffer->Detach(thread_); + arrayBuffer->Detach(thread_, withNativeAreaAllocator); } else { // Write Accessors(ArrayBufferData) which is a pointer to a Buffer void *buffer = np->GetExternalPointer(); @@ -882,7 +895,7 @@ bool JSSerializer::WritePlainObject(const JSHandle &objValue) JSHandle obj = JSHandle::Cast(objValue); std::vector keyVector; uint32_t propertiesLength = obj->GetNumberOfKeys(); - JSObject::GetAllKeys(obj, keyVector); + JSObject::GetAllKeysForSerialization(obj, keyVector); if (keyVector.size() != propertiesLength) { return false; } @@ -1084,16 +1097,15 @@ bool JSDeserializer::ReadDouble(double *value) JSDeserializer::~JSDeserializer() { referenceMap_.clear(); - free(begin_); - begin_ = nullptr; } JSHandle JSDeserializer::Deserialize() { size_t maxSerializerSize = thread_->GetEcmaVM()->GetEcmaParamConfiguration().GetMaxJSSerializerSize(); - uint8_t dataSize = end_ - begin_; + size_t dataSize = end_ - begin_; if (dataSize > maxSerializerSize) { - LOG_ECMA(ERROR) << "The Serialization data size exceed limit Size"; + LOG_ECMA(ERROR) << "The Serialization data size has exceed limit Size, current size is: " << dataSize << + " max size is: " << maxSerializerSize; return JSHandle(); } JSHandle res = DeserializeJSTaggedValue(); @@ -1180,11 +1192,9 @@ JSHandle JSDeserializer::DeserializeJSTaggedValue() case SerializationUID::JS_BIGUINT64_ARRAY: return ReadJSTypedArray(SerializationUID::JS_BIGUINT64_ARRAY); case SerializationUID::JS_ARRAY_BUFFER: - return ReadJSArrayBuffer(SerializationUID::JS_ARRAY_BUFFER); case SerializationUID::JS_SHARED_ARRAY_BUFFER: - return ReadJSArrayBuffer(SerializationUID::JS_SHARED_ARRAY_BUFFER); case SerializationUID::JS_TRANSFER_ARRAY_BUFFER: - return ReadJSArrayBuffer(SerializationUID::JS_TRANSFER_ARRAY_BUFFER); + return ReadJSArrayBuffer(uid); case SerializationUID::TAGGED_OBJECT_REFERNCE: return ReadReference(); case SerializationUID::CONCURRENT_FUNCTION: @@ -1257,7 +1267,7 @@ JSHandle JSDeserializer::ReadByteArray() JSHandle byteArray = factory_->NewByteArray(arrayLength, arrayType); for (int32_t i = 0; i < arrayLength; i++) { JSHandle val = DeserializeJSTaggedValue(); - byteArray->Set(i, viewType, val.GetTaggedType()); + byteArray->Set(thread_, i, viewType, val.GetTaggedType()); } return JSHandle(byteArray); } @@ -1327,6 +1337,17 @@ JSHandle JSDeserializer::ReadMethod() JSHandle constPool = thread_->GetCurrentEcmaContext()->FindOrCreateConstPool(jsPandaFile.get(), method->GetMethodId()); method->SetConstantPool(thread_, constPool.GetTaggedValue()); + + if (method->IsAotWithCallField()) { + uintptr_t codeEntry; + if (!ReadNativePointer(&codeEntry)) { + return JSHandle(); + } + method->SetCodeEntryAndMarkAOT(codeEntry); + + uint8_t deoptThreshold = thread_->GetEcmaVM()->GetJSOptions().GetDeoptThreshold(); + method->SetDeoptThreshold(deoptThreshold); + } return methodTag; } @@ -1443,7 +1464,7 @@ JSHandle JSDeserializer::ReadJSArray() if (!JudgeType(SerializationUID::INT32) || !ReadInt(&arrLength)) { return JSHandle(); } - jsArray->SetLength(thread_, JSTaggedValue(arrLength)); + jsArray->SetLength(arrLength); return arrayTag; } @@ -1728,7 +1749,7 @@ JSHandle JSDeserializer::ReadJSTypedArray(SerializationUID uid) return objTag; } -JSHandle JSDeserializer::ReadTransferJSNativePointer() +JSHandle JSDeserializer::ReadJSNativePointer() { uintptr_t externalPtr; if (!ReadNativePointer(&externalPtr)) { @@ -1756,6 +1777,10 @@ JSHandle JSDeserializer::ReadTransferJSNativePointer() JSHandle JSDeserializer::ReadJSArrayBuffer(SerializationUID uid) { + bool withNativeAreaAllocator; + if (!ReadBoolean(&withNativeAreaAllocator)) { + return JSHandle(); + } // read access length int32_t arrayLength; if (!JudgeType(SerializationUID::INT32) || !ReadInt(&arrayLength)) { @@ -1764,33 +1789,29 @@ JSHandle JSDeserializer::ReadJSArrayBuffer(SerializationUID uid) // read access shared bool shared = (uid == SerializationUID::JS_SHARED_ARRAY_BUFFER); - JSHandle arrayBufferTag; + JSHandle arrayBuffer; if (arrayLength == 0) { // create an empty arrayBuffer - JSHandle arrayBuffer = factory_->NewJSArrayBuffer(0); + arrayBuffer = factory_->NewJSArrayBuffer(0); arrayBuffer->SetShared(shared); - arrayBufferTag = JSHandle::Cast(arrayBuffer); } else { if (shared) { uint64_t *bufferAddr = reinterpret_cast(GetBuffer(sizeof(uint64_t))); void *bufferData = ToVoidPtr(*bufferAddr); - JSHandle arrayBuffer = factory_->NewJSSharedArrayBuffer(bufferData, arrayLength); - arrayBufferTag = JSHandle::Cast(arrayBuffer); + arrayBuffer = factory_->NewJSSharedArrayBuffer(bufferData, arrayLength); } else if (uid == SerializationUID::JS_TRANSFER_ARRAY_BUFFER) { - JSHandle np = ReadTransferJSNativePointer(); + JSHandle np = ReadJSNativePointer(); if (np.IsEmpty()) { return JSHandle(); } - JSHandle arrayBuffer = - factory_->NewJSArrayBuffer(arrayLength, JSHandle::Cast(np)); - arrayBufferTag = JSHandle::Cast(arrayBuffer); + arrayBuffer = factory_->NewJSArrayBuffer(0); + arrayBuffer->Attach(thread_, arrayLength, np.GetTaggedValue(), withNativeAreaAllocator); } else { void *fromBuffer = GetBuffer(arrayLength); if (fromBuffer == nullptr) { - return arrayBufferTag; + return JSHandle(); } - JSHandle arrayBuffer = factory_->NewJSArrayBuffer(arrayLength); - arrayBufferTag = JSHandle::Cast(arrayBuffer); + arrayBuffer = factory_->NewJSArrayBuffer(arrayLength); JSNativePointer* np = JSNativePointer::Cast(arrayBuffer->GetArrayBufferData().GetTaggedObject()); void *toBuffer = np->GetExternalPointer(); if (memcpy_s(toBuffer, arrayLength, fromBuffer, arrayLength) != EOK) { @@ -1799,6 +1820,9 @@ JSHandle JSDeserializer::ReadJSArrayBuffer(SerializationUID uid) } } } + + arrayBuffer->SetWithNativeAreaAllocator(withNativeAreaAllocator); + JSHandle arrayBufferTag = JSHandle::Cast(arrayBuffer); referenceMap_.emplace(objectId_++, arrayBufferTag); // read jsarraybuffer properties if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(arrayBufferTag)) { @@ -1969,11 +1993,14 @@ bool JSDeserializer::ReadBoolean(bool *value) bool Serializer::WriteValue( JSThread *thread, const JSHandle &value, const JSHandle &transfer) { + ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "Serializer::WriteValue"); if (data_ != nullptr) { return false; } data_.reset(new SerializationData); - if (!PrepareTransfer(thread, transfer)) { + if (value.GetTaggedValue() == transfer.GetTaggedValue()) { + valueSerializer_.SetDefaultTransfer(); + } else if (!PrepareTransfer(thread, transfer)) { return false; } if (!valueSerializer_.SerializeJSTaggedValue(value)) { @@ -2020,6 +2047,7 @@ bool Serializer::PrepareTransfer(JSThread *thread, const JSHandle JSHandle Deserializer::ReadValue() { + ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "Deserializer::ReadValue"); return valueDeserializer_.Deserialize(); } } // namespace panda::ecmascript diff --git a/ecmascript/js_serializer.h b/ecmascript/js_serializer.h index c66647c49b037c009653ca6f14727df9890eb249..e153768e7476cafd689f3490d3f567ce03709a96 100644 --- a/ecmascript/js_serializer.h +++ b/ecmascript/js_serializer.h @@ -115,6 +115,11 @@ public: // Return pointer to the buffer and its length, should not use this Serializer anymore after Release std::pair ReleaseBuffer(); + void SetDefaultTransfer() + { + defaultTransfer_ = true; + } + private: bool WriteJSFunction(const JSHandle &value); bool WriteMethod(const JSHandle &value); @@ -141,7 +146,7 @@ private: bool WriteJSTypedArray(const JSHandle &value, SerializationUID uId); bool WritePlainObject(const JSHandle &value); bool WriteNativeBindingObject(const JSHandle &value); - bool TransferJSNativePointer(const JSHandle &value); + bool WriteJSNativePointer(const JSHandle &value); bool WriteJSArrayBuffer(const JSHandle &value); bool WriteBigInt(const JSHandle &value); bool WriteDesc(const PropertyDescriptor &desc); @@ -165,6 +170,7 @@ private: CUnorderedMap referenceMap_; CUnorderedSet transferDataSet_; uint64_t objectId_ = 0; + bool defaultTransfer_ = false; }; class JSDeserializer { @@ -198,7 +204,7 @@ private: JSHandle ReadJSSet(); JSHandle ReadJSRegExp(); JSHandle ReadJSTypedArray(SerializationUID uid); - JSHandle ReadTransferJSNativePointer(); + JSHandle ReadJSNativePointer(); JSHandle ReadJSArrayBuffer(SerializationUID uid); JSHandle ReadReference(); JSHandle ReadNativeBindingObject(); @@ -279,13 +285,17 @@ private: class Deserializer { public: Deserializer(JSThread *thread, SerializationData *data, void *hint) - : valueDeserializer_(thread, data->GetData(), data->GetSize(), hint) {} - ~Deserializer() = default; + : valueDeserializer_(thread, data->GetData(), data->GetSize(), hint), data_(data) {} + ~Deserializer() + { + data_.reset(nullptr); + } JSHandle ReadValue(); private: ecmascript::JSDeserializer valueDeserializer_; + std::unique_ptr data_; NO_COPY_SEMANTIC(Deserializer); }; diff --git a/ecmascript/js_set.cpp b/ecmascript/js_set.cpp index d26c765d81784b64389c2588da67aceb49b79e68..21dc62341df32489279b5f6e7576e643ce7018b0 100644 --- a/ecmascript/js_set.cpp +++ b/ecmascript/js_set.cpp @@ -56,14 +56,15 @@ bool JSSet::Has(JSTaggedValue value) const return LinkedHashSet::Cast(GetLinkedSet().GetTaggedObject())->Has(value); } -int JSSet::GetSize() const +uint32_t JSSet::GetSize() const { return LinkedHashSet::Cast(GetLinkedSet().GetTaggedObject())->NumberOfElements(); } JSTaggedValue JSSet::GetValue(int entry) const { - ASSERT_PRINT(entry >= 0 && entry < GetSize(), "entry must be non-negative integer less than capacity"); + ASSERT_PRINT(entry >= 0 && static_cast(entry) < GetSize(), + "entry must be non-negative integer less than capacity"); return LinkedHashSet::Cast(GetLinkedSet().GetTaggedObject())->GetValue(entry); } } // namespace panda::ecmascript diff --git a/ecmascript/js_set.h b/ecmascript/js_set.h index 50d5ee8a8a58523ed71918c1a0a88449d9153dcc..d02372ec4023037650095fd960028f578647280b 100644 --- a/ecmascript/js_set.h +++ b/ecmascript/js_set.h @@ -32,7 +32,7 @@ public: bool Has(JSTaggedValue value) const; - int GetSize() const; + uint32_t GetSize() const; JSTaggedValue GetValue(int entry) const; diff --git a/ecmascript/js_stable_array.cpp b/ecmascript/js_stable_array.cpp index 94f85eb807d4ed665c11391c47047eafddb90a41..1763a2bcd679340438383cc01dce135afae26527 100644 --- a/ecmascript/js_stable_array.cpp +++ b/ecmascript/js_stable_array.cpp @@ -19,13 +19,17 @@ #include "ecmascript/base/builtins_base.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/ecma_string-inl.h" #include "ecmascript/global_env.h" #include "ecmascript/interpreter/fast_runtime_stub-inl.h" #include "ecmascript/js_array.h" #include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/js_tagged_value.h" #include "ecmascript/object_factory.h" #include "ecmascript/tagged_array.h" +#include "macros.h" namespace panda::ecmascript { using TypedArrayHelper = base::TypedArrayHelper; @@ -40,7 +44,7 @@ JSTaggedValue JSStableArray::Push(JSHandle receiver, EcmaRuntimeCallInf TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject()); if (newLength > elements->GetLength()) { - elements = *JSObject::GrowElementsCapacity(thread, JSHandle::Cast(receiver), newLength); + elements = *JSObject::GrowElementsCapacity(thread, JSHandle::Cast(receiver), newLength, true); } for (uint32_t k = 0; k < argc; k++) { @@ -124,6 +128,7 @@ JSTaggedValue JSStableArray::Splice(JSHandle receiver, EcmaRuntimeCallI toKey.Update(JSTaggedValue(k)); if (newArrayHandle->IsJSProxy()) { toKey.Update(JSTaggedValue::ToString(thread, toKey).GetTaggedValue()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); } JSObject::CreateDataPropertyOrThrow(thread, newArrayHandle, toKey, fromValue); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -211,6 +216,20 @@ JSTaggedValue JSStableArray::Shift(JSHandle receiver, EcmaRuntimeCallIn return result.IsHole() ? JSTaggedValue::Undefined() : result; } +void JSStableArray::SetSepValue(JSHandle sepStringHandle, int &sep, uint32_t &sepLength) +{ + if (EcmaStringAccessor(sepStringHandle).IsUtf8() && EcmaStringAccessor(sepStringHandle).GetLength() == 1) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + sep = EcmaStringAccessor(sepStringHandle).Get(0); + } else if (EcmaStringAccessor(sepStringHandle).GetLength() == 0) { + sep = JSStableArray::SeparatorFlag::MINUS_TWO; + sepLength = 0; + } else { + sep = JSStableArray::SeparatorFlag::MINUS_ONE; + sepLength = EcmaStringAccessor(sepStringHandle).GetLength(); + } +} + JSTaggedValue JSStableArray::Join(JSHandle receiver, EcmaRuntimeCallInfo *argv) { JSThread *thread = argv->GetThread(); @@ -219,26 +238,20 @@ JSTaggedValue JSStableArray::Join(JSHandle receiver, EcmaRuntimeCallInf int sep = ','; uint32_t sepLength = 1; JSHandle sepStringHandle; + auto context = thread->GetCurrentEcmaContext(); + JSHandle receiverValue = JSHandle::Cast(receiver); if (!sepHandle->IsUndefined()) { if (sepHandle->IsString()) { sepStringHandle = JSHandle::Cast(sepHandle); } else { sepStringHandle = JSTaggedValue::ToString(thread, sepHandle); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - } - if (EcmaStringAccessor(sepStringHandle).IsUtf8() && EcmaStringAccessor(sepStringHandle).GetLength() == 1) { - // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - sep = EcmaStringAccessor(sepStringHandle).Get(0); - } else if (EcmaStringAccessor(sepStringHandle).GetLength() == 0) { - sep = JSStableArray::SeparatorFlag::MINUS_TWO; - sepLength = 0; - } else { - sep = JSStableArray::SeparatorFlag::MINUS_ONE; - sepLength = EcmaStringAccessor(sepStringHandle).GetLength(); + RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue); } + SetSepValue(sepStringHandle, sep, sepLength); } if (length == 0) { const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + context->JoinStackPopFastPath(receiverValue); return globalConst->GetEmptyString(); } TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject()); @@ -261,7 +274,7 @@ JSTaggedValue JSStableArray::Join(JSHandle receiver, EcmaRuntimeCallInf if (!element.IsString()) { elementHandle.Update(element); JSHandle strElement = JSTaggedValue::ToString(thread, elementHandle); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, receiverValue); element = strElement.GetTaggedValue(); elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject()); } @@ -298,6 +311,7 @@ JSTaggedValue JSStableArray::Join(JSHandle receiver, EcmaRuntimeCallInf } ASSERT_PRINT( isOneByte == EcmaStringAccessor::CanBeCompressed(newString), "isOneByte does not match the real value!"); + context->JoinStackPopFastPath(receiverValue); return JSTaggedValue(newString); } @@ -339,6 +353,40 @@ JSTaggedValue JSStableArray::HandleFindIndexOfStable(JSThread *thread, JSHandle< return callResult; } +JSTaggedValue JSStableArray::HandleFindLastIndexOfStable(JSThread *thread, JSHandle thisObjHandle, + JSHandle callbackFnHandle, + JSHandle thisArgHandle, int64_t &k) +{ + JSHandle thisObjVal(thisObjHandle); + JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); + JSTaggedValue callResult = base::BuiltinsBase::GetTaggedBoolean(false); + const int32_t argsLength = 3; // 3: ?kValue, k, O? + JSMutableHandle array(thread, thisObjHandle->GetElements()); + JSMutableHandle kValue(thread, JSTaggedValue::Undefined()); + while (k >= 0) { + // Elements of thisObjHandle may change. + array.Update(thisObjHandle->GetElements()); + kValue.Update(array->Get(k)); + EcmaRuntimeCallInfo *info = + EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + info->SetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue()); + callResult = JSFunction::Call(info); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, callResult); + if (callResult.ToBoolean()) { + return callResult; + } + k--; + if (base::ArrayHelper::GetArrayLength(thread, thisObjVal) - 1 < k) { + return callResult; + } + if (!thisObjVal->IsStableJSArray(thread)) { + return callResult; + } + } + return callResult; +} + JSTaggedValue JSStableArray::HandleEveryOfStable(JSThread *thread, JSHandle thisObjHandle, JSHandle callbackFnHandle, JSHandle thisArgHandle, uint32_t &k) @@ -357,6 +405,7 @@ JSTaggedValue JSStableArray::HandleEveryOfStable(JSThread *thread, JSHandleSetCallArg(kValue.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue()); callResult = JSFunction::Call(info); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -368,6 +417,7 @@ JSTaggedValue JSStableArray::HandleEveryOfStable(JSThread *thread, JSHandleSetCallArg(kValue1.GetTaggedValue(), JSTaggedValue(k), thisObjVal.GetTaggedValue()); callResult = JSFunction::Call(info); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -385,13 +435,12 @@ JSTaggedValue JSStableArray::HandleEveryOfStable(JSThread *thread, JSHandle thisObjHandle, JSHandle callbackFnHandle, - JSHandle thisArgHandle, uint32_t &k) + JSHandle thisArgHandle, uint32_t len, uint32_t &k) { JSMutableHandle key(thread, JSTaggedValue::Undefined()); JSHandle thisObjVal(thisObjHandle); JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); JSMutableHandle array(thread, thisObjHandle->GetElements()); - uint64_t len = static_cast(base::ArrayHelper::GetArrayLength(thread, thisObjVal)); const int32_t argsLength = 3; // 3: ?kValue, k, O? JSMutableHandle kValue(thread, JSTaggedValue::Undefined()); if (array->GetLength() <= k) { @@ -431,33 +480,205 @@ JSTaggedValue JSStableArray::HandleforEachOfStable(JSThread *thread, JSHandle receiver, - JSHandle searchElement, uint32_t from, uint32_t len) +template +JSTaggedValue JSStableArray::FindRawData(IndexOfContext &ctx, Predicate &&predicate) { - JSHandle elements(thread, JSHandle::Cast(receiver)->GetElements()); - while (from < len) { - JSTaggedValue value = JSTaggedValue::Hole(); - if (elements->GetLength() > from) { - value = elements->Get(from); - } - if (!value.IsUndefined() && !value.IsHole()) { - if (JSTaggedValue::StrictEqual(searchElement.GetTaggedValue(), value)) { - return JSTaggedValue(from); + DISALLOW_GARBAGE_COLLECTION; + JSHandle elements(ctx.thread, JSHandle::Cast(ctx.receiver)->GetElements()); + // Note: GC is guaranteed not to happen since no new object is created during the searching process. + JSTaggedType *data = elements->GetData(); + JSTaggedType *first = data + ctx.fromIndex; + JSTaggedType *last = data + ctx.length; + // Note: for stable arrays, elements->GetLength() returns the CAPACITY, instead of actual length! + + JSMutableHandle indexHandle(ctx.thread, JSTaggedValue::Undefined()); + for (JSTaggedType *cur = first; cur < last; ++cur) { + if (LIKELY(*cur != JSTaggedValue::VALUE_HOLE)) { + if (UNLIKELY(std::invoke(predicate, *cur))) { + return base::BuiltinsBase::GetTaggedInt64(cur - data); } - } else { - bool exist = JSTaggedValue::HasProperty(thread, receiver, from); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - if (exist) { - JSHandle kValueHandle = JSArray::FastGetPropertyByValue(thread, receiver, from); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - if (JSTaggedValue::StrictEqual(thread, searchElement, kValueHandle)) { - return JSTaggedValue(from); - } + continue; + } + // Fallback slow path + indexHandle.Update(base::BuiltinsBase::GetTaggedInt64(cur - data)); + bool found = base::ArrayHelper::ElementIsStrictEqualTo( + ctx.thread, ctx.receiver, indexHandle, ctx.searchElement); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(ctx.thread); + if (found) { + return indexHandle.GetTaggedValue(); + } + } + return JSTaggedValue(-1); // Not found +} + +template +JSTaggedValue JSStableArray::FindLastRawData(IndexOfContext &ctx, Predicate &&predicate) +{ + DISALLOW_GARBAGE_COLLECTION; + JSHandle elements(ctx.thread, JSHandle::Cast(ctx.receiver)->GetElements()); + // Note: GC is guaranteed not to happen since no new object is created during the searching process. + JSTaggedType *data = elements->GetData(); + JSTaggedType *beforeFirst = data - 1; + JSTaggedType *beforeLast = data + ctx.fromIndex; + + JSMutableHandle indexHandle(ctx.thread, JSTaggedValue::Undefined()); + for (JSTaggedType *cur = beforeLast; cur > beforeFirst; --cur) { + if (LIKELY(*cur != JSTaggedValue::VALUE_HOLE)) { + if (UNLIKELY(std::invoke(predicate, *cur))) { + return base::BuiltinsBase::GetTaggedInt64(cur - data); } + continue; } - from++; + // Fallback slow path + indexHandle.Update(base::BuiltinsBase::GetTaggedInt64(cur - data)); + bool found = base::ArrayHelper::ElementIsStrictEqualTo( + ctx.thread, ctx.receiver, indexHandle, ctx.searchElement); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(ctx.thread); + if (found) { + return indexHandle.GetTaggedValue(); + } + } + return JSTaggedValue(-1); // Not found +} + +template +JSTaggedValue JSStableArray::FindRawDataDispatch(IndexOfType type, IndexOfContext &ctx, Predicate &&predicate) +{ + switch (type) { + case IndexOfType::IndexOf: + return FindRawData(ctx, std::forward(predicate)); + case IndexOfType::LastIndexOf: + return FindLastRawData(ctx, std::forward(predicate)); + default: + UNREACHABLE(); } - return JSTaggedValue(-1); +} + +// Zeros need special judge +JSTaggedValue JSStableArray::IndexOfZero(IndexOfType type, IndexOfContext &ctx) +{ + return FindRawDataDispatch(type, ctx, [](JSTaggedType cur) { + return JSTaggedValue(cur).IsExactlyZero(); + }); +} + +JSTaggedValue JSStableArray::IndexOfInt32(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement) +{ + ASSERT(searchElement.IsInt()); + int32_t untagged = searchElement.GetInt(); + if (untagged == 0) { + return IndexOfZero(type, ctx); + } + JSTaggedType targetInt32 = searchElement.GetRawData(); + JSTaggedType targetDouble = JSTaggedValue(static_cast(untagged)).GetRawData(); + return FindRawDataDispatch(type, ctx, [targetInt32, targetDouble](JSTaggedType cur) { + return cur == targetInt32 || cur == targetDouble; + }); +} + +JSTaggedValue JSStableArray::IndexOfDouble(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement) +{ + ASSERT(searchElement.IsDouble()); + double untagged = searchElement.GetDouble(); + if (std::isnan(untagged)) { + return JSTaggedValue(-1); + } + if (untagged == 0.0) { + return IndexOfZero(type, ctx); + } + JSTaggedType targetDouble = searchElement.GetRawData(); + if (searchElement.WithinInt32()) { + JSTaggedType targetInt32 = JSTaggedValue(static_cast(untagged)).GetRawData(); + return FindRawDataDispatch(type, ctx, [targetDouble, targetInt32](JSTaggedType cur) { + return cur == targetDouble || cur == targetInt32; + }); + } else { + return FindRawDataDispatch(type, ctx, [targetDouble](JSTaggedType cur) { + return cur == targetDouble; + }); + } +} + +JSTaggedValue JSStableArray::IndexOfObjectAddress(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement) +{ + ASSERT(searchElement.IsObject()); + JSTaggedType targetAddress = searchElement.GetRawData(); + return FindRawDataDispatch(type, ctx, [targetAddress](JSTaggedType cur) { + return cur == targetAddress; + }); +} + +JSTaggedValue JSStableArray::IndexOfString(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement) +{ + ASSERT(searchElement.IsString()); + JSTaggedType targetAddress = searchElement.GetRawData(); + return FindRawDataDispatch(type, ctx, [searchElement, targetAddress](JSTaggedType cur) { + if (targetAddress == cur) { + return true; + } + JSTaggedValue curValue(cur); + if (!curValue.IsString()) { + return false; + } + return JSTaggedValue::StringCompare( + EcmaString::Cast(curValue.GetTaggedObject()), + EcmaString::Cast(searchElement.GetTaggedObject())); + }); +} + +JSTaggedValue JSStableArray::IndexOfBigInt(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement) +{ + ASSERT(searchElement.IsBigInt()); + JSTaggedType targetAddress = searchElement.GetRawData(); + return FindRawDataDispatch(type, ctx, [searchElement, targetAddress](JSTaggedType cur) { + if (cur == targetAddress) { + return true; + } + JSTaggedValue curValue(cur); + if (!curValue.IsBigInt()) { + return false; + } + return BigInt::Equal(curValue, searchElement); + }); +} + +JSTaggedValue JSStableArray::IndexOfDispatch(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement) +{ + if (searchElement.IsInt()) { + return IndexOfInt32(type, ctx, searchElement); + } else if (searchElement.IsDouble()) { + return IndexOfDouble(type, ctx, searchElement); + } else if (searchElement.IsString()) { + return IndexOfString(type, ctx, searchElement); + } else if (searchElement.IsBigInt()) { + return IndexOfBigInt(type, ctx, searchElement); + } else { + return IndexOfObjectAddress(type, ctx, searchElement); + } +} + +JSTaggedValue JSStableArray::IndexOf(JSThread *thread, JSHandle receiver, + JSHandle searchElement, uint32_t from, uint32_t len) +{ + IndexOfContext ctx; + ctx.thread = thread; + ctx.receiver = receiver; + ctx.searchElement = searchElement; + ctx.fromIndex = from; + ctx.length = len; + return IndexOfDispatch(IndexOfType::IndexOf, ctx, searchElement.GetTaggedValue()); +} + +JSTaggedValue JSStableArray::LastIndexOf(JSThread *thread, JSHandle receiver, + JSHandle searchElement, uint32_t from, uint32_t len) +{ + IndexOfContext ctx; + ctx.thread = thread; + ctx.receiver = receiver; + ctx.searchElement = searchElement; + ctx.fromIndex = from; + ctx.length = len; + return IndexOfDispatch(IndexOfType::LastIndexOf, ctx, searchElement.GetTaggedValue()); } JSTaggedValue JSStableArray::Filter(JSHandle newArrayHandle, JSHandle thisObjHandle, @@ -545,53 +766,19 @@ JSTaggedValue JSStableArray::Map(JSHandle newArrayHandle, JSHandle thisObjHandle, - JSHandle thisHandle, int64_t &lower, uint32_t len) +JSTaggedValue JSStableArray::Reverse(JSThread *thread, JSHandle thisObjHandle, uint32_t len) { - JSHandle thisObjVal(thisObjHandle); if (thisObjHandle->IsJSArray()) { JSArray::CheckAndCopyArray(thread, JSHandle::Cast(thisObjHandle)); } - JSHandle array(thread, thisObjHandle->GetElements()); - JSMutableHandle lowerP(thread, JSTaggedValue::Undefined()); - JSMutableHandle upperP(thread, JSTaggedValue::Undefined()); - JSMutableHandle lowerValueHandle(thread, JSTaggedValue::Undefined()); - JSMutableHandle upperValueHandle(thread, JSTaggedValue::Undefined()); - int64_t middle = std::floor(len / 2); - while (lower != middle) { - if (array->GetLength() != len) { - break; - } - int64_t upper = static_cast(len) - lower - 1; - lowerP.Update(JSTaggedValue(lower)); - upperP.Update(JSTaggedValue(upper)); - bool lowerExists = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, lowerP)); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - if (lowerExists) { - lowerValueHandle.Update(array->Get(lower)); - } - bool upperExists = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, upperP)); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - if (upperExists) { - upperValueHandle.Update(array->Get(upper)); - } - if (lowerExists && upperExists) { - array->Set(thread, lower, upperValueHandle.GetTaggedValue()); - array->Set(thread, upper, lowerValueHandle.GetTaggedValue()); - } else if (upperExists) { - array->Set(thread, lower, upperValueHandle.GetTaggedValue()); - JSTaggedValue::SetProperty(thread, thisObjVal, lowerP, upperValueHandle, true); - JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, upperP); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - } else if (lowerExists) { - array->Set(thread, upper, lowerValueHandle.GetTaggedValue()); - JSTaggedValue::SetProperty(thread, thisObjVal, upperP, lowerValueHandle, true); - JSTaggedValue::DeletePropertyOrThrow(thread, thisObjVal, lowerP); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - } - lower++; - } - return base::BuiltinsBase::GetTaggedDouble(true); + DISALLOW_GARBAGE_COLLECTION; + JSHandle elements(thread, thisObjHandle->GetElements()); + JSTaggedType *data = elements->GetData(); + ASSERT_PRINT(len <= elements->GetLength(), "Length exceeds capacity of contiguous array container."); + // Reversing raw data in-place is OK since no object is created or deleted, + // only pointers swapping for objects or values swapping for primitive types. + std::reverse(data, data + len); + return thisObjHandle.GetTaggedValue(); // Returns the address of thisValue } JSTaggedValue JSStableArray::Concat(JSThread *thread, JSHandle newArrayHandle, @@ -618,7 +805,7 @@ JSTaggedValue JSStableArray::Concat(JSThread *thread, JSHandle newArra } JSTaggedValue JSStableArray::FastCopyFromArrayToTypedArray(JSThread *thread, JSHandle &targetArray, - DataViewType targetType, uint32_t targetOffset, + DataViewType targetType, uint64_t targetOffset, uint32_t srcLength, JSHandle &elements) { JSHandle targetBuffer(thread, targetArray->GetViewedArrayBufferOrByteArray()); @@ -658,4 +845,200 @@ JSTaggedValue JSStableArray::FastCopyFromArrayToTypedArray(JSThread *thread, JSH } return JSTaggedValue::Undefined(); } + +JSTaggedValue JSStableArray::At(JSHandle receiver, EcmaRuntimeCallInfo *argv) +{ + JSThread *thread = argv->GetThread(); + uint32_t thisLen = receiver->GetArrayLength(); + if (thisLen == 0) { + return JSTaggedValue::Undefined(); + } + JSTaggedNumber index = JSTaggedValue::ToInteger(thread, base::BuiltinsBase::GetCallArg(argv, 0)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + int64_t relativeIndex = index.GetNumber(); + int64_t k = 0; + if (relativeIndex >= 0) { + k = relativeIndex; + } else { + k = static_cast(thisLen) + relativeIndex; + } + if (k < 0 || k >= thisLen) { + return JSTaggedValue::Undefined(); + } + + TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject()); + auto result = JSTaggedValue::Hole(); + result = elements->Get(k); + return result.IsHole() ? JSTaggedValue::Undefined() : result; +} + +JSTaggedValue JSStableArray::With(JSThread *thread, JSHandle receiver, + int64_t insertCount, int64_t index, JSHandle value) +{ + JSHandle thisObjHandle(receiver); + JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle, + JSTaggedNumber(static_cast(insertCount))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle newArrayHandle(thread, newArray); + + JSHandle thisObjVal(thisObjHandle); + TaggedArray *srcElements = TaggedArray::Cast(thisObjHandle->GetElements().GetTaggedObject()); + JSMutableHandle srcElementsHandle(thread, srcElements); + TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements().GetTaggedObject()); + + if (insertCount > destElements->GetLength()) { + destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, insertCount); + } + + for (uint32_t idx = 0; idx < insertCount; idx++) { + if (idx == index) { + destElements->Set(thread, idx, value.GetTaggedValue()); + } else { + auto kValue = srcElementsHandle->Get(idx); + if (kValue.IsHole()) { + destElements->Set(thread, idx, JSTaggedValue::Undefined()); + } else { + destElements->Set(thread, idx, kValue); + } + } + } + JSHandle::Cast(newArrayHandle)->SetArrayLength(thread, insertCount); + + return newArrayHandle.GetTaggedValue(); +} + +JSTaggedValue JSStableArray::ToSpliced(JSHandle receiver, EcmaRuntimeCallInfo *argv, + int64_t argc, int64_t actualStart, int64_t actualSkipCount, int64_t insertCount) +{ + JSThread *thread = argv->GetThread(); + + JSHandle thisObjHandle(receiver); + JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle, + JSTaggedNumber(static_cast(insertCount))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle newArrayHandle(thread, newArray); + + JSHandle thisObjVal(thisObjHandle); + TaggedArray *srcElements = TaggedArray::Cast(thisObjHandle->GetElements().GetTaggedObject()); + JSMutableHandle srcElementsHandle(thread, srcElements); + TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements().GetTaggedObject()); + + if (insertCount > destElements->GetLength()) { + destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, insertCount); + } + + int64_t i = 0, r = actualStart + actualSkipCount; + + for (int64_t idx = 0; idx < actualStart; idx++, i++) { + auto kValue = srcElementsHandle->Get(idx); + if (kValue.IsHole()) { + destElements->Set(thread, i, JSTaggedValue::Undefined()); + } else { + destElements->Set(thread, i, kValue); + } + } + + for (uint32_t pos = 2; pos < argc; ++pos) { // 2:2 means there two arguments before the insert items. + auto element = base::BuiltinsBase::GetCallArg(argv, pos); + destElements->Set(thread, i, element.GetTaggedValue()); + ++i; + } + + while (i < insertCount) { + auto kValue = srcElementsHandle->Get(r); + if (kValue.IsHole()) { + destElements->Set(thread, i, JSTaggedValue::Undefined()); + } else { + destElements->Set(thread, i, kValue); + } + ++i; + ++r; + } + + JSHandle::Cast(newArrayHandle)->SetArrayLength(thread, insertCount); + + return newArrayHandle.GetTaggedValue(); +} + +JSTaggedValue JSStableArray::ToReversed(JSThread *thread, JSHandle receiver, + int64_t insertCount) +{ + JSHandle thisObjHandle(receiver); + JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle, + JSTaggedNumber(static_cast(insertCount))); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle newArrayHandle(thread, newArray); + + JSHandle thisObjVal(thisObjHandle); + TaggedArray *srcElements = TaggedArray::Cast(thisObjHandle->GetElements().GetTaggedObject()); + JSMutableHandle srcElementsHandle(thread, srcElements); + TaggedArray *destElements = TaggedArray::Cast(newArrayHandle->GetElements().GetTaggedObject()); + + if (insertCount > destElements->GetLength()) { + destElements = *JSObject::GrowElementsCapacity(thread, newArrayHandle, insertCount); + } + + for (uint32_t idx = 0; idx < insertCount; idx++) { + auto kValue = srcElementsHandle->Get(idx); + if (kValue.IsHole()) { + destElements->Set(thread, insertCount - idx - 1, JSTaggedValue::Undefined()); + } else { + destElements->Set(thread, insertCount - idx - 1, kValue); + } + } + JSHandle::Cast(newArrayHandle)->SetArrayLength(thread, insertCount); + + return newArrayHandle.GetTaggedValue(); +} + +JSTaggedValue JSStableArray::Reduce(JSThread *thread, JSHandle thisObjHandle, + JSHandle callbackFnHandle, + JSMutableHandle accumulator, int64_t &k, int64_t &len) +{ + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSMutableHandle array(thread, thisObjHandle->GetElements()); + JSHandle thisObjVal(thisObjHandle); + JSTaggedValue callResult = JSTaggedValue::Undefined(); + while (k < len) { + array.Update(thisObjHandle->GetElements()); + JSTaggedValue kValue(array->Get(k)); + if (!kValue.IsHole()) { + JSHandle undefined = globalConst->GetHandledUndefined(); + const uint32_t argsLength = 4; // 4: «accumulator, kValue, k, O» + EcmaRuntimeCallInfo *info = + EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, undefined, undefined, argsLength); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + info->SetCallArg(accumulator.GetTaggedValue(), kValue, JSTaggedValue(k), + thisObjVal.GetTaggedValue()); + callResult = JSFunction::Call(info); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (array->GetLength() < len) { + len = array->GetLength(); + } + accumulator.Update(callResult); + } + k++; + if (!thisObjVal->IsStableJSArray(thread)) { + break; + } + } + return base::BuiltinsBase::GetTaggedDouble(true); +} + +JSTaggedValue JSStableArray::Slice(JSThread *thread, JSHandle thisObjHandle, + int64_t &k, int64_t &count) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle srcElements(thread, thisObjHandle->GetElements()); + int64_t len = static_cast(srcElements->GetLength()); + int64_t oldLen; + if (len > k + count) { + oldLen = count; + } else { + oldLen = len - k; + } + JSHandle dstElements = factory->NewAndCopyTaggedArray(srcElements, count, oldLen, k); + return factory->NewJSStableArrayWithElements(dstElements).GetTaggedValue(); +} + } // namespace panda::ecmascript diff --git a/ecmascript/js_stable_array.h b/ecmascript/js_stable_array.h index 48da8cb0ff57d460aeaf7a7f811238b708347d64..ec9266dcb380711bc05cc848c227c55f1f92c2fc 100644 --- a/ecmascript/js_stable_array.h +++ b/ecmascript/js_stable_array.h @@ -34,25 +34,69 @@ public: static JSTaggedValue HandleFindIndexOfStable(JSThread *thread, JSHandle thisObjHandle, JSHandle callbackFnHandle, JSHandle thisArgHandle, uint32_t &k); + static JSTaggedValue HandleFindLastIndexOfStable(JSThread *thread, JSHandle thisObjHandle, + JSHandle callbackFnHandle, + JSHandle thisArgHandle, int64_t &k); static JSTaggedValue HandleEveryOfStable(JSThread *thread, JSHandle thisObjHandle, JSHandle callbackFnHandle, JSHandle thisArgHandle, uint32_t &k); static JSTaggedValue HandleforEachOfStable(JSThread *thread, JSHandle thisObjHandle, JSHandle callbackFnHandle, - JSHandle thisArgHandle, uint32_t &k); + JSHandle thisArgHandle, uint32_t len, uint32_t &k); static JSTaggedValue IndexOf(JSThread *thread, JSHandle receiver, JSHandle searchElement, uint32_t from, uint32_t len); + static JSTaggedValue LastIndexOf(JSThread *thread, JSHandle receiver, + JSHandle searchElement, uint32_t from, uint32_t len); static JSTaggedValue Filter(JSHandle newArrayHandle, JSHandle thisObjHandle, EcmaRuntimeCallInfo *argv, uint32_t &k, uint32_t &toIndex); static JSTaggedValue Map(JSHandle newArrayHandle, JSHandle thisObjHandle, EcmaRuntimeCallInfo *argv, uint32_t &k, uint32_t len); - static JSTaggedValue Reverse(JSThread *thread, JSHandle thisObjHandle, - JSHandle thisHandle, int64_t &lower, uint32_t len); + static JSTaggedValue Reverse(JSThread *thread, JSHandle thisObjHandle, uint32_t len); static JSTaggedValue Concat(JSThread *thread, JSHandle newArrayHandle, JSHandle thisObjHandle, int64_t &k, int64_t &n); static JSTaggedValue FastCopyFromArrayToTypedArray(JSThread *thread, JSHandle &target, - DataViewType targetType, uint32_t targetOffset, + DataViewType targetType, uint64_t targetOffset, uint32_t srcLength, JSHandle &elements); + static JSTaggedValue At(JSHandle receiver, EcmaRuntimeCallInfo *argv); + static JSTaggedValue With(JSThread *thread, JSHandle receiver, + int64_t insertCount, int64_t index, JSHandle value); + static JSTaggedValue ToSpliced(JSHandle receiver, EcmaRuntimeCallInfo *argv, + int64_t argc, int64_t actualStart, int64_t actualSkipCount, int64_t insertCount); + static JSTaggedValue ToReversed(JSThread *thread, JSHandle receiver, int64_t insertCount); + static JSTaggedValue Reduce(JSThread *thread, JSHandle thisObjHandle, + JSHandle callbackFnHandle, + JSMutableHandle accumulator, int64_t &k, int64_t &len); + static JSTaggedValue Slice(JSThread *thread, JSHandle thisObjHandle, int64_t &k, int64_t &count); + +private: + static void SetSepValue(JSHandle sepStringHandle, int &sep, uint32_t &sepLength); + enum class IndexOfType { + IndexOf, + LastIndexOf + }; + + struct IndexOfContext { + JSThread *thread; + JSHandle receiver; + JSHandle searchElement; + uint32_t fromIndex; + uint32_t length; + }; + + template + static JSTaggedValue FindRawData(IndexOfContext &ctx, Predicate &&predicate); + template + static JSTaggedValue FindLastRawData(IndexOfContext &ctx, Predicate &&predicate); + template + static JSTaggedValue FindRawDataDispatch(IndexOfType type, IndexOfContext &ctx, Predicate &&predicate); + + static JSTaggedValue IndexOfZero(IndexOfType type, IndexOfContext &ctx); + static JSTaggedValue IndexOfInt32(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement); + static JSTaggedValue IndexOfDouble(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement); + static JSTaggedValue IndexOfObjectAddress(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement); + static JSTaggedValue IndexOfString(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement); + static JSTaggedValue IndexOfBigInt(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement); + static JSTaggedValue IndexOfDispatch(IndexOfType type, IndexOfContext &ctx, JSTaggedValue searchElement); }; } // namespace panda::ecmascript #endif // ECMASCRIPT_JS_STABLE_ARRAY_H diff --git a/ecmascript/js_tagged_value-inl.h b/ecmascript/js_tagged_value-inl.h index c7fbfa1601cca424bf285c520fd0ccda764083b8..b0321816fe80bc686223b20d024191311b130e27 100644 --- a/ecmascript/js_tagged_value-inl.h +++ b/ecmascript/js_tagged_value-inl.h @@ -24,9 +24,7 @@ #include "ecmascript/base/string_helper.h" #include "ecmascript/ecma_macros.h" #include "ecmascript/ecma_runtime_call_info.h" -#include "ecmascript/ecma_string-inl.h" #include "ecmascript/js_bigint.h" -#include "ecmascript/js_hclass-inl.h" #include "ecmascript/js_object.h" #include "ecmascript/js_proxy.h" #include "ecmascript/js_symbol.h" @@ -276,7 +274,7 @@ inline JSHandle JSTaggedValue::RequireObjectCoercible(JSThread *t const JSHandle &tagged, const char *message) { - if (tagged->IsUndefined() || tagged->IsNull()) { + if (tagged->IsUndefinedOrNull()) { THROW_TYPE_ERROR_AND_RETURN(thread, message, JSHandle(thread, JSTaggedValue::Exception())); } return tagged; @@ -305,6 +303,11 @@ inline bool JSTaggedValue::IsExtensible(JSThread *thread) const return IsHeapObject() && GetTaggedObject()->GetClass()->IsExtensible(); } +inline bool JSTaggedValue::IsExactlyZero() const +{ + return value_ == VALUE_ZERO || value_ == VALUE_POSITIVE_ZERO || value_ == VALUE_NEGATIVE_ZERO; +} + inline bool JSTaggedValue::IsClassConstructor() const { return IsHeapObject() && GetTaggedObject()->GetClass()->IsClassConstructor(); @@ -414,23 +417,17 @@ inline bool JSTaggedValue::StrictEqual(const JSTaggedValue &x, const JSTaggedVal if (x.IsInt() && y.IsInt()) { return StrictIntEquals(x.GetInt(), y.GetInt()); } - - if (x.IsDouble() && y.IsDouble()) { - return StrictNumberEquals(x.GetDouble(), y.GetDouble()); - } - if (x.IsNumber() && y.IsNumber()) { return StrictNumberEquals(x.GetNumber(), y.GetNumber()); } - + // Note: x == y must be put after number comparison + // in case of NaN (whose comparison result is always false even with another NaN) if (x == y) { return true; } - if (x.IsString() && y.IsString()) { return StringCompare(EcmaString::Cast(x.GetTaggedObject()), EcmaString::Cast(y.GetTaggedObject())); } - if (x.IsBigInt() && y.IsBigInt()) { return BigInt::Equal(x, y); } @@ -476,6 +473,11 @@ inline bool JSTaggedValue::IsTreeString() const return IsHeapObject() && GetTaggedObject()->GetClass()->IsTreeString(); } +inline bool JSTaggedValue::IsSlicedString() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsSlicedString(); +} + inline bool JSTaggedValue::IsBigInt() const { return IsHeapObject() && GetTaggedObject()->GetClass()->IsBigInt(); @@ -491,6 +493,11 @@ inline bool JSTaggedValue::IsTaggedArray() const return IsHeapObject() && GetTaggedObject()->GetClass()->IsTaggedArray(); } +inline bool JSTaggedValue::IsDictionary() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsDictionary(); +} + inline bool JSTaggedValue::IsByteArray() const { return IsHeapObject() && GetTaggedObject()->GetClass()->IsByteArray(); @@ -533,7 +540,13 @@ inline bool JSTaggedValue::IsJSNativePointer() const inline bool JSTaggedValue::CheckIsJSNativePointer() const { - return IsHeapObject() && !IsInvalidValue() && GetTaggedObject()->GetClass()->IsJSNativePointer(); + if (IsHeapObject() && !IsInvalidValue()) { + auto hclass = GetTaggedObject()->GetClass(); + if (hclass != nullptr) { + return hclass->IsJSNativePointer(); + } + } + return false; } inline bool JSTaggedValue::IsSymbol() const @@ -548,7 +561,13 @@ inline bool JSTaggedValue::IsJSProxy() const inline bool JSTaggedValue::CheckIsJSProxy() const { - return IsHeapObject() && !IsInvalidValue() && GetTaggedObject()->GetClass()->IsJSProxy(); + if (IsHeapObject() && !IsInvalidValue()) { + auto hclass = GetTaggedObject()->GetClass(); + if (hclass != nullptr) { + return hclass->IsJSProxy(); + } + } + return false; } inline bool JSTaggedValue::IsBoolean() const @@ -992,7 +1011,13 @@ inline bool JSTaggedValue::IsJSFunctionBase() const inline bool JSTaggedValue::CheckIsJSFunctionBase() const { - return IsHeapObject() && !IsInvalidValue() && GetTaggedObject()->GetClass()->IsJSFunctionBase(); + if (IsHeapObject() && !IsInvalidValue()) { + auto hclass = GetTaggedObject()->GetClass(); + if (hclass != nullptr) { + return hclass->IsJSFunctionBase(); + } + } + return false; } inline bool JSTaggedValue::IsBoundFunction() const @@ -1084,6 +1109,16 @@ inline bool JSTaggedValue::IsProtoChangeMarker() const return IsHeapObject() && GetTaggedObject()->GetClass()->IsProtoChangeMarker(); } +inline bool JSTaggedValue::IsMarkerCell() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsMarkerCell(); +} + +inline bool JSTaggedValue::IsTrackInfoObject() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsTrackInfoObject(); +} + inline bool JSTaggedValue::IsJSGlobalEnv() const { return IsHeapObject() && GetTaggedObject()->GetClass()->IsJsGlobalEnv(); diff --git a/ecmascript/js_tagged_value.cpp b/ecmascript/js_tagged_value.cpp index 9c3bcaa9fc4b171ce7845eff94c3758a7f8ec89d..4c2b1f943e0fea0695b82b94329dc778f23b31da 100644 --- a/ecmascript/js_tagged_value.cpp +++ b/ecmascript/js_tagged_value.cpp @@ -41,6 +41,7 @@ #include "ecmascript/module/js_module_namespace.h" #include "ecmascript/tagged_array.h" #include "ecmascript/object_factory.h" +#include "ecmascript/symbol_table.h" namespace panda::ecmascript { JSHandle GetTypeString(JSThread *thread, PreferredPrimitiveType type) @@ -387,6 +388,7 @@ JSTaggedValue JSTaggedValue::OrdinaryToPrimitive(JSThread *thread, const JSHandl keyString = globalConst->GetHandledValueOfString(); } JSHandle entryfunc = GetProperty(thread, tagged, keyString).GetValue(); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); if (entryfunc->IsCallable()) { JSHandle undefined = globalConst->GetHandledUndefined(); EcmaRuntimeCallInfo *info = @@ -810,6 +812,16 @@ bool JSTaggedValue::SetPrototype(JSThread *thread, const JSHandle if (obj->IsSpecialContainer() || !obj->IsECMAObject()) { THROW_TYPE_ERROR_AND_RETURN(thread, "Can not set Prototype on Container or non ECMA Object", false); } + if (obj->IsJSFunction() && proto->IsJSFunction()) { + JSHandle objFunc = JSHandle::Cast(obj); + JSHandle protoFunc = JSHandle::Cast(proto); + JSTaggedValue objProtoOrHClass(objFunc->GetProtoOrHClass()); + JSTaggedValue protoOrHClass(protoFunc->GetProtoOrHClass()); + if (objProtoOrHClass.IsJSHClass() && protoOrHClass.IsJSHClass() && objProtoOrHClass != protoOrHClass) { + JSHandle cachedJSHClass = JSHandle(thread, objProtoOrHClass); + objFunc->SetProtoOrHClass(thread, cachedJSHClass->GetPrototype()); + } + } return JSObject::SetPrototype(thread, JSHandle(obj), proto); } @@ -824,6 +836,7 @@ JSTaggedValue JSTaggedValue::GetPrototype(JSThread *thread, const JSHandle(obj)); } + bool JSTaggedValue::PreventExtensions(JSThread *thread, const JSHandle &obj) { if (obj->IsJSProxy()) { @@ -853,11 +866,10 @@ JSHandle JSTaggedValue::GetOwnPropertyKeys(JSThread *thread, const } JSHandle JSTaggedValue::GetAllPropertyKeys(JSThread *thread, - const JSHandle &obj, uint32_t filter) + const JSHandle &obj, uint32_t filter) { if (obj->IsJSProxy()) { - LOG_ECMA(WARN) << "GetAllPropertyKeys do not support JSProxy yet"; - return thread->GetEcmaVM()->GetFactory()->EmptyArray(); + return JSProxy::GetAllPropertyKeys(thread, JSHandle(obj), filter); } if (obj->IsTypedArray()) { LOG_ECMA(WARN) << "GetAllPropertyKeys do not support TypedArray yet"; @@ -942,6 +954,25 @@ bool JSTaggedValue::GlobalHasOwnProperty(JSThread *thread, const JSHandle &tagged) +{ + // 1. If v is an Object, return true. + if (tagged->IsECMAObject()) { + return true; + } + // 2. If v is a Symbol and KeyForSymbol(v) is undefined, return true. + if (tagged->IsSymbol()) { + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + auto *table = env->GetRegisterSymbols().GetObject(); + JSTaggedValue key = table->FindSymbol(tagged.GetTaggedValue()); + if (key.IsUndefined()) { + return true; + } + } + // 3. Return false. + return false; +} + JSTaggedNumber JSTaggedValue::ToIndex(JSThread *thread, const JSHandle &tagged) { if (tagged->IsInt() && tagged->GetInt() >= 0) { @@ -1170,6 +1201,7 @@ bool JSTaggedValue::GetContainerProperty(JSThread *thread, const JSHandle JSTaggedValue::ToNumeric(JSThread *thread, JSHandle tagged) { // 1. Let primValue be ? ToPrimitive(value, number) @@ -1185,6 +1217,7 @@ JSHandle JSTaggedValue::ToNumeric(JSThread *thread, JSHandle value(thread, number); return value; } + OperationResult JSTaggedValue::GetJSAPIProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key) { diff --git a/ecmascript/js_tagged_value.h b/ecmascript/js_tagged_value.h index afe68e793aa366c03794a6cda1a9cbadf04a899a..62668ddd5e1f88aa74247c427af753cf242260c9 100644 --- a/ecmascript/js_tagged_value.h +++ b/ecmascript/js_tagged_value.h @@ -34,6 +34,7 @@ class PropertyDescriptor; class OperationResult; class EcmaString; class JSThread; +struct Reference; static constexpr double SAFE_NUMBER = 9007199254740991LL; @@ -112,6 +113,10 @@ public: static constexpr size_t INT_SIGN_BIT_OFFSET = 31; static constexpr size_t DOUBLE_ENCODE_OFFSET_BIT = 48; static constexpr JSTaggedType DOUBLE_ENCODE_OFFSET = 1ULL << DOUBLE_ENCODE_OFFSET_BIT; + // Tagged +0.0 = IEEE754 representation of +0.0 + offset + static constexpr JSTaggedType VALUE_POSITIVE_ZERO = 0x0000'0000'0000'0000uLL + DOUBLE_ENCODE_OFFSET; + // Tagged -0.0 = IEEE754 representation of -0.0 + offset + static constexpr JSTaggedType VALUE_NEGATIVE_ZERO = 0x8000'0000'0000'0000uLL + DOUBLE_ENCODE_OFFSET; static JSTaggedValue Cast(TaggedObject *object) { @@ -443,6 +448,7 @@ public: bool IsInteger() const; bool WithinInt32() const; bool IsZero() const; + bool IsExactlyZero() const; static bool IsPropertyKey(const JSHandle &key); static JSHandle RequireObjectCoercible(JSThread *thread, const JSHandle &tagged, const char *message = "RequireObjectCoercible throw Error"); @@ -458,6 +464,9 @@ public: // ES6 7.4 Operations on Iterator Objects static JSObject *CreateIterResultObject(JSThread *thread, const JSHandle &value, bool done); + // ECMAScript 2023 allow the use of most Symbols as keys in weak collections + static bool CanBeHeldWeakly(JSThread *thread, const JSHandle &tagged); + // ecma6 7.3 static OperationResult GetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key); @@ -513,8 +522,10 @@ public: bool IsLineString() const; bool IsConstantString() const; bool IsTreeString() const; + bool IsSlicedString() const; bool IsStringOrSymbol() const; bool IsTaggedArray() const; + bool IsDictionary() const; bool IsByteArray() const; bool IsConstantPool() const; bool IsAOTLiteralInfo() const; @@ -663,6 +674,8 @@ public: bool IsPropertyBox() const; bool IsProtoChangeMarker() const; bool IsProtoChangeDetails() const; + bool IsMarkerCell() const; + bool IsTrackInfoObject() const; bool IsMachineCodeObject() const; bool IsClassInfoExtractor() const; bool IsTSType() const; @@ -705,8 +718,7 @@ public: void DumpTaggedValue(std::ostream &os) const DUMP_API_ATTR; void Dump(std::ostream &os) const DUMP_API_ATTR; void D() const DUMP_API_ATTR; - void DumpForSnapshot(std::vector> &vec, - bool isVmMode = true) const; + void DumpForSnapshot(std::vector &vec, bool isVmMode = true) const; static void DV(JSTaggedType val) DUMP_API_ATTR; private: diff --git a/ecmascript/js_thread.cpp b/ecmascript/js_thread.cpp index d5fd501ba831f88abf9900b75d256fcd5f1f0a7a..f89960511c514f21baa40511d57e19815d2e7eca 100644 --- a/ecmascript/js_thread.cpp +++ b/ecmascript/js_thread.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -25,6 +25,7 @@ #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER) #include "ecmascript/dfx/cpu_profiler/cpu_profiler.h" #endif +#include "ecmascript/dfx/vm_thread_control.h" #include "ecmascript/ecma_global_storage.h" #include "ecmascript/ecma_param_configuration.h" #include "ecmascript/global_env_constants-inl.h" @@ -111,10 +112,12 @@ JSThread::~JSThread() delete item; } contexts_.clear(); + GetNativeAreaAllocator()->FreeArea(regExpCache_); glueData_.frameBase_ = nullptr; nativeAreaAllocator_ = nullptr; heapRegionAllocator_ = nullptr; + regExpCache_ = nullptr; if (vmThreadControl_ != nullptr) { delete vmThreadControl_; vmThreadControl_ = nullptr; @@ -215,17 +218,16 @@ void JSThread::Iterate(const RootVisitor &visitor, const RootRangeVisitor &range if (!glueData_.exception_.IsHole()) { visitor(Root::ROOT_VM, ObjectSlot(ToUintPtr(&glueData_.exception_))); } - // visit global Constant - glueData_.globalConst_.VisitRangeSlot(rangeVisitor); + EcmaContext *tempContext = currentContext_; for (EcmaContext *context : contexts_) { // visit stack roots - SwitchCurrentContext(context); + SwitchCurrentContext(context, true); FrameHandler frameHandler(this); frameHandler.Iterate(visitor, rangeVisitor, derivedVisitor); context->Iterate(visitor, rangeVisitor); } - SwitchCurrentContext(tempContext); + SwitchCurrentContext(tempContext, true); // visit tagged handle storage roots if (vm_->GetJSOptions().EnableGlobalLeakCheck()) { IterateHandleWithCheck(visitor, rangeVisitor); @@ -362,18 +364,22 @@ void JSThread::ShrinkHandleStorage(int prevIndex) GetCurrentEcmaContext()->ShrinkHandleStorage(prevIndex); } -void JSThread::NotifyStableArrayElementsGuardians(JSHandle receiver) +void JSThread::NotifyStableArrayElementsGuardians(JSHandle receiver, StableArrayChangeKind changeKind) { if (!glueData_.stableArrayElementsGuardians_) { return; } - if (!receiver->GetJSHClass()->IsPrototype()) { + if (!receiver->GetJSHClass()->IsPrototype() && !receiver->IsJSArray()) { return; } auto env = GetEcmaVM()->GetGlobalEnv(); if (receiver.GetTaggedValue() == env->GetObjectFunctionPrototype().GetTaggedValue() || receiver.GetTaggedValue() == env->GetArrayPrototype().GetTaggedValue()) { glueData_.stableArrayElementsGuardians_ = false; + return; + } + if (changeKind == StableArrayChangeKind::PROTO && receiver->IsJSArray()) { + glueData_.stableArrayElementsGuardians_ = false; } } @@ -420,7 +426,7 @@ void JSThread::CheckOrSwitchPGOStubs() } if (isSwitch) { Address curAddress; -#define SWITCH_PGO_STUB_ENTRY(fromName, toName) \ +#define SWITCH_PGO_STUB_ENTRY(fromName, toName, ...) \ curAddress = GetBCStubEntry(BytecodeStubCSigns::ID_##fromName); \ SetBCStubEntry(BytecodeStubCSigns::ID_##fromName, GetBCStubEntry(BytecodeStubCSigns::ID_##toName)); \ SetBCStubEntry(BytecodeStubCSigns::ID_##toName, curAddress); @@ -568,6 +574,7 @@ bool JSThread::IsMainThread() void JSThread::PushContext(EcmaContext *context) { + const_cast(vm_->GetHeap())->WaitAllTasksFinished(); contexts_.emplace_back(context); if (!currentContext_) { @@ -596,9 +603,10 @@ void JSThread::PushContext(EcmaContext *context) void JSThread::PopContext() { contexts_.pop_back(); + currentContext_ = contexts_.back(); } -void JSThread::SwitchCurrentContext(EcmaContext *currentContext) +void JSThread::SwitchCurrentContext(EcmaContext *currentContext, bool isInIterate) { ASSERT(std::count(contexts_.begin(), contexts_.end(), currentContext)); @@ -621,17 +629,52 @@ void JSThread::SwitchCurrentContext(EcmaContext *currentContext) SetGlueGlobalEnv(*(currentContext->GetGlobalEnv())); SetGlobalObject(currentContext->GetGlobalEnv()->GetGlobalObject()); } + if (!isInIterate) { + // If isInIterate is true, it means it is in GC iterate and global variables are no need to change. + glueData_.globalConst_ = const_cast(currentContext->GlobalConstants()); + } currentContext_ = currentContext; } +bool JSThread::EraseContext(EcmaContext *context) +{ + const_cast(vm_->GetHeap())->WaitAllTasksFinished(); + bool isCurrentContext = false; + auto iter = std::find(contexts_.begin(), contexts_.end(), context); + if (*iter == context) { + if (currentContext_ == context) { + isCurrentContext = true; + } + contexts_.erase(iter); + if (isCurrentContext) { + SwitchCurrentContext(contexts_.back()); + } + return true; + } + return false; +} + PropertiesCache *JSThread::GetPropertiesCache() const { return currentContext_->GetPropertiesCache(); } -void JSThread::InitGlobalConst(JSHClass *hClass) +const GlobalEnvConstants *JSThread::GetFirstGlobalConst() const +{ + return contexts_[0]->GlobalConstants(); +} + +bool JSThread::IsAllContextsInitialized() const { - glueData_.globalConst_.Init(this, hClass); + return contexts_.back()->IsInitialized(); +} + +Area *JSThread::GetOrCreateRegExpCache() +{ + if (regExpCache_ == nullptr) { + regExpCache_ = nativeAreaAllocator_->AllocateArea(MAX_REGEXP_CACHE_SIZE); + } + return regExpCache_; } } // namespace panda::ecmascript diff --git a/ecmascript/js_thread.h b/ecmascript/js_thread.h index 1637a68f2e1c902e935ab7073b03a4dc908e0ab7..719a7a6e0b84461d07291c79d8c8820f9e7a31a2 100644 --- a/ecmascript/js_thread.h +++ b/ecmascript/js_thread.h @@ -24,7 +24,7 @@ #include "ecmascript/compiler/common_stubs.h" #include "ecmascript/compiler/interpreter_stub.h" #include "ecmascript/compiler/rt_call_signature.h" -#include "ecmascript/dfx/vm_thread_control.h" +#include "ecmascript/elements.h" #include "ecmascript/frames.h" #include "ecmascript/global_env_constants.h" #include "ecmascript/mem/visitor.h" @@ -58,6 +58,8 @@ enum class BCStubStatus: uint8_t { PROFILE_BC_STUB, }; +enum class StableArrayChangeKind { PROTO, NOT_PROTO }; + struct BCStubEntries { static constexpr size_t EXISTING_BC_HANDLER_STUB_ENTRIES_COUNT = kungfu::BytecodeStubCSigns::NUM_OF_ALL_NORMAL_STUBS; @@ -197,6 +199,12 @@ public: using BCStubStatusBits = PGOStatusBits::NextField; using ThreadId = uint32_t; + enum FrameDroppedState { + StateFalse = 0, + StateTrue, + StatePending + }; + explicit JSThread(EcmaVM *vm); PUBLIC_API ~JSThread(); @@ -313,10 +321,15 @@ public: const GlobalEnvConstants *GlobalConstants() const { - return &glueData_.globalConst_; + return glueData_.globalConst_; } - void NotifyStableArrayElementsGuardians(JSHandle receiver); + const CMap &GetArrayHClassIndexMap() const + { + return arrayHClassIndexMap_; + } + + void NotifyStableArrayElementsGuardians(JSHandle receiver, StableArrayChangeKind changeKind); bool IsStableArrayElementsGuardiansInvalid() const { @@ -514,6 +527,16 @@ public: return vmThreadControl_; } + void SetEnableStackSourceFile(bool value) + { + enableStackSourceFile_ = value; + } + + bool GetEnableStackSourceFile() const + { + return enableStackSourceFile_; + } + static constexpr size_t GetGlueDataOffset() { return MEMBER_OFFSET(JSThread, glueData_); @@ -640,6 +663,61 @@ public: return glueData_.allowCrossThreadExecution_; } + bool IsFrameDropped() + { + return glueData_.isFrameDropped_; + } + + void SetFrameDroppedState() + { + glueData_.isFrameDropped_ = true; + } + + void ResetFrameDroppedState() + { + glueData_.isFrameDropped_ = false; + } + + bool IsEntryFrameDroppedTrue() + { + return glueData_.entryFrameDroppedState_ == FrameDroppedState::StateTrue; + } + + bool IsEntryFrameDroppedPending() + { + return glueData_.entryFrameDroppedState_ == FrameDroppedState::StatePending; + } + + void SetEntryFrameDroppedState() + { + glueData_.entryFrameDroppedState_ = FrameDroppedState::StateTrue; + } + + void ResetEntryFrameDroppedState() + { + glueData_.entryFrameDroppedState_ = FrameDroppedState::StateFalse; + } + + void PendingEntryFrameDroppedState() + { + glueData_.entryFrameDroppedState_ = FrameDroppedState::StatePending; + } + + bool IsDebugMode() + { + return glueData_.isDebugMode_; + } + + void SetDebugModeState() + { + glueData_.isDebugMode_ = true; + } + + void ResetDebugModeState() + { + glueData_.isDebugMode_ = false; + } + bool IsStartGlobalLeakCheck() const; bool EnableGlobalObjectLeakCheck() const; bool EnableGlobalPrimitiveLeakCheck() const; @@ -670,9 +748,12 @@ public: base::AlignedUint64, base::AlignedUint64, base::AlignedPointer, - GlobalEnvConstants, + base::AlignedPointer, base::AlignedUint64, base::AlignedUint64, + JSTaggedValue, + base::AlignedBool, + base::AlignedBool, JSTaggedValue> { enum class Index : size_t { BCStubEntriesIndex = 0, @@ -697,6 +778,9 @@ public: AllowCrossThreadExecutionIndex, InterruptVectorIndex, IsStartHeapSamplingIndex, + IsDebugModeIndex, + IsFrameDroppedIndex, + EntryFrameDroppedStateIndex, NumOfMembers }; static_assert(static_cast(Index::NumOfMembers) == NumOfTypes); @@ -806,6 +890,21 @@ public: return GetOffset(Index::IsStartHeapSamplingIndex)>(isArch32); } + static size_t GetIsDebugModeOffset(bool isArch32) + { + return GetOffset(Index::IsDebugModeIndex)>(isArch32); + } + + static size_t GetIsFrameDroppedOffset(bool isArch32) + { + return GetOffset(Index::IsFrameDroppedIndex)>(isArch32); + } + + static size_t GetEntryFrameDroppedStateOffset(bool isArch32) + { + return GetOffset(Index::EntryFrameDroppedStateIndex)>(isArch32); + } + alignas(EAS) BCStubEntries bcStubEntries_; alignas(EAS) JSTaggedValue exception_ {JSTaggedValue::Hole()}; alignas(EAS) JSTaggedValue globalObject_ {JSTaggedValue::Hole()}; @@ -824,10 +923,13 @@ public: alignas(EAS) uint64_t stackStart_ {0}; alignas(EAS) uint64_t stackLimit_ {0}; alignas(EAS) GlobalEnv *glueGlobalEnv_; - alignas(EAS) GlobalEnvConstants globalConst_; + alignas(EAS) GlobalEnvConstants *globalConst_; alignas(EAS) bool allowCrossThreadExecution_ {false}; alignas(EAS) volatile uint64_t interruptVector_ {0}; alignas(EAS) JSTaggedValue isStartHeapSampling_ {JSTaggedValue::False()}; + alignas(EAS) bool isDebugMode_ {false}; + alignas(EAS) bool isFrameDropped_ {false}; + alignas(EAS) uint64_t entryFrameDroppedState_ {FrameDroppedState::StateFalse}; }; STATIC_ASSERT_EQ_ARCH(sizeof(GlueData), GlueData::SizeArch32, GlueData::SizeArch64); @@ -838,20 +940,35 @@ public: { return currentContext_; } - void SwitchCurrentContext(EcmaContext *currentContext); + void SwitchCurrentContext(EcmaContext *currentContext, bool isInIterate = false); CVector GetEcmaContexts() { return contexts_; } + + bool EraseContext(EcmaContext *context); + + const GlobalEnvConstants *GetFirstGlobalConst() const; + bool IsAllContextsInitialized() const; + Area *GetOrCreateRegExpCache(); + private: NO_COPY_SEMANTIC(JSThread); NO_MOVE_SEMANTIC(JSThread); + void SetGlobalConst(GlobalEnvConstants *globalConst) + { + glueData_.globalConst_ = globalConst; + } void SetCurrentEcmaContext(EcmaContext *context) { currentContext_ = context; } - void InitGlobalConst(JSHClass *hClass); + + void SetArrayHClassIndexMap(const CMap &map) + { + arrayHClassIndexMap_ = map; + } void DumpStack() DUMP_API_ATTR; @@ -864,6 +981,7 @@ private: GlueData glueData_; std::atomic id_; EcmaVM *vm_ {nullptr}; + Area *regExpCache_ {nullptr}; // MM: handles, global-handles, and aot-stubs. int nestedLevel_ = 0; @@ -888,6 +1006,7 @@ private: bool runtimeState_ {false}; bool isAsmInterpreter_ {false}; VmThreadControl *vmThreadControl_ {nullptr}; + bool enableStackSourceFile_ {true}; // CpuProfiler bool isProfiling_ {false}; @@ -897,10 +1016,13 @@ private: bool finalizationCheckState_ {false}; + CMap arrayHClassIndexMap_; + CVector contexts_; EcmaContext *currentContext_ {nullptr}; friend class GlobalHandleCollection; friend class EcmaVM; + friend class EcmaContext; }; } // namespace panda::ecmascript #endif // ECMASCRIPT_JS_THREAD_H diff --git a/ecmascript/js_typed_array.cpp b/ecmascript/js_typed_array.cpp index d0aa382e0b1e220e367d6ca39a110a49aa5adfa8..50cce14f50606fa38f7100f8a493013e54abf11f 100644 --- a/ecmascript/js_typed_array.cpp +++ b/ecmascript/js_typed_array.cpp @@ -99,6 +99,7 @@ bool JSTypedArray::HasProperty(JSThread *thread, const JSHandle & } JSHandle numericIndexHandle(thread, numericIndex); JSTaggedNumber numericIndexNumber = JSTaggedValue::ToNumber(thread, numericIndexHandle); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); double tNegZero = -0.0; auto eZero = JSTaggedNumber(tNegZero); JSHandle zero(thread, JSTaggedValue(0)); @@ -120,6 +121,7 @@ bool JSTypedArray::HasProperty(JSThread *thread, const JSHandle & return true; } JSTaggedValue parent = JSTaggedValue::GetPrototype(thread, JSHandle::Cast(typedarrayObj)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); if (!parent.IsNull()) { return JSTaggedValue::HasProperty(thread, JSHandle(thread, parent), key); } @@ -161,6 +163,7 @@ bool JSTypedArray::DefineOwnProperty(JSThread *thread, const JSHandle numericIndexHandle(thread, numericIndex); JSTaggedNumber numericIndexNumber = JSTaggedValue::ToNumber(thread, numericIndexHandle); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false); double tNegZero = -0.0; auto eZero = JSTaggedNumber(tNegZero); JSHandle zero(thread, JSTaggedValue(0)); @@ -293,6 +296,7 @@ JSHandle JSTypedArray::OwnPropertyKeys(JSThread *thread, const JSHa for (uint32_t k = 0; k < bufferKeysLen; k++) { tKey.Update(JSTaggedValue(k)); JSHandle sKey(JSTaggedValue::ToString(thread, tKey)); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread); nameList->Set(thread, copyLength, sKey.GetTaggedValue()); copyLength++; } @@ -340,7 +344,7 @@ JSHandle JSTypedArray::OwnEnumPropertyKeys(JSThread *thread, const // a. Add ToString(i) as the last element of keys. uint32_t copyLength = 0; for (uint32_t k = 0; k < bufferKeysLen; k++) { - auto key = base::NumberHelper::NumberToString(thread, JSTaggedValue(k)); + auto key = base::NumberHelper::IntToEcmaString(thread, k); nameList->Set(thread, copyLength, key); copyLength++; } @@ -696,7 +700,8 @@ JSTaggedValue JSTypedArray::FastSetPropertyByIndex(JSThread *thread, const JSTag // Let elementType be the String value of the Element Type value in Table 49 for arrayTypeName. DataViewType elementType = TypedArrayHelper::GetType(jsType); // Perform SetValueInBuffer(buffer, indexedPosition, elementType, numValue). - return BuiltinsArrayBuffer::FastSetValueInBuffer(buffer, byteIndex, elementType, numValue.GetNumber(), true); + return BuiltinsArrayBuffer::FastSetValueInBuffer(thread, + buffer, byteIndex, elementType, numValue.GetNumber(), true); } JSTaggedValue JSTypedArray::GetOffHeapBuffer(JSThread *thread, JSHandle &typedArray) @@ -723,4 +728,41 @@ JSTaggedValue JSTypedArray::GetOffHeapBuffer(JSThread *thread, JSHandle &typedArray, + const JSHandle &value, uint32_t start, uint32_t end) +{ + // Assert: O is an Object that has [[ViewedArrayBuffer]], [[ArrayLength]], [[ByteOffset]], and + // [[TypedArrayName]] internal slots. + ASSERT(typedArray->IsTypedArray()); + // If O.[[ContentType]] is BigInt, let numValue be ? ToBigInt(value). + JSHandle typedArrayObj = JSHandle::Cast(typedArray); + if (UNLIKELY(typedArrayObj->GetContentType() == ContentType::BigInt || value->IsECMAObject())) { + return false; + } + JSTaggedNumber numValue = JSTypedArray::NonEcmaObjectToNumber(thread, value.GetTaggedValue()); + // ReturnIfAbrupt(numValue). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, true); + JSTaggedValue buffer = typedArrayObj->GetViewedArrayBufferOrByteArray(); + // If ℝ(index) < 0 or ℝ(index) ≥ O.[[ArrayLength]], return false. + uint32_t arrLen = typedArrayObj->GetArrayLength(); + // Let offset be the value of O’s [[ByteOffset]] internal slot. + uint32_t offset = typedArrayObj->GetByteOffset(); + // Let arrayTypeName be the String value of O’s [[TypedArrayName]] + // Let elementSize be the Number value of the Element Size value specified in Table 49 for arrayTypeName. + JSType jsType = typedArrayObj->GetClass()->GetObjectType(); + uint32_t elementSize = TypedArrayHelper::GetElementSize(jsType); + // Let elementType be the String value of the Element Type value in Table 49 for arrayTypeName. + DataViewType elementType = TypedArrayHelper::GetType(jsType); + uint64_t byteIndex = 0; + uint32_t k = start; + while (k < end && k < arrLen) { + // Let indexedPosition = (index × elementSize) + offset. + byteIndex = k * elementSize + offset; + // Perform SetValueInBuffer(buffer, indexedPosition, elementType, numValue). + BuiltinsArrayBuffer::FastSetValueInBuffer(thread, buffer, byteIndex, elementType, numValue.GetNumber(), true); + k++; + } + return true; +} } // namespace panda::ecmascript diff --git a/ecmascript/js_typed_array.h b/ecmascript/js_typed_array.h index fbd93024dcfd673e44610895cb2343becf8130df..39eb78065f2f488e730e5f873e10a63e97f54b2b 100644 --- a/ecmascript/js_typed_array.h +++ b/ecmascript/js_typed_array.h @@ -24,7 +24,7 @@ namespace panda::ecmascript { enum class ContentType : uint8_t { None = 1, Number, BigInt }; class JSTypedArray : public JSObject { public: - static constexpr size_t MAX_ONHEAP_LENGTH = 64; + static constexpr size_t MAX_ONHEAP_LENGTH = 512 * 8; static JSTypedArray *Cast(TaggedObject *object) { #if ECMASCRIPT_ENABLE_CAST_CHECK @@ -98,6 +98,8 @@ public: // only use in TypeArray fast set property static JSTaggedNumber NonEcmaObjectToNumber(JSThread *thread, const JSTaggedValue tagged); static JSTaggedValue GetOffHeapBuffer(JSThread *thread, JSHandle &typedArray); + static bool FastTypedArrayFill(JSThread *thread, const JSHandle &typedArray, + const JSHandle &value, uint32_t start, uint32_t end); static constexpr size_t VIEWED_ARRAY_BUFFER_OFFSET = JSObject::SIZE; static DataViewType GetTypeFromName(JSThread *thread, const JSHandle &typeName); ACCESSORS(ViewedArrayBufferOrByteArray, VIEWED_ARRAY_BUFFER_OFFSET, TYPED_ARRAY_NAME_OFFSET) diff --git a/ecmascript/js_vm/BUILD.gn b/ecmascript/js_vm/BUILD.gn index f2b973427f0d7ede309ae0068b050f111e07b519..e96ad28e625208ba2a3adee07d6c012f41500c6e 100644 --- a/ecmascript/js_vm/BUILD.gn +++ b/ecmascript/js_vm/BUILD.gn @@ -44,3 +44,27 @@ ohos_executable("ark_js_vm") { part_name = "ets_runtime" subsystem_name = "arkcompiler" } + +ohos_executable("ark_js_context") { + sources = [ "context_main.cpp" ] + + configs = [ + "$js_root:ark_jsruntime_common_config", + "$js_root:ark_jsruntime_public_config", + ] + + deps = [ "$js_root:libark_jsruntime" ] + external_deps = [ + "runtime_core:arkfile_header_deps", + "runtime_core:libarkbase_static", + ] + + # hiviewdfx libraries + external_deps += hiviewdfx_ext_deps + deps += hiviewdfx_deps + + install_enable = false + + part_name = "ets_runtime" + subsystem_name = "arkcompiler" +} diff --git a/ecmascript/js_vm/context_main.cpp b/ecmascript/js_vm/context_main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6b75f7c3e7bd442ae61e9851f03c2d94a627f49e --- /dev/null +++ b/ecmascript/js_vm/context_main.cpp @@ -0,0 +1,128 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include "ecmascript/base/string_helper.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/js_runtime_options.h" +#include "ecmascript/log.h" +#include "ecmascript/mem/mem_controller.h" +#include "ecmascript/mem/clock_scope.h" +#include "ecmascript/napi/include/jsnapi.h" + +namespace panda::ecmascript { +void BlockSignals() +{ +#if defined(PANDA_TARGET_UNIX) + sigset_t set; + if (sigemptyset(&set) == -1) { + LOG_ECMA(ERROR) << "sigemptyset failed"; + return; + } +#endif // PANDA_TARGET_UNIX +} + +std::string GetHelper() +{ + std::string str; + str.append(COMMON_HELP_HEAD_MSG); + str.append(HELP_OPTION_MSG); + return str; +} + +int Main(const int argc, const char **argv) +{ + auto startTime = + std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()) + .count(); + + BlockSignals(); + + if (argc < 2) { // 2: at least have two arguments + std::cerr << GetHelper(); + return -1; + } + + int newArgc = argc; + std::string files = argv[argc - 1]; + if (!base::StringHelper::EndsWith(files, ".abc")) { + std::cerr << "The last argument must be abc file" << std::endl; + std::cerr << GetHelper(); + return 1; + } + + newArgc--; + JSRuntimeOptions runtimeOptions; + bool retOpt = runtimeOptions.ParseCommand(newArgc, argv); + if (!retOpt) { + std::cerr << GetHelper(); + return 1; + } + + if (runtimeOptions.IsStartupTime()) { + std::cout << "\n" + << "Startup start time: " << startTime << std::endl; + } + bool ret = true; + EcmaVM *vm = JSNApi::CreateEcmaVM(runtimeOptions); + if (vm == nullptr) { + std::cerr << "Cannot Create vm" << std::endl; + return -1; + } + + bool isMergeAbc = runtimeOptions.GetMergeAbc(); + JSNApi::SetBundle(vm, !isMergeAbc); + { + LocalScope scope(vm); + std::string entry = runtimeOptions.GetEntryPoint(); +#if defined(PANDA_TARGET_WINDOWS) + arg_list_t fileNames = base::StringHelper::SplitString(files, ";"); +#else + arg_list_t fileNames = base::StringHelper::SplitString(files, ":"); +#endif + EcmaContext *context1 = JSNApi::CreateJSContext(vm); + ClockScope execute; + for (const auto &fileName : fileNames) { + auto res = JSNApi::Execute(vm, fileName, entry); + if (!res) { + std::cerr << "Cannot execute panda file '" << fileName << "' with entry '" << entry << "'" << std::endl; + ret = false; + break; + } + } + auto totalTime = execute.TotalSpentTime(); + JSNApi::DestroyJSContext(vm, context1); + + if (runtimeOptions.IsEnablePrintExecuteTime()) { + std::cout << "execute pandafile spent time " << totalTime << "ms" << std::endl; + } + } + + JSNApi::DestroyJSVM(vm); + return ret ? 0 : -1; +} +} // namespace panda::ecmascript + +int main(int argc, const char **argv) +{ + return panda::ecmascript::Main(argc, argv); +} diff --git a/ecmascript/js_weak_container.cpp b/ecmascript/js_weak_container.cpp index 8874c7ba8e3a32d025ab38eb0f10900037771073..6cb6bfeebe0a151b27a7875b2e51ab0eb44c1856 100644 --- a/ecmascript/js_weak_container.cpp +++ b/ecmascript/js_weak_container.cpp @@ -25,7 +25,7 @@ void JSWeakMap::Set(JSThread *thread, const JSHandle &map, const JSHa { [[maybe_unused]] EcmaHandleScope handleScope(thread); if (!LinkedHashMap::IsKey(JSTaggedValue(key.GetTaggedValue().CreateAndGetWeakRef()))) { - THROW_TYPE_ERROR(thread, "the value must be Key of JSMap"); + THROW_TYPE_ERROR(thread, "the value must be Key of JSWeakMap"); } JSHandle mapHandle(thread, LinkedHashMap::Cast(map->GetLinkedMap().GetTaggedObject())); @@ -62,6 +62,18 @@ int JSWeakMap::GetSize() const return LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject())->NumberOfElements(); } +JSTaggedValue JSWeakMap::GetKey(int entry) const +{ + ASSERT_PRINT(entry >= 0 && entry < GetSize(), "entry must be non-negative integer less than capacity"); + return LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject())->GetKey(entry); +} + +JSTaggedValue JSWeakMap::GetValue(int entry) const +{ + ASSERT_PRINT(entry >= 0 && entry < GetSize(), "entry must be non-negative integer less than capacity"); + return LinkedHashMap::Cast(GetLinkedMap().GetTaggedObject())->GetValue(entry); +} + void JSWeakSet::Add(JSThread *thread, const JSHandle &weakSet, const JSHandle &value) { if (!LinkedHashSet::IsKey(value.GetTaggedValue())) { @@ -95,4 +107,10 @@ int JSWeakSet::GetSize() const { return LinkedHashSet::Cast(GetLinkedSet().GetTaggedObject())->NumberOfElements(); } + +JSTaggedValue JSWeakSet::GetValue(int entry) const +{ + ASSERT_PRINT(entry >= 0 && entry < GetSize(), "entry must be non-negative integer less than capacity"); + return LinkedHashSet::Cast(GetLinkedSet().GetTaggedObject())->GetValue(entry); +} } // namespace panda::ecmascript diff --git a/ecmascript/js_weak_container.h b/ecmascript/js_weak_container.h index 48e9e41df2a9debb03fe68fe922894fb13ba8300..badfb7f4e7aefc6955c0dc7733746a18f64deb34 100644 --- a/ecmascript/js_weak_container.h +++ b/ecmascript/js_weak_container.h @@ -38,6 +38,10 @@ public: int GetSize() const; + JSTaggedValue GetKey(int entry) const; + + JSTaggedValue GetValue(int entry) const; + static constexpr size_t LINKED_MAP_OFFSET = JSObject::SIZE; ACCESSORS(LinkedMap, LINKED_MAP_OFFSET, SIZE) @@ -60,6 +64,8 @@ public: int GetSize() const; + JSTaggedValue GetValue(int entry) const; + static constexpr size_t LINKED_SET_OFFSET = JSObject::SIZE; ACCESSORS(LinkedSet, LINKED_SET_OFFSET, SIZE) diff --git a/ecmascript/jspandafile/accessor/module_data_accessor.cpp b/ecmascript/jspandafile/accessor/module_data_accessor.cpp index d833fa44c3eca856c7628a87c28089ed818dd3be..da6ec423d4d16857126997525e388c7d477ddb37 100644 --- a/ecmascript/jspandafile/accessor/module_data_accessor.cpp +++ b/ecmascript/jspandafile/accessor/module_data_accessor.cpp @@ -14,6 +14,7 @@ */ #include "ecmascript/jspandafile/accessor/module_data_accessor.h" +#include "ecmascript/global_env_constants-inl.h" #include "ecmascript/jspandafile/js_pandafile_manager.h" namespace panda::ecmascript { diff --git a/ecmascript/jspandafile/bytecode_inst/old_instruction_enum.h b/ecmascript/jspandafile/bytecode_inst/old_instruction_enum.h index ca507445e06387dfb13b24419d9022adcc435881..c814aa41f3a8ef4d0d69c3a809a08de6b03b5443 100644 --- a/ecmascript/jspandafile/bytecode_inst/old_instruction_enum.h +++ b/ecmascript/jspandafile/bytecode_inst/old_instruction_enum.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * Copyright (c) 2022-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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef OLD_INSTRUCTION_ENUM_H -#define OLD_INSTRUCTION_ENUM_H +#ifndef ECMASCRIPT_JSPANDAFILE_BYTECODE_INST_OLD_INSTRUCTION_ENUM_H +#define ECMASCRIPT_JSPANDAFILE_BYTECODE_INST_OLD_INSTRUCTION_ENUM_H enum class Format : uint8_t { ID16, @@ -478,4 +478,4 @@ enum Flags : uint32_t { ACC_WRITE = 0x40000, }; -#endif // OLD_INSTRUCTION_ENUM_H +#endif // ECMASCRIPT_JSPANDAFILE_BYTECODE_INST_OLD_INSTRUCTION_ENUM_H diff --git a/ecmascript/jspandafile/class_info_extractor.cpp b/ecmascript/jspandafile/class_info_extractor.cpp index 2e03bcb2cd7abf84cca6c6cd674b9a667dce8ae7..52e386c9a28957ce38cf27f0777416c60d54c65e 100644 --- a/ecmascript/jspandafile/class_info_extractor.cpp +++ b/ecmascript/jspandafile/class_info_extractor.cpp @@ -185,7 +185,7 @@ JSHandle ClassInfoExtractor::CreatePrototypeHClass(JSThread *thread, c return JSHandle(globalConst->GetHandledClassPrototypeClass()); } JSHandle hclass; - if (LIKELY(length <= PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES)) { + if (LIKELY(length <= PropertyAttributes::MAX_FAST_PROPS_CAPACITY)) { JSMutableHandle key(thread, JSTaggedValue::Undefined()); JSHandle layout = factory->CreateLayoutInfo(length, MemSpaceType::OLD_SPACE, GrowMode::KEEP); for (uint32_t index = 0; index < length; ++index) { @@ -198,7 +198,7 @@ JSHandle ClassInfoExtractor::CreatePrototypeHClass(JSThread *thread, c } attributes.SetIsInlinedProps(true); - attributes.SetRepresentation(Representation::MIXED); + attributes.SetRepresentation(Representation::TAGGED); attributes.SetOffset(index); layout->AddKey(thread, index, key.GetTaggedValue(), attributes); } @@ -241,7 +241,7 @@ JSHandle ClassInfoExtractor::CreateConstructorHClass(JSThread *thread, } } JSHandle hclass; - if (LIKELY(length <= PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES)) { + if (LIKELY(length <= PropertyAttributes::MAX_FAST_PROPS_CAPACITY)) { JSMutableHandle key(thread, JSTaggedValue::Undefined()); JSHandle layout = factory->CreateLayoutInfo(length, MemSpaceType::OLD_SPACE, GrowMode::KEEP); for (uint32_t index = 0; index < length; ++index) { @@ -273,7 +273,7 @@ JSHandle ClassInfoExtractor::CreateConstructorHClass(JSThread *thread, } attributes.SetIsInlinedProps(true); - attributes.SetRepresentation(Representation::MIXED); + attributes.SetRepresentation(Representation::TAGGED); attributes.SetOffset(index); layout->AddKey(thread, index, key.GetTaggedValue(), attributes); } @@ -407,15 +407,16 @@ JSHandle ClassHelper::DefineClassFromExtractor(JSThread *thread, con const GlobalEnvConstants *globalConst = thread->GlobalConstants(); JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(prototype), globalConst->GetHandledConstructorString(), ctorDesc); - + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSFunction, thread); constructor->SetHomeObject(thread, prototype); constructor->SetProtoOrHClass(thread, prototype); - + if (thread->GetEcmaVM()->IsEnablePGOProfiler()) { + thread->GetEcmaVM()->GetPGOProfiler()->ProfileDefineClass(constructor.GetTaggedType()); + } return constructor; } JSHandle ClassHelper::DefineClassWithIHClass(JSThread *thread, - [[maybe_unused]] const JSHandle &base, JSHandle &extractor, const JSHandle &lexenv, const JSHandle &ihclass, @@ -467,7 +468,8 @@ JSHandle ClassHelper::DefineClassWithIHClass(JSThread *thread, // static uint32_t staticLength = staticProperties->GetLength(); - + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + int correntIndex = 0; if (LIKELY(!constructorHClass->IsDictionaryMode())) { for (uint32_t index = 0; index < staticLength; ++index) { propValue.Update(staticProperties->Get(index)); @@ -477,7 +479,13 @@ JSHandle ClassHelper::DefineClassWithIHClass(JSThread *thread, propFunc->SetLexicalEnv(thread, lexenv); propValue.Update(propFunc); } - JSHandle::Cast(constructor)->SetPropertyInlinedProps(thread, index, propValue.GetTaggedValue()); + bool needCorrentIndex = index >= ClassInfoExtractor::STATIC_RESERVED_LENGTH; + if (needCorrentIndex) { + key.Update(staticKeys->Get(index)); + correntIndex = JSHClass::FindPropertyEntry(thread, *constructorHClass, key.GetTaggedValue()); + } + JSHandle::Cast(constructor)->SetPropertyInlinedProps(thread, + needCorrentIndex ? static_cast(correntIndex) : index, propValue.GetTaggedValue()); } } else { JSHandle dict = BuildDictionaryProperties(thread, JSHandle(constructor), staticKeys, @@ -495,10 +503,13 @@ JSHandle ClassHelper::DefineClassWithIHClass(JSThread *thread, const GlobalEnvConstants *globalConst = thread->GlobalConstants(); JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle(prototype), globalConst->GetHandledConstructorString(), ctorDesc); - + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSFunction, thread); constructor->SetHomeObject(thread, prototype); constructor->SetProtoOrHClass(thread, ihclass); + if (thread->GetEcmaVM()->IsEnablePGOProfiler()) { + thread->GetEcmaVM()->GetPGOProfiler()->ProfileDefineClass(constructor.GetTaggedType()); + } return constructor; } @@ -510,7 +521,7 @@ JSHandle ClassHelper::BuildDictionaryProperties(JSThread *thread { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); uint32_t length = keys->GetLength(); - ASSERT(length > PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES); + ASSERT(length > PropertyAttributes::MAX_FAST_PROPS_CAPACITY); ASSERT(keys->GetLength() == properties->GetLength()); JSMutableHandle dict( diff --git a/ecmascript/jspandafile/class_info_extractor.h b/ecmascript/jspandafile/class_info_extractor.h index 2aed38c8817b11bd0c7d7fdcede08c7711a1501f..8428df920329eb55ea2174e3d2be32ddd2ad4821 100644 --- a/ecmascript/jspandafile/class_info_extractor.h +++ b/ecmascript/jspandafile/class_info_extractor.h @@ -98,7 +98,6 @@ public: const JSHandle &lexenv); static JSHandle DefineClassWithIHClass(JSThread *thread, - const JSHandle &base, JSHandle &extractor, const JSHandle &lexenv, const JSHandle &ihclass, diff --git a/ecmascript/jspandafile/debug_info_extractor.cpp b/ecmascript/jspandafile/debug_info_extractor.cpp index 01176395b919e319f945138c70b9283b148dfaa6..81138076c5cdbb2dbce15587e3fc668f070db6c0 100644 --- a/ecmascript/jspandafile/debug_info_extractor.cpp +++ b/ecmascript/jspandafile/debug_info_extractor.cpp @@ -55,6 +55,13 @@ public: void ProcessEnd() { + // When process ends, update any variableInfo + // with end_offset = 0, set it to the state address. + for (auto iter = lvt_.begin(); iter != lvt_.end(); iter++) { + if (iter->endOffset == 0) { + iter->endOffset = state_->GetAddress(); + } + } } bool HandleAdvanceLine(int32_t lineDiff) const @@ -93,21 +100,34 @@ public: bool HandleStartLocal(int32_t regNumber, uint32_t nameId, [[maybe_unused]] uint32_t typeId) { + // start_offset is the current state address, end_offset will temporarily be 0 here, + // then being updated inside the HandleEndLocal method. + uint32_t startOffset = state_->GetAddress(); + uint32_t endOffset = 0; const char *name = GetStringFromConstantPool(state_->GetPandaFile(), nameId); - lvt_.emplace(name, regNumber); + lvt_.push_back({name, regNumber, startOffset, endOffset}); return true; } bool HandleStartLocalExtended(int32_t regNumber, uint32_t nameId, [[maybe_unused]] uint32_t typeId, [[maybe_unused]] uint32_t typeSignatureId) { + uint32_t startOffset = state_->GetAddress(); + uint32_t endOffset = 0; const char *name = GetStringFromConstantPool(state_->GetPandaFile(), nameId); - lvt_.emplace(name, regNumber); + lvt_.push_back({name, regNumber, startOffset, endOffset}); return true; } bool HandleEndLocal([[maybe_unused]] int32_t regNumber) { + for (auto iter = lvt_.rbegin(); iter != lvt_.rend(); iter++) { + // reversely finds the variable and updates its end_offset to be state address + if (iter->regNumber == regNumber && iter->endOffset == 0) { + iter->endOffset = state_->GetAddress(); + break; + } + } return true; } diff --git a/ecmascript/jspandafile/debug_info_extractor.h b/ecmascript/jspandafile/debug_info_extractor.h index a205f880f5d19d3fc655b2945c1d387a4b64336b..c70ae26c3f0aa8adc8fde9940b9b54c9b34c09c8 100644 --- a/ecmascript/jspandafile/debug_info_extractor.h +++ b/ecmascript/jspandafile/debug_info_extractor.h @@ -56,7 +56,8 @@ using ColumnNumberTable = CVector; using JSPtLocation = tooling::JSPtLocation; /* - * LocalVariableInfo define in frontend, now only use name and regNumber: + * Full version of LocalVariableInfo is defined in frontend, + * here only using name, reg_number, start_offset, and end_offset: * std::string name * std::string type * std::string typeSignature @@ -64,7 +65,13 @@ using JSPtLocation = tooling::JSPtLocation; * uint32_t startOffset * uint32_t endOffset */ -using LocalVariableTable = CUnorderedMap; // name, regNumber +struct LocalVariableInfo { + std::string name; + int32_t regNumber; + uint32_t startOffset; + uint32_t endOffset; +}; +using LocalVariableTable = CVector; // public for debugger class PUBLIC_API DebugInfoExtractor { @@ -111,6 +118,11 @@ public: methodIds.push_back(mda.GetMethodId()); }); + int32_t minColumn = INT32_MAX; + uint32_t currentOffset = UINT32_MAX; + uint32_t minColumnOffset = UINT32_MAX; + EntityId currentMethodId; + EntityId minColumnMethodId; for (auto &methodId : methodIds) { const std::string &sourceFile = GetSourceFile(methodId); // the url for testcases is empty @@ -123,16 +135,28 @@ public: if (lineTable[j].line != line) { continue; } - uint32_t currentOffset = lineTable[j].offset; + currentMethodId = methodId; + currentOffset = lineTable[j].offset; uint32_t nextOffset = ((j == lineTable.size() - 1) ? UINT32_MAX : lineTable[j + 1].offset); for (const auto &pair : columnTable) { - if (pair.column == column && pair.offset >= currentOffset && pair.offset < nextOffset) { - return cb(JSPtLocation(jsPandaFile_, methodId, pair.offset, url)); + if (pair.offset >= currentOffset && pair.offset < nextOffset) { + if (pair.column == column) { + return cb(JSPtLocation(jsPandaFile_, methodId, pair.offset, url)); + } else if (pair.column < minColumn) { + minColumn = pair.column; + minColumnOffset = currentOffset; + minColumnMethodId = currentMethodId; + } } } - return cb(JSPtLocation(jsPandaFile_, methodId, currentOffset, url)); } } + if (minColumn != INT32_MAX) { // find the smallest column for the corresponding row + return cb(JSPtLocation(jsPandaFile_, minColumnMethodId, minColumnOffset, url)); + } + if (currentOffset != UINT32_MAX) { // find corresponding row, but not find corresponding column + return cb(JSPtLocation(jsPandaFile_, currentMethodId, currentOffset, url)); + } } return false; } @@ -161,25 +185,33 @@ public: return cb(column); } -#if defined(ECMASCRIPT_SUPPORT_CPUPROFILER) || defined(ECMASCRIPT_SUPPORT_HEAPSAMPLING) int32_t GetFristLine(panda_file::File::EntityId methodId) { const LineNumberTable &lineTable = GetLineNumberTable(methodId); - if (lineTable.size() <= 1) { + auto tableSize = lineTable.size(); + if (tableSize == 0) { return 0; } - return lineTable[1].line + 1; + if (tableSize == 1) { + return lineTable[0].line + 1; + } + int firstLineIndex = ((lineTable[0].line == SPECIAL_LINE_MARK) ? 1 : 0); + return lineTable[firstLineIndex].line + 1; } int32_t GetFristColumn(panda_file::File::EntityId methodId) { const ColumnNumberTable &columnTable = GetColumnNumberTable(methodId); - if (columnTable.size() == 0) { + auto tableSize = columnTable.size(); + if (tableSize == 0) { return 0; } - return columnTable[0].column + 1; + if (tableSize == 1) { + return columnTable[0].column + 1; + } + int firstColumnIndex = ((columnTable[0].column == SPECIAL_LINE_MARK) ? 1 : 0); + return columnTable[firstColumnIndex].column + 1; } -#endif void Extract(); diff --git a/ecmascript/jspandafile/js_pandafile.cpp b/ecmascript/jspandafile/js_pandafile.cpp index 35b07aaaf970af48ca628478fb8d681d70a6a3a4..e33b3029d52b72a6c76edae6e8912980c56bba4f 100644 --- a/ecmascript/jspandafile/js_pandafile.cpp +++ b/ecmascript/jspandafile/js_pandafile.cpp @@ -20,6 +20,9 @@ #include "libpandafile/class_data_accessor-inl.h" namespace panda::ecmascript { +namespace { +const CString OHOS_PKG_ABC_PATH_ROOT = "/ets/"; // abc file always under /ets/ dir in HAP/HSP +} // namespace bool JSPandaFile::loadedFirstPandaFile = false; JSPandaFile::JSPandaFile(const panda_file::File *pf, const CString &descriptor) : pf_(pf), desc_(descriptor) @@ -219,62 +222,24 @@ bool JSPandaFile::IsFirstMergedAbc() const return false; } -bool JSPandaFile::IsModule(JSThread *thread, const CString &recordName, CString fullRecordName) const +bool JSPandaFile::CheckAndGetRecordInfo(const CString &recordName, JSRecordInfo &recordInfo) const { if (IsBundlePack()) { - return jsRecordInfo_.begin()->second.moduleRecordIdx != -1; - } - auto info = jsRecordInfo_.find(recordName); - if (info != jsRecordInfo_.end()) { - return info->second.moduleRecordIdx != -1; - } - if (fullRecordName.empty()) { - fullRecordName = recordName; - } - CString msg = "cannot find record '" + fullRecordName + "', please check the request path."; - THROW_REFERENCE_ERROR_AND_RETURN(thread, msg.c_str(), false); -} - -bool JSPandaFile::IsCjs(JSThread *thread, const CString &recordName) const -{ - if (IsBundlePack()) { - return jsRecordInfo_.begin()->second.isCjs; - } - auto info = jsRecordInfo_.find(recordName); - if (info != jsRecordInfo_.end()) { - return info->second.isCjs; - } - CString msg = "cannot find record '" + recordName + "', please check the request path."; - THROW_REFERENCE_ERROR_AND_RETURN(thread, msg.c_str(), false); -} - -bool JSPandaFile::IsJson(JSThread *thread, const CString &recordName) const -{ - if (IsBundlePack()) { - return jsRecordInfo_.begin()->second.isJson; + recordInfo = jsRecordInfo_.begin()->second; + return true; } auto info = jsRecordInfo_.find(recordName); if (info != jsRecordInfo_.end()) { - return info->second.isJson; + recordInfo = info->second; + return true; } - CString msg = "cannot find record '" + recordName + "', please check the request path."; - THROW_REFERENCE_ERROR_AND_RETURN(thread, msg.c_str(), false); + return false; } -CString JSPandaFile::GetJsonStringId(JSThread *thread, const CString &recordName) const +CString JSPandaFile::GetJsonStringId(const JSRecordInfo &jsRecordInfo) const { - if (IsBundlePack()) { - StringData sd = GetStringData(EntityId(jsRecordInfo_.begin()->second.jsonStringId)); - return utf::Mutf8AsCString(sd.data); - } - - auto info = jsRecordInfo_.find(recordName); - if (info != jsRecordInfo_.end()) { - StringData sd = GetStringData(EntityId(info->second.jsonStringId)); - return utf::Mutf8AsCString(sd.data); - } - CString msg = "cannot find record '" + recordName + "', please check the request path."; - THROW_REFERENCE_ERROR_AND_RETURN(thread, msg.c_str(), ""); + StringData sd = GetStringData(EntityId(jsRecordInfo.jsonStringId)); + return utf::Mutf8AsCString(sd.data); } CString JSPandaFile::GetEntryPoint(const CString &recordName) const @@ -376,4 +341,36 @@ FunctionKind JSPandaFile::GetFunctionKind(ConstPoolType type) } return kind; } + +/* + handle desc like: + case1: /data/storage/el1/bundle/entry/ets/modules.abc -> entry/ets/modules.abc + case2: /data/storage/el1/bundle/entry/ets/widgets.abc -> entry/ets/widgets.abc + case3: /data/app/el1/bundle/public/com.xx.xx/entry/ets/modules.abc -> entry/ets/modules.abc + case4: /data/app/el1/bundle/public/com.xx.xx/entry/ets/widgets.abc -> entry/ets/widgets.abc +*/ +CString JSPandaFile::GetNormalizedFileDesc(const CString &desc) +{ + // file not in OHOS package. + if (desc.rfind('/', 0) != 0) { + return desc; + } + auto etsTokenPos = desc.rfind(OHOS_PKG_ABC_PATH_ROOT); + if (etsTokenPos == std::string::npos) { + // file not in OHOS package. + return desc; + } + auto ohosModulePos = desc.rfind('/', etsTokenPos - 1); + if (ohosModulePos == std::string::npos) { + LOG_ECMA(ERROR) << "Get abcPath from desc failed. desc: " << desc; + return desc; + } + // substring likes {ohosModuleName}/ets/modules.abc or {ohosModuleName}/ets/widgets.abc + return desc.substr(ohosModulePos + 1); +} + +CString JSPandaFile::GetNormalizedFileDesc() const +{ + return GetNormalizedFileDesc(desc_); +} } // namespace panda::ecmascript diff --git a/ecmascript/jspandafile/js_pandafile.h b/ecmascript/jspandafile/js_pandafile.h index 73aad318cbab59a38f2f61ded866185ab78a6a7a..e354fcfbc8422eb77a04be9b5f0f590cbe9732a6 100644 --- a/ecmascript/jspandafile/js_pandafile.h +++ b/ecmascript/jspandafile/js_pandafile.h @@ -77,6 +77,7 @@ public: static constexpr char BUNDLE_INSTALL_PATH[] = "/data/storage/el1/bundle/"; static constexpr int PACKAGE_NAME_LEN = 8; static constexpr int TYPE_SUMMARY_OFFSET_NOT_FOUND = 0; + static constexpr int32_t PF_OFFSET = 0; JSPandaFile(const panda_file::File *pf, const CString &descriptor); ~JSPandaFile(); @@ -86,6 +87,10 @@ public: return desc_; } + CString GetNormalizedFileDesc() const; + + static CString GetNormalizedFileDesc(const CString &desc); + uint32_t GetChecksum() const { return checksum_; @@ -241,14 +246,24 @@ public: return pf_->GetHeader()->file_size; } - bool PUBLIC_API IsModule(JSThread *thread, const CString &recordName = ENTRY_FUNCTION_NAME, - CString fullRecordName = "") const; + bool CheckAndGetRecordInfo(const CString &recordName, JSRecordInfo &recordInfo) const; - bool IsCjs(JSThread *thread, const CString &recordName = ENTRY_FUNCTION_NAME) const; + CString GetJsonStringId(const JSRecordInfo &jsRecordInfo) const; - bool IsJson(JSThread *thread, const CString &recordName = ENTRY_FUNCTION_NAME) const; + bool PUBLIC_API IsModule(const JSRecordInfo &jsRecordInfo) const + { + return jsRecordInfo.moduleRecordIdx != -1; + } + + bool IsCjs(const JSRecordInfo &jsRecordInfo) const + { + return jsRecordInfo.isCjs; + } - CString GetJsonStringId(JSThread *thread, const CString &recordName = ENTRY_FUNCTION_NAME) const; + bool IsJson(const JSRecordInfo &jsRecordInfo) const + { + return jsRecordInfo.isJson; + } bool IsBundlePack() const { @@ -334,6 +349,11 @@ public: return false; } + bool HasTSTypes(const JSRecordInfo &recordInfo) const + { + return recordInfo.hasTSTypes; + } + uint32_t GetTypeSummaryOffset(const CString &recordName) const { auto it = jsRecordInfo_.find(recordName); @@ -370,13 +390,13 @@ private: static constexpr size_t VERSION_SIZE = 4; static constexpr std::array OLD_VERSION {0, 0, 0, 2}; + const panda_file::File *pf_ {nullptr}; uint32_t constpoolIndex_ {0}; uint32_t checksum_ {0}; CUnorderedMap methodLiteralMap_; CUnorderedMap constpoolMap_; uint32_t numMethods_ {0}; MethodLiteral *methodLiterals_ {nullptr}; - const panda_file::File *pf_ {nullptr}; CString desc_; uint32_t anFileInfoIndex_ {INVALID_INDEX}; bool isNewVersion_ {false}; diff --git a/ecmascript/jspandafile/js_pandafile_executor.cpp b/ecmascript/jspandafile/js_pandafile_executor.cpp index fb3dc1cac6c5262df10ee61d202b4b5b1e20b1eb..e695b38d112c0b497ddb6c934e5feafecd96489f 100644 --- a/ecmascript/jspandafile/js_pandafile_executor.cpp +++ b/ecmascript/jspandafile/js_pandafile_executor.cpp @@ -24,6 +24,7 @@ #include "ecmascript/mem/c_string.h" #include "ecmascript/mem/c_containers.h" #include "ecmascript/module/js_module_manager.h" +#include "ecmascript/module/module_path_helper.h" #include "ecmascript/patch/quick_fix_manager.h" namespace panda::ecmascript { @@ -32,16 +33,17 @@ Expected JSPandaFileExecutor::ExecuteFromFile(JSThread *thr std::string_view entryPoint, bool needUpdate, bool excuteFromJob) { LOG_ECMA(DEBUG) << "JSPandaFileExecutor::ExecuteFromFile filename " << filename; + ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "JSPandaFileExecutor::ExecuteFromFile"); CString entry; CString name; - CString normalName = PathHelper::NormalizePath(filename); EcmaVM *vm = thread->GetEcmaVM(); if (!vm->IsBundlePack() && !excuteFromJob) { #if defined(PANDA_TARGET_LINUX) || defined(OHOS_UNIT_TEST) name = filename; entry = entryPoint.data(); #else - entry = PathHelper::ParseOhmUrl(vm, normalName, name); + CString normalName = PathHelper::NormalizePath(filename); + ModulePathHelper::ParseOhmUrl(vm, normalName, name, entry); #if !defined(PANDA_TARGET_WINDOWS) && !defined(PANDA_TARGET_MACOS) if (name.empty()) { name = vm->GetAssetPath(); @@ -73,25 +75,27 @@ Expected JSPandaFileExecutor::ExecuteFromFile(JSThread *thr if (!jsPandaFile->IsBundlePack() && !excuteFromJob && !vm->GetBundleName().empty()) { jsPandaFile->CheckIsRecordWithBundleName(entry); if (!jsPandaFile->IsRecordWithBundleName()) { - PathHelper::CroppingRecord(entry); + PathHelper::AdaptOldIsaRecord(entry); } } - bool isModule = jsPandaFile->IsModule(thread, entry, realEntry); - if (thread->HasPendingException()) { - thread->GetCurrentEcmaContext()->HandleUncaughtException(thread->GetException()); - return Unexpected(false); + JSRecordInfo recordInfo; + bool hasRecord = jsPandaFile->CheckAndGetRecordInfo(entry, recordInfo); + if (!hasRecord) { + LOG_FULL(ERROR) << "cannot find record '" << realEntry <<"' in baseFileName " << name << "."; + CString msg = "cannot find record '" + realEntry + "', please check the request path."; + THROW_REFERENCE_ERROR_AND_RETURN(thread, msg.c_str(), Unexpected(false)); } - if (isModule) { + if (jsPandaFile->IsModule(recordInfo)) { [[maybe_unused]] EcmaHandleScope scope(thread); ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager(); JSHandle moduleRecord(thread->GlobalConstants()->GetHandledUndefined()); if (jsPandaFile->IsBundlePack()) { - moduleRecord = moduleManager->HostResolveImportedModule(name); + moduleRecord = moduleManager->HostResolveImportedModule(name, excuteFromJob); } else { - moduleRecord = moduleManager->HostResolveImportedModuleWithMerge(name, entry); + moduleRecord = moduleManager->HostResolveImportedModuleWithMerge(name, entry, excuteFromJob); } - SourceTextModule::Instantiate(thread, moduleRecord); + SourceTextModule::Instantiate(thread, moduleRecord, excuteFromJob); if (thread->HasPendingException()) { if (!excuteFromJob) { thread->GetCurrentEcmaContext()->HandleUncaughtException(thread->GetException()); @@ -111,6 +115,7 @@ Expected JSPandaFileExecutor::ExecuteFromBuffer(JSThread *t const void *buffer, size_t size, std::string_view entryPoint, const CString &filename, bool needUpdate) { LOG_ECMA(DEBUG) << "JSPandaFileExecutor::ExecuteFromBuffer filename " << filename; + ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "JSPandaFileExecutor::ExecuteFromBuffer"); CString normalName = PathHelper::NormalizePath(filename); std::shared_ptr jsPandaFile = JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, normalName, entryPoint, buffer, size, needUpdate); @@ -122,8 +127,14 @@ Expected JSPandaFileExecutor::ExecuteFromBuffer(JSThread *t LoadAOTFilesForFile(vm, jsPandaFile.get()); CString entry = entryPoint.data(); - bool isModule = jsPandaFile->IsModule(thread, entry); - if (isModule) { + JSRecordInfo recordInfo; + bool hasRecord = jsPandaFile->CheckAndGetRecordInfo(entry, recordInfo); + if (!hasRecord) { + LOG_FULL(ERROR) << "cannot find record '" << entry <<"' in baseFileName " << normalName << "."; + CString msg = "cannot find record '" + entry + "', please check the request path."; + THROW_REFERENCE_ERROR_AND_RETURN(thread, msg.c_str(), Unexpected(false)); + } + if (jsPandaFile->IsModule(recordInfo)) { bool isBundle = jsPandaFile->IsBundlePack(); return CommonExecuteBuffer(thread, isBundle, normalName, entry, buffer, size); } @@ -135,6 +146,7 @@ Expected JSPandaFileExecutor::ExecuteModuleBuffer( JSThread *thread, const void *buffer, size_t size, const CString &filename, bool needUpdate) { LOG_ECMA(DEBUG) << "JSPandaFileExecutor::ExecuteModuleBuffer filename " << filename; + ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "JSPandaFileExecutor::ExecuteModuleBuffer"); CString name; EcmaVM *vm = thread->GetEcmaVM(); #if !defined(PANDA_TARGET_WINDOWS) && !defined(PANDA_TARGET_MACOS) @@ -147,7 +159,8 @@ Expected JSPandaFileExecutor::ExecuteModuleBuffer( name = assetPath + "/" + JSPandaFile::MERGE_ABC_NAME; #endif CString normalName = PathHelper::NormalizePath(filename); - CString entry = PathHelper::ParseOhmUrl(vm, normalName, name); + CString entry; + ModulePathHelper::ParseOhmUrl(vm, normalName, name, entry); std::shared_ptr jsPandaFile = JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, name, entry, buffer, size, needUpdate); if (jsPandaFile == nullptr) { @@ -163,16 +176,19 @@ Expected JSPandaFileExecutor::ExecuteModuleBuffer( if (!isBundle) { jsPandaFile->CheckIsRecordWithBundleName(entry); if (!jsPandaFile->IsRecordWithBundleName()) { - PathHelper::CroppingRecord(entry); + PathHelper::AdaptOldIsaRecord(entry); } } - // will be refactored, temporarily use the function IsModule to verify realEntry - [[maybe_unused]] bool isModule = jsPandaFile->IsModule(thread, entry, realEntry); - if (thread->HasPendingException()) { - thread->GetCurrentEcmaContext()->HandleUncaughtException(thread->GetException()); - return Unexpected(false); + JSRecordInfo recordInfo; + bool hasRecord = jsPandaFile->CheckAndGetRecordInfo(entry, recordInfo); + if (!hasRecord) { + LOG_FULL(ERROR) << "cannot find record '" << realEntry <<"' in baseFileName " << name << "."; + CString msg = "cannot find record '" + realEntry + "', please check the request path."; + THROW_REFERENCE_ERROR_AND_RETURN(thread, msg.c_str(), Unexpected(false)); + } + if (!jsPandaFile->IsModule(recordInfo)) { + LOG_ECMA(FATAL) << "Input file is not esmodule"; } - ASSERT(isModule); return CommonExecuteBuffer(thread, isBundle, name, entry, buffer, size); } @@ -235,6 +251,7 @@ Expected JSPandaFileExecutor::ExecuteFromBufferSecure(JSThr size_t size, std::string_view entryPoint, const CString &filename, bool needUpdate) { LOG_ECMA(DEBUG) << "JSPandaFileExecutor::ExecuteFromBufferSecure with secure buffer filename " << filename; + ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "JSPandaFileExecutor::ExecuteFromBufferSecure"); CString normalName = PathHelper::NormalizePath(filename); std::shared_ptr jsPandaFile = JSPandaFileManager::GetInstance()-> LoadJSPandaFileSecure(thread, normalName, entryPoint, buffer, size, needUpdate); @@ -246,8 +263,14 @@ Expected JSPandaFileExecutor::ExecuteFromBufferSecure(JSThr LoadAOTFilesForFile(vm, jsPandaFile.get()); CString entry = entryPoint.data(); - bool isModule = jsPandaFile->IsModule(thread, entry); - if (isModule) { + JSRecordInfo recordInfo; + bool hasRecord = jsPandaFile->CheckAndGetRecordInfo(entry, recordInfo); + if (!hasRecord) { + LOG_FULL(ERROR) << "cannot find record '" << entry <<"' in baseFileName " << normalName << "."; + CString msg = "cannot find record '" + entry + "', please check the request path."; + THROW_REFERENCE_ERROR_AND_RETURN(thread, msg.c_str(), Unexpected(false)); + } + if (jsPandaFile->IsModule(recordInfo)) { return CommonExecuteBuffer(thread, normalName, entry, jsPandaFile.get()); } return JSPandaFileExecutor::Execute(thread, jsPandaFile.get(), entry); @@ -282,6 +305,7 @@ Expected JSPandaFileExecutor::ExecuteModuleBufferSecure(JST size_t size, const CString &filename, bool needUpdate) { LOG_ECMA(DEBUG) << "JSPandaFileExecutor::ExecuteModuleBufferSecure with secure buffer filename " << filename; + ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "JSPandaFileExecutor::ExecuteModuleBufferSecure"); CString name; EcmaVM *vm = thread->GetEcmaVM(); #if !defined(PANDA_TARGET_WINDOWS) && !defined(PANDA_TARGET_MACOS) @@ -294,7 +318,8 @@ Expected JSPandaFileExecutor::ExecuteModuleBufferSecure(JST name = assetPath + "/" + JSPandaFile::MERGE_ABC_NAME; #endif CString normalName = PathHelper::NormalizePath(filename); - CString entry = PathHelper::ParseOhmUrl(vm, normalName, name); + CString entry; + ModulePathHelper::ParseOhmUrl(vm, normalName, name, entry); std::shared_ptr jsPandaFile = JSPandaFileManager::GetInstance()-> LoadJSPandaFileSecure(thread, name, entry, buffer, size, needUpdate); if (jsPandaFile == nullptr) { @@ -308,17 +333,21 @@ Expected JSPandaFileExecutor::ExecuteModuleBufferSecure(JST if (!jsPandaFile->IsBundlePack()) { jsPandaFile->CheckIsRecordWithBundleName(entry); if (!jsPandaFile->IsRecordWithBundleName()) { - PathHelper::CroppingRecord(entry); + PathHelper::AdaptOldIsaRecord(entry); } } // will be refactored, temporarily use the function IsModule to verify realEntry - [[maybe_unused]] bool isModule = jsPandaFile->IsModule(thread, entry, realEntry); - if (thread->HasPendingException()) { - thread->GetCurrentEcmaContext()->HandleUncaughtException(thread->GetException()); - return Unexpected(false); + JSRecordInfo recordInfo; + bool hasRecord = jsPandaFile->CheckAndGetRecordInfo(entry, recordInfo); + if (!hasRecord) { + LOG_FULL(ERROR) << "cannot find record '" << realEntry <<"' in baseFileName " << name << "."; + CString msg = "cannot find record '" + realEntry + "', please check the request path."; + THROW_REFERENCE_ERROR_AND_RETURN(thread, msg.c_str(), Unexpected(false)); + } + if (!jsPandaFile->IsModule(recordInfo)) { + LOG_ECMA(FATAL) << "Input file is not esmodule"; } - ASSERT(isModule); return CommonExecuteBuffer(thread, name, entry, jsPandaFile.get()); } } // namespace panda::ecmascript diff --git a/ecmascript/jspandafile/js_pandafile_manager.cpp b/ecmascript/jspandafile/js_pandafile_manager.cpp index c2776fe5e128ad55c1f077c3199dee776c57eed8..62ca05e5d2ee481bd1afafd49778b0f91e4703e5 100644 --- a/ecmascript/jspandafile/js_pandafile_manager.cpp +++ b/ecmascript/jspandafile/js_pandafile_manager.cpp @@ -15,15 +15,17 @@ #include "ecmascript/jspandafile/js_pandafile_manager.h" -#include "ecmascript/base/path_helper.h" #include "ecmascript/compiler/aot_file/an_file_data_manager.h" #include "ecmascript/compiler/aot_file/aot_file_manager.h" #include "ecmascript/js_file_path.h" #include "ecmascript/jspandafile/program_object.h" +#include "ecmascript/module/module_path_helper.h" #include "ecmascript/pgo_profiler/pgo_profiler_manager.h" #include "file.h" +#include "jsnapi.h" namespace panda::ecmascript { +using PGOProfilerManager = pgo::PGOProfilerManager; static const size_t MALLOC_SIZE_LIMIT = 2147483648; // Max internal memory used by the VM declared in options JSPandaFileManager *JSPandaFileManager::GetInstance() @@ -34,7 +36,7 @@ JSPandaFileManager *JSPandaFileManager::GetInstance() JSPandaFileManager::~JSPandaFileManager() { - os::memory::LockHolder lock(jsPandaFileLock_); + LockHolder lock(jsPandaFileLock_); extractors_.clear(); oldJSPandaFiles_.clear(); loadedJSPandaFiles_.clear(); @@ -44,7 +46,7 @@ std::shared_ptr JSPandaFileManager::LoadJSPandaFile(JSThread *threa std::string_view entryPoint, bool needUpdate) { { - os::memory::LockHolder lock(jsPandaFileLock_); + LockHolder lock(jsPandaFileLock_); std::shared_ptr jsPandaFile; if (needUpdate) { auto pf = panda_file::OpenPandaFileOrZip(filename, panda_file::File::READ_WRITE); @@ -58,30 +60,40 @@ std::shared_ptr JSPandaFileManager::LoadJSPandaFile(JSThread *threa } if (jsPandaFile != nullptr) { InsertJSPandaFileVmUnlocked(thread->GetEcmaVM(), jsPandaFile); -#if defined(ECMASCRIPT_SUPPORT_CPUPROFILER) - if (thread->GetIsProfiling()) { - GetJSPtExtractorAndExtract(jsPandaFile.get()); - } -#endif return jsPandaFile; } } EcmaVM *vm = thread->GetEcmaVM(); - bool mode = thread->GetCurrentEcmaContext()->GetModuleManager()->GetCurrentMode(); + ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager(); std::unique_ptr pf; - if (!vm->IsBundlePack() && mode) { + if (!vm->IsBundlePack() && moduleManager->GetExecuteMode()) { ResolveBufferCallback resolveBufferCallback = vm->GetResolveBufferCallback(); if (resolveBufferCallback == nullptr) { LOG_ECMA(ERROR) << "resolveBufferCallback is nullptr"; +#if defined(PANDA_TARGET_WINDOWS) || defined(PANDA_TARGET_MACOS) + if (vm->EnableReportModuleResolvingFailure()) { + LOG_NO_TAG(ERROR) << "[ArkRuntime Log] Importing shared package is not supported in the Previewer."; + } +#endif return nullptr; } - std::vector data = resolveBufferCallback(base::PathHelper::ParseHapPath(filename)); - if (data.empty()) { + uint8_t *data = nullptr; + size_t dataSize = 0; + bool getBuffer = resolveBufferCallback(ModulePathHelper::ParseHapPath(filename), &data, &dataSize); + if (!getBuffer) { LOG_ECMA(ERROR) << "resolveBufferCallback get buffer failed"; return nullptr; } - pf = panda_file::OpenPandaFileFromMemory(data.data(), data.size()); + if (!JSNApi::CheckSecureMem(reinterpret_cast(data))) { + LOG_ECMA(ERROR) << "Hsp secure memory check failed, please execute in secure memory."; + return nullptr; + } +#if defined(PANDA_TARGET_ANDROID) || defined(PANDA_TARGET_IOS) + pf = panda_file::OpenPandaFileFromMemory(data, dataSize); +#else + pf = panda_file::OpenPandaFileFromSecureMemory(data, dataSize); +#endif } else { pf = panda_file::OpenPandaFileOrZip(filename, panda_file::File::READ_WRITE); } @@ -105,10 +117,11 @@ std::shared_ptr JSPandaFileManager::LoadJSPandaFile(JSThread *threa std::string_view entryPoint, const void *buffer, size_t size, bool needUpdate) { if (buffer == nullptr || size == 0) { + LOG_FULL(ERROR) << "Input buffer is empty"; return nullptr; } { - os::memory::LockHolder lock(jsPandaFileLock_); + LockHolder lock(jsPandaFileLock_); std::shared_ptr jsPandaFile; if (needUpdate) { auto pf = panda_file::OpenPandaFileFromMemory(buffer, size); @@ -122,11 +135,6 @@ std::shared_ptr JSPandaFileManager::LoadJSPandaFile(JSThread *threa } if (jsPandaFile != nullptr) { InsertJSPandaFileVmUnlocked(thread->GetEcmaVM(), jsPandaFile); -#if defined(ECMASCRIPT_SUPPORT_CPUPROFILER) - if (thread->GetIsProfiling()) { - GetJSPtExtractorAndExtract(jsPandaFile.get()); - } -#endif return jsPandaFile; } } @@ -153,10 +161,11 @@ std::shared_ptr JSPandaFileManager::LoadJSPandaFileSecure(JSThread std::string_view entryPoint, uint8_t *buffer, size_t size, bool needUpdate) { if (buffer == nullptr || size == 0) { + LOG_FULL(ERROR) << "Input buffer is empty"; return nullptr; } { - os::memory::LockHolder lock(jsPandaFileLock_); + LockHolder lock(jsPandaFileLock_); std::shared_ptr jsPandaFile; if (needUpdate) { auto pf = panda_file::OpenPandaFileFromSecureMemory(buffer, size); @@ -170,11 +179,6 @@ std::shared_ptr JSPandaFileManager::LoadJSPandaFileSecure(JSThread } if (jsPandaFile != nullptr) { InsertJSPandaFileVmUnlocked(thread->GetEcmaVM(), jsPandaFile); -#if defined(ECMASCRIPT_SUPPORT_CPUPROFILER) - if (thread->GetIsProfiling()) { - GetJSPtExtractorAndExtract(jsPandaFile.get()); - } -#endif return jsPandaFile; } } @@ -201,10 +205,6 @@ JSHandle JSPandaFileManager::GenerateProgram(EcmaVM *vm, const JSPandaF std::string_view entryPoint) { ASSERT(GetJSPandaFile(jsPandaFile->GetPandaFile()) != nullptr); - if (AnFileDataManager::GetInstance()->IsEnable()) { - vm->GetJSThread()->GetCurrentEcmaContext()->GetAOTFileManager()->LoadAiFile(jsPandaFile); - } - return PandaFileTranslator::GenerateProgram(vm, jsPandaFile, entryPoint); } @@ -226,7 +226,7 @@ std::shared_ptr JSPandaFileManager::FindJSPandaFileWithChecksum(con std::shared_ptr JSPandaFileManager::FindMergedJSPandaFile() { - os::memory::LockHolder lock(jsPandaFileLock_); + LockHolder lock(jsPandaFileLock_); for (const auto &iter : loadedJSPandaFiles_) { const std::shared_ptr &jsPandafile = iter.second.first; if (jsPandafile->IsFirstMergedAbc()) { @@ -250,13 +250,13 @@ std::shared_ptr JSPandaFileManager::FindJSPandaFileUnlocked(const C std::shared_ptr JSPandaFileManager::FindJSPandaFile(const CString &filename) { - os::memory::LockHolder lock(jsPandaFileLock_); + LockHolder lock(jsPandaFileLock_); return FindJSPandaFileUnlocked(filename); } std::shared_ptr JSPandaFileManager::GetJSPandaFile(const panda_file::File *pf) { - os::memory::LockHolder lock(jsPandaFileLock_); + LockHolder lock(jsPandaFileLock_); for (const auto &iter : loadedJSPandaFiles_) { const std::shared_ptr &jsPandafile = iter.second.first; if (jsPandafile->GetPandaFile() == pf) { @@ -269,7 +269,7 @@ std::shared_ptr JSPandaFileManager::GetJSPandaFile(const panda_file void JSPandaFileManager::AddJSPandaFileVm(const EcmaVM *vm, const std::shared_ptr &jsPandaFile) { const auto &filename = jsPandaFile->GetJSPandaFileDesc(); - os::memory::LockHolder lock(jsPandaFileLock_); + LockHolder lock(jsPandaFileLock_); if (loadedJSPandaFiles_.find(filename) != loadedJSPandaFiles_.end()) { LOG_ECMA(FATAL) << "add failed, file already exist: " << filename; UNREACHABLE(); @@ -301,7 +301,7 @@ void JSPandaFileManager::RemoveJSPandaFileVm(const EcmaVM *vm, const JSPandaFile return; } - os::memory::LockHolder lock(jsPandaFileLock_); + LockHolder lock(jsPandaFileLock_); auto iterOld = oldJSPandaFiles_.begin(); while (iterOld != oldJSPandaFiles_.end()) { if (iterOld->first.get() == jsPandaFile) { @@ -313,6 +313,7 @@ void JSPandaFileManager::RemoveJSPandaFileVm(const EcmaVM *vm, const JSPandaFile } return; } + iterOld++; } const auto &filename = jsPandaFile->GetJSPandaFileDesc(); auto iter = loadedJSPandaFiles_.find(filename); @@ -369,7 +370,8 @@ std::shared_ptr JSPandaFileManager::OpenJSPandaFileFromBuffer(uint8 std::shared_ptr JSPandaFileManager::NewJSPandaFile(const panda_file::File *pf, const CString &desc) { std::shared_ptr jsPandaFile = std::make_shared(pf, desc); - PGOProfilerManager::GetInstance()->SamplePandaFileInfo(jsPandaFile->GetChecksum()); + PGOProfilerManager::GetInstance()->SamplePandaFileInfo(jsPandaFile->GetChecksum(), + jsPandaFile->GetJSPandaFileDesc()); return jsPandaFile; } @@ -377,7 +379,7 @@ DebugInfoExtractor *JSPandaFileManager::GetJSPtExtractor(const JSPandaFile *jsPa { LOG_ECMA_IF(jsPandaFile == nullptr, FATAL) << "GetJSPtExtractor error, js pandafile is nullptr"; - os::memory::LockHolder lock(jsPandaFileLock_); + LockHolder lock(jsPandaFileLock_); const auto &filename = jsPandaFile->GetJSPandaFileDesc(); if (loadedJSPandaFiles_.find(filename) == loadedJSPandaFiles_.end()) { LOG_ECMA(FATAL) << "get extractor failed, file not exist: " << filename; @@ -399,7 +401,7 @@ DebugInfoExtractor *JSPandaFileManager::GetJSPtExtractorAndExtract(const JSPanda { LOG_ECMA_IF(jsPandaFile == nullptr, FATAL) << "GetJSPtExtractor error, js pandafile is nullptr"; - os::memory::LockHolder lock(jsPandaFileLock_); + LockHolder lock(jsPandaFileLock_); const auto &filename = jsPandaFile->GetJSPandaFileDesc(); if (loadedJSPandaFiles_.find(filename) == loadedJSPandaFiles_.end()) { LOG_ECMA(FATAL) << "get extractor failed, file not exist: " << filename; @@ -415,24 +417,32 @@ DebugInfoExtractor *JSPandaFileManager::GetJSPtExtractorAndExtract(const JSPanda return extractor; } - DebugInfoExtractor *extractor = iter->second.get(); - extractor->Extract(); - return extractor; + return iter->second.get(); } DebugInfoExtractor *JSPandaFileManager::CpuProfilerGetJSPtExtractor(const JSPandaFile *jsPandaFile) { - auto const &filename = jsPandaFile->GetJSPandaFileDesc(); + LOG_ECMA_IF(jsPandaFile == nullptr, FATAL) << "GetJSPtExtractor error, js pandafile is nullptr"; + + LockHolder lock(jsPandaFileLock_); + const auto &filename = jsPandaFile->GetJSPandaFileDesc(); if (loadedJSPandaFiles_.find(filename) == loadedJSPandaFiles_.end()) { - return nullptr; + LOG_ECMA(FATAL) << "get extractor failed, file not exist: " << filename; + UNREACHABLE(); } + DebugInfoExtractor *extractor = nullptr; auto iter = extractors_.find(jsPandaFile); if (iter == extractors_.end()) { - return nullptr; + auto extractorPtr = std::make_unique(jsPandaFile); + extractor = extractorPtr.get(); + extractors_[jsPandaFile] = std::move(extractorPtr); + } else { + extractor = iter->second.get(); } - return iter->second.get(); + extractor->Extract(); + return extractor; } std::shared_ptr JSPandaFileManager::GenerateJSPandaFile(JSThread *thread, const panda_file::File *pf, @@ -457,7 +467,7 @@ std::shared_ptr JSPandaFileManager::GenerateJSPandaFile(JSThread *t { // For worker, JSPandaFile may be created by another vm. - os::memory::LockHolder lock(jsPandaFileLock_); + LockHolder lock(jsPandaFileLock_); std::shared_ptr jsPandaFile = FindJSPandaFileUnlocked(desc); if (jsPandaFile != nullptr) { InsertJSPandaFileVmUnlocked(vm, jsPandaFile); diff --git a/ecmascript/jspandafile/js_pandafile_manager.h b/ecmascript/jspandafile/js_pandafile_manager.h index 77cf2e414a1c424a5220496abeb421791a9d0e02..83488e9b6decbb28338498fb6df9c2e8c5f0333a 100644 --- a/ecmascript/jspandafile/js_pandafile_manager.h +++ b/ecmascript/jspandafile/js_pandafile_manager.h @@ -19,6 +19,7 @@ #include "ecmascript/jspandafile/js_pandafile.h" #include "ecmascript/jspandafile/panda_file_translator.h" #include "ecmascript/jspandafile/debug_info_extractor.h" +#include "ecmascript/platform/mutex.h" namespace panda { namespace ecmascript { @@ -59,7 +60,7 @@ public: template void EnumerateJSPandaFiles(Callback cb) { - os::memory::LockHolder lock(jsPandaFileLock_); + LockHolder lock(jsPandaFileLock_); for (const auto &item : loadedJSPandaFiles_) { if (!cb(item.second.first.get())) { return; @@ -97,7 +98,7 @@ private: static void *AllocateBuffer(size_t size); static void FreeBuffer(void *mem); - os::memory::RecursiveMutex jsPandaFileLock_; + RecursiveMutex jsPandaFileLock_; // JSPandaFile was hold by ecma vm list. using JSPandaFileVmsPair = std::pair, std::set>; std::unordered_map loadedJSPandaFiles_; diff --git a/ecmascript/jspandafile/literal_data_extractor.cpp b/ecmascript/jspandafile/literal_data_extractor.cpp index c26705ec32105f20482ed400a56ef688e8c31fbb..1624b6f8daf751d0c20c5fbddc9c801cd4c9de62 100644 --- a/ecmascript/jspandafile/literal_data_extractor.cpp +++ b/ecmascript/jspandafile/literal_data_extractor.cpp @@ -19,6 +19,7 @@ #include "ecmascript/compiler/aot_file/aot_file_manager.h" #include "ecmascript/ecma_string.h" #include "ecmascript/global_env.h" +#include "ecmascript/js_tagged_value.h" #include "ecmascript/js_thread.h" #include "ecmascript/module/js_module_manager.h" #include "ecmascript/patch/quick_fix_manager.h" @@ -222,10 +223,6 @@ JSHandle LiteralDataExtractor::DefineMethodInLiteral(JSThread *threa ASSERT(methodLiteral != nullptr); methodLiteral->SetFunctionKind(kind); bool canFastCall = false; - JSHandle method = factory->NewMethod( - jsPandaFile, methodLiteral, constpool, entryIndex, isLoadedAOT, &canFastCall); - JSHandle jsFunc = factory->NewJSFunction(method, kind, isLoadedAOT, canFastCall); - jsFunc->SetPropertyInlinedProps(thread, JSFunction::LENGTH_INLINE_PROPERTY_INDEX, JSTaggedValue(length)); CString moduleName = jsPandaFile->GetJSPandaFileDesc(); CString entry = JSPandaFile::ENTRY_FUNCTION_NAME; @@ -233,11 +230,21 @@ JSHandle LiteralDataExtractor::DefineMethodInLiteral(JSThread *threa moduleName = entryPoint; entry = entryPoint; } - if (jsPandaFile->IsModule(thread, entry)) { - JSHandle module = thread->GetCurrentEcmaContext()->GetModuleManager()->HostGetImportedModule( - moduleName); - jsFunc->SetModule(thread, module.GetTaggedValue()); + JSRecordInfo recordInfo; + bool hasRecord = jsPandaFile->CheckAndGetRecordInfo(entry, recordInfo); + if (!hasRecord) { + LOG_ECMA(FATAL) << "cannot find record '" + entry + "', please check the request path."; } + JSHandle module(thread, JSTaggedValue::Undefined()); + if (jsPandaFile->IsModule(recordInfo)) { + module = thread->GetCurrentEcmaContext()->GetModuleManager()->HostGetImportedModule(moduleName); + } + + JSHandle method = factory->NewMethod(jsPandaFile, methodLiteral, constpool, + JSHandle(thread, module.GetTaggedValue()), entryIndex, isLoadedAOT, &canFastCall); + JSHandle jsFunc = factory->NewJSFunction(method, kind, isLoadedAOT, canFastCall); + jsFunc->SetPropertyInlinedProps(thread, JSFunction::LENGTH_INLINE_PROPERTY_INDEX, JSTaggedValue(length)); + return jsFunc; } @@ -337,7 +344,7 @@ void LiteralDataExtractor::ExtractObjectDatas(JSThread *thread, const JSPandaFil int entryIndex = 0; bool needSetAotFlag = (isLoadedAOT && (epos % pairSize == 0) && !flag); if (needSetAotFlag) { - entryIndex = entryIndexes->Get(pos++).GetInt(); + entryIndex = entryIndexes->GetObjectFromCache(pos++).GetInt(); // -1 : this jsfunction is a large function if (entryIndex == -1) { needSetAotFlag = false; @@ -425,7 +432,7 @@ JSHandle LiteralDataExtractor::GetDatasIgnoreType(JSThread *thread, int entryIndex = 0; bool needSetAotFlag = isLoadedAOT; if (isLoadedAOT) { - entryIndex = entryIndexes->Get(index++).GetInt(); + entryIndex = entryIndexes->GetObjectFromCache(index++).GetInt(); if (entryIndex == -1) { needSetAotFlag = false; } diff --git a/ecmascript/jspandafile/method_literal.cpp b/ecmascript/jspandafile/method_literal.cpp index 55ef561333463eb5a0282fcd0fa3c588143ff632..b1003af7b73166681c567834346a2eab8d2a7c5f 100644 --- a/ecmascript/jspandafile/method_literal.cpp +++ b/ecmascript/jspandafile/method_literal.cpp @@ -121,6 +121,18 @@ CString MethodLiteral::GetRecordName(const JSPandaFile *jsPandaFile, EntityId me return JSPandaFile::ParseEntryPoint(desc); } +const char *MethodLiteral::GetRecordNameWithSymbol(const JSPandaFile *jsPandaFile, EntityId methodId) +{ + if (jsPandaFile == nullptr) { + return ""; + } + + const panda_file::File *pf = jsPandaFile->GetPandaFile(); + panda_file::MethodDataAccessor mda(*pf, methodId); + panda_file::ClassDataAccessor cda(*pf, mda.GetClassId()); + return utf::Mutf8AsCString(cda.GetDescriptor()); +} + uint32_t MethodLiteral::GetCodeSize(const JSPandaFile *jsPandaFile, EntityId methodId) { if (jsPandaFile == nullptr) { diff --git a/ecmascript/jspandafile/method_literal.h b/ecmascript/jspandafile/method_literal.h index 9cdd6c12e595141c93d34cbb4ad65c69a4d5e2a0..512c7361085106d9769fe6fc8053efdae0b8a603 100644 --- a/ecmascript/jspandafile/method_literal.h +++ b/ecmascript/jspandafile/method_literal.h @@ -17,7 +17,7 @@ #define ECMASCRIPT_JSPANDAFILE_METHOD_LITERAL_H #include "ecmascript/base/aligned_struct.h" -#include "ecmascript/compiler/gate_meta_data.h" +#include "ecmascript/compiler/share_gate_meta_data.h" #include "ecmascript/js_function_kind.h" #include "ecmascript/js_tagged_value.h" #include "ecmascript/mem/c_string.h" @@ -216,6 +216,7 @@ public: static constexpr size_t FUNCTION_KIND_NUM_BITS = 4; using BuiltinIdBits = BitField; // offset 0-7 using FunctionKindBits = BuiltinIdBits::NextField; // offset 8-11 + using IsNoGCBit = FunctionKindBits::NextFlag; // offset 12 inline NO_THREAD_SANITIZE void SetHotnessCounter(int16_t counter) { @@ -253,6 +254,16 @@ public: extraLiteralInfo_ = FunctionKindBits::Update(extraLiteralInfo_, kind); } + void SetNoGCBit(bool isNoGC) + { + extraLiteralInfo_ = IsNoGCBit::Update(extraLiteralInfo_, isNoGC); + } + + bool IsNoGC() const + { + return IsNoGCBit::Decode(extraLiteralInfo_); + } + FunctionKind GetFunctionKind() const { return static_cast(FunctionKindBits::Decode(extraLiteralInfo_)); @@ -297,6 +308,7 @@ public: static std::string PUBLIC_API ParseFunctionName(const JSPandaFile *jsPandaFile, EntityId methodId); static uint32_t GetCodeSize(const JSPandaFile *jsPandaFile, EntityId methodId); static CString GetRecordName(const JSPandaFile *jsPandaFile, EntityId methodId); + static const char PUBLIC_API *GetRecordNameWithSymbol(const JSPandaFile *jsPandaFile, EntityId methodId); const uint8_t *GetBytecodeArray() const { diff --git a/ecmascript/jspandafile/panda_file_translator.cpp b/ecmascript/jspandafile/panda_file_translator.cpp index 78fccd7a040a6c9f8173c0ebdf43a8051945ee39..d3122e4d24af3d5270b1cba5d86778210c156237 100644 --- a/ecmascript/jspandafile/panda_file_translator.cpp +++ b/ecmascript/jspandafile/panda_file_translator.cpp @@ -17,6 +17,7 @@ #include "ecmascript/compiler/aot_file/aot_file_manager.h" #include "ecmascript/global_env.h" +#include "ecmascript/interpreter/slow_runtime_stub.h" #include "ecmascript/js_array.h" #include "ecmascript/js_function.h" #include "ecmascript/js_thread.h" @@ -144,7 +145,13 @@ JSHandle PandaFileTranslator::GenerateProgramInternal(EcmaVM *vm, JSHandle method = factory->NewMethod(mainMethodLiteral); JSHandle hclass = JSHandle::Cast(env->GetFunctionClassWithProto()); JSHandle mainFunc = factory->NewJSFunctionByHClass(method, hclass); - + // Main function is created profileTypeInfo by default. + if (thread->IsPGOProfilerEnable()) { + auto res = SlowRuntimeStub::NotifyInlineCache(thread, method.GetObject()); + if (!res.IsUndefined()) { + thread->GetEcmaVM()->GetPGOProfiler()->PGOPreDump(mainFunc.GetTaggedType()); + } + } program->SetMainFunction(thread, mainFunc.GetTaggedValue()); method->SetConstantPool(thread, constpool); } diff --git a/ecmascript/jspandafile/program_object.h b/ecmascript/jspandafile/program_object.h index e9dc49c30fea8989cb130393fef4fb7a295c5704..52cf9709f458c5239211b65df845e6a9924b1876 100644 --- a/ecmascript/jspandafile/program_object.h +++ b/ecmascript/jspandafile/program_object.h @@ -26,7 +26,10 @@ #include "ecmascript/jspandafile/literal_data_extractor.h" #include "ecmascript/module/js_module_manager.h" #include "ecmascript/patch/quick_fix_manager.h" +#include "ecmascript/pgo_profiler/pgo_profiler.h" +#include "ecmascript/pgo_profiler/pgo_profiler_manager.h" +#include "ecmascript/pgo_profiler/pgo_utils.h" #include "libpandafile/class_data_accessor-inl.h" #include "libpandafile/index_accessor.h" @@ -55,6 +58,13 @@ public: * | object literal(JSObject) | | * | class literal(ClassLiteral) | v * +--------------------------------+---- + * | ... | ^ + * | InstanceTSHClass | | + * | ConstructorTSHClass |snapshotCPList(the positions of each part are random) + * | ArrayTSElements | | + * | ArrayTSElementsKind | v + * | constIndexInfo(TaggedArray) |at the end of snapshotCPList + * +--------------------------------+---- * | IndexHeader | * +--------------------------------+ * | JSPandaFile | @@ -62,9 +72,10 @@ public: */ class ConstantPool : public TaggedArray { public: - static constexpr size_t JS_PANDA_FILE_INDEX = 1; - static constexpr size_t INDEX_HEADER_INDEX = 2; - static constexpr size_t RESERVED_POOL_LENGTH = INDEX_HEADER_INDEX; + static constexpr size_t JS_PANDA_FILE_INDEX = 1; // not need gc + static constexpr size_t INDEX_HEADER_INDEX = 2; // not need gc + static constexpr size_t CONSTANT_INDEX_INFO_INDEX = 3; + static constexpr size_t RESERVED_POOL_LENGTH = INDEX_HEADER_INDEX; // divide the gc area static ConstantPool *Cast(TaggedObject *object) { @@ -136,18 +147,22 @@ public: static size_t ComputeSize(uint32_t cacheSize) { - return TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), cacheSize + RESERVED_POOL_LENGTH); + // 1 : constIndexInfo is a TaggedArray, take up an extra spot + return TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), cacheSize + 1 + RESERVED_POOL_LENGTH); } - inline void InitializeWithSpecialValue(JSTaggedValue initValue, uint32_t capacity, uint32_t extraLength = 0) + inline void InitializeWithSpecialValue(JSThread *thread, JSTaggedValue initValue, + uint32_t capacity, uint32_t extraLength = 0) { ASSERT(initValue.IsSpecial()); - SetLength(capacity + RESERVED_POOL_LENGTH); + // 1 : constIndexInfo is a TaggedArray, take up an extra spot + SetLength(capacity + 1 + RESERVED_POOL_LENGTH); SetExtraLength(extraLength); for (uint32_t i = 0; i < capacity; i++) { size_t offset = JSTaggedValue::TaggedTypeSize() * i; Barriers::SetPrimitive(GetData(), offset, initValue.GetRawData()); } + SetConstantIndexInfo(thread); SetJSPandaFile(nullptr); SetIndexHeader(nullptr); } @@ -167,6 +182,12 @@ public: return Barriers::GetValue(GetData(), GetJSPandaFileOffset()); } + inline void SetConstantIndexInfo(JSThread *thread) + { + JSHandle array(thread->GlobalConstants()->GetHandledEmptyArray()); + Barriers::SetPrimitive(GetData(), GetConstantIndexInfoOffset(), array.GetTaggedValue().GetRawData()); + } + inline void SetObjectToCache(JSThread *thread, uint32_t index, JSTaggedValue value) { Set(thread, index, value); @@ -177,7 +198,8 @@ public: return Get(index); } - static JSTaggedValue GetMethodFromCache(JSThread *thread, JSTaggedValue constpool, uint32_t index) + static JSTaggedValue GetMethodFromCache( + JSThread *thread, JSTaggedValue constpool, JSTaggedValue module, uint32_t index) { const ConstantPool *taggedPool = ConstantPool::Cast(constpool.GetTaggedObject()); auto val = taggedPool->GetObjectFromCache(index); @@ -200,13 +222,14 @@ public: [[maybe_unused]] EcmaHandleScope handleScope(thread); ASSERT(jsPandaFile->IsNewVersion()); JSHandle constpoolHandle(thread, constpool); + JSHandle moduleHandle(thread, module); EcmaVM *vm = thread->GetEcmaVM(); EntityId id = constpoolHandle->GetEntityId(index); MethodLiteral *methodLiteral = jsPandaFile->FindMethodLiteral(id.GetOffset()); ASSERT(methodLiteral != nullptr); ObjectFactory *factory = vm->GetFactory(); - JSHandle method = factory->NewMethod(jsPandaFile, methodLiteral, constpoolHandle, + JSHandle method = factory->NewMethod(jsPandaFile, methodLiteral, constpoolHandle, moduleHandle, entryIndex, isLoadedAOT && hasEntryIndex); constpoolHandle->SetObjectToCache(thread, index, method.GetTaggedValue()); return method.GetTaggedValue(); @@ -274,7 +297,11 @@ public: JSMutableHandle properties(thread, JSTaggedValue::Undefined()); LiteralDataExtractor::ExtractObjectDatas(thread, jsPandaFile, id, elements, properties, constpoolHandle, entry, needSetAotFlag, entryIndexes); - JSHandle obj = JSObject::CreateObjectFromProperties(thread, properties); + JSTaggedValue ihcVal = JSTaggedValue::Undefined(); + if (needSetAotFlag) { + ihcVal = entryIndexes->GetIhc(); + } + JSHandle obj = JSObject::CreateObjectFromProperties(thread, properties, ihcVal); JSMutableHandle key(thread, JSTaggedValue::Undefined()); JSMutableHandle valueHandle(thread, JSTaggedValue::Undefined()); size_t elementsLen = elements->GetLength(); @@ -286,6 +313,13 @@ public: valueHandle.Update(elements->Get(i + 1)); JSObject::DefinePropertyByLiteral(thread, obj, key, valueHandle); } + if (thread->GetEcmaVM()->IsEnablePGOProfiler()) { + pgo::ApEntityId abcId(0); + pgo::PGOProfilerManager::GetInstance()->GetPandaFileId(jsPandaFile->GetJSPandaFileDesc(), + abcId); + thread->GetEcmaVM()->GetPGOProfiler()->ProfileCreateObject(obj.GetTaggedType(), abcId, + id.GetOffset()); + } val = obj.GetTaggedValue(); break; } @@ -295,6 +329,9 @@ public: uint32_t length = literal->GetLength(); JSHandle arr(JSArray::ArrayCreate(thread, JSTaggedNumber(length), ArrayMode::LITERAL)); arr->SetElements(thread, literal); + if (thread->GetEcmaVM()->IsEnablePGOProfiler()) { + JSHClass::TransitToElementsKind(thread, arr); + } val = arr.GetTaggedValue(); break; } @@ -308,6 +345,13 @@ public: return val; } + static panda_file::File::EntityId GetIdFromCache(JSTaggedValue constpool, uint32_t index) + { + const ConstantPool *taggedPool = ConstantPool::Cast(constpool.GetTaggedObject()); + panda_file::File::EntityId id = taggedPool->GetEntityId(index); + return id; + } + template static JSTaggedValue GetLiteralFromCache(JSThread *thread, JSTaggedValue constpool, uint32_t index, JSTaggedValue module) @@ -356,6 +400,11 @@ private: return JSTaggedValue::TaggedTypeSize() * (GetLength() - INDEX_HEADER_INDEX); } + inline size_t GetConstantIndexInfoOffset() const + { + return JSTaggedValue::TaggedTypeSize() * (GetLength() - CONSTANT_INDEX_INFO_INDEX); + } + inline size_t GetLastOffset() const { return JSTaggedValue::TaggedTypeSize() * GetLength() + DATA_OFFSET; diff --git a/ecmascript/jspandafile/scope_info_extractor.cpp b/ecmascript/jspandafile/scope_info_extractor.cpp index 3d5e748af39766d47c27d5d49dd73e5d209301e9..e4b33e291ea7b35bd7653b122ea40d066e9369ec 100644 --- a/ecmascript/jspandafile/scope_info_extractor.cpp +++ b/ecmascript/jspandafile/scope_info_extractor.cpp @@ -18,6 +18,7 @@ #include "ecmascript/interpreter/frame_handler.h" #include "ecmascript/jspandafile/literal_data_extractor.h" #include "ecmascript/jspandafile/program_object.h" +#include "ecmascript/object_factory-inl.h" #include "ecmascript/tagged_array-inl.h" namespace panda::ecmascript { diff --git a/ecmascript/jspandafile/tests/js_pandafile_test.cpp b/ecmascript/jspandafile/tests/js_pandafile_test.cpp index c9eec87b6e1b5640c96f7155e87459e2a78fd6ac..6beb52620358be22f6c920b9fe63e058fc252b7e 100644 --- a/ecmascript/jspandafile/tests/js_pandafile_test.cpp +++ b/ecmascript/jspandafile/tests/js_pandafile_test.cpp @@ -15,6 +15,7 @@ #include "assembler/assembly-emitter.h" #include "assembler/assembly-parser.h" +#include "gtest/gtest.h" #include "libpandabase/utils/utf.h" #include "libpandafile/class_data_accessor-inl.h" @@ -263,8 +264,10 @@ HWTEST_F_L0(JSPandaFileTest, IsModule_IsCjs) )"; const CString fileName1 = "test1.pa"; std::shared_ptr pf1 = CreateJSPandaFile(source1, fileName1); - EXPECT_EQ(pf1->IsModule(thread), false); - EXPECT_EQ(pf1->IsCjs(thread), false); + JSPandaFile::JSRecordInfo info = + const_cast(pf1.get())-> FindRecordInfo(JSPandaFile::ENTRY_FUNCTION_NAME); + EXPECT_EQ(pf1->IsModule(info), false); + EXPECT_EQ(pf1->IsCjs(info), false); } HWTEST_F_L0(JSPandaFileTest, SetLoadedAOTStatus_IsLoadedAOT) @@ -306,4 +309,38 @@ HWTEST_F_L0(JSPandaFileTest, IsParsedConstpoolOfCurrentVM) recordInfo.SetParsedConstpoolVM(instance); EXPECT_TRUE(pf->FindRecordInfo(JSPandaFile::ENTRY_FUNCTION_NAME).IsParsedConstpoolOfCurrentVM(instance)); } + +HWTEST_F_L0(JSPandaFileTest, NormalizedFileDescTest) +{ + const char *source = R"( + .function void foo() {} + )"; + CString fileName = "/data/storage/el1/bundle/entry/ets/modules.abc"; + std::shared_ptr pf = CreateJSPandaFile(source, fileName); + EXPECT_EQ(pf->GetNormalizedFileDesc(), "entry/ets/modules.abc"); + + fileName = "/data/storage/el1/bundle/entry/ets/widgets.abc"; + pf = CreateJSPandaFile(source, fileName); + EXPECT_EQ(pf->GetNormalizedFileDesc(), "entry/ets/widgets.abc"); + + fileName = "/data/app/el1/bundle/public/com.xx.xx/entry.hsp/entry/ets/modules.abc"; + pf = CreateJSPandaFile(source, fileName); + EXPECT_EQ(pf->GetNormalizedFileDesc(), "entry/ets/modules.abc"); + + fileName = "/data/app/el1/bundle/public/com.xx.xx/entry.hap/entry/ets/widgets.abc"; + pf = CreateJSPandaFile(source, fileName); + EXPECT_EQ(pf->GetNormalizedFileDesc(), "entry/ets/widgets.abc"); + + fileName = "/system/app/Camera/Camera.hap/entry1/ets/modules.abc"; + pf = CreateJSPandaFile(source, fileName); + EXPECT_EQ(pf->GetNormalizedFileDesc(), "entry1/ets/modules.abc"); + + fileName = "test.pa"; + pf = CreateJSPandaFile(source, fileName); + EXPECT_EQ(pf->GetNormalizedFileDesc(), fileName); + + fileName = "libapp.ability.AbilityStage.z.so/app.ability.AbilityStage.js"; + pf = CreateJSPandaFile(source, fileName); + EXPECT_EQ(pf->GetNormalizedFileDesc(), fileName); +} } // namespace panda::test \ No newline at end of file diff --git a/ecmascript/layout_info.cpp b/ecmascript/layout_info.cpp index 6c64a35551ba89dda1b605a1b7653b9189e501b8..6d4664ac280cd56e7417566d337b20e7ce7a82a9 100644 --- a/ecmascript/layout_info.cpp +++ b/ecmascript/layout_info.cpp @@ -20,8 +20,20 @@ #include "ecmascript/js_object-inl.h" #include "ecmascript/js_symbol.h" #include "ecmascript/mem/assert_scope.h" +#include "pgo_profiler/pgo_profiler_layout.h" namespace panda::ecmascript { +using PGOHandler = pgo::PGOHandler; +void LayoutInfo::Initialize(const JSThread *thread, int num) +{ + SetExtraLength(num); + int propNum = GetPropertiesCapacity(); + auto attr = PropertyAttributes(); + for (int i = 0; i < propNum; i++) { + SetPropertyInit(thread, i, JSTaggedValue::Hole(), attr); + } +} + void LayoutInfo::AddKey(const JSThread *thread, [[maybe_unused]] int index, const JSTaggedValue &key, const PropertyAttributes &attr) { @@ -76,7 +88,7 @@ void LayoutInfo::GetAllKeys(const JSThread *thread, int end, int offset, TaggedA } } void LayoutInfo::GetAllKeysByFilter(const JSThread *thread, uint32_t numberOfProps, uint32_t &keyArrayEffectivelength, - TaggedArray *keyArray, const JSHandle object, uint32_t filter) + TaggedArray *keyArray, const JSHandle object, uint32_t filter) { ASSERT(numberOfProps <= static_cast(NumberOfElements())); ASSERT_PRINT(keyArrayEffectivelength + numberOfProps <= keyArray->GetLength(), @@ -117,15 +129,12 @@ void LayoutInfo::GetAllKeysByFilter(const JSThread *thread, uint32_t numberOfPro } } -void LayoutInfo::GetAllKeys(int end, std::vector &keyVector, const JSHandle object) +void LayoutInfo::GetAllKeysForSerialization(int end, std::vector &keyVector) { ASSERT(end <= NumberOfElements()); for (int i = 0; i < end; i++) { JSTaggedValue key = GetKey(i); if (key.IsString() || key.IsSymbol()) { - if (IsUninitializedProperty(object, i)) { - continue; - } keyVector.emplace_back(key); } } @@ -164,7 +173,7 @@ bool LayoutInfo::IsUninitializedProperty(const JSHandle object, uint32 return val.IsHole(); } -void LayoutInfo::DumpFieldIndexForProfile(int index, PGOHClassLayoutDesc &desc, PGOObjLayoutKind kind) +void LayoutInfo::DumpFieldIndexForProfile(int index, PGOHClassLayoutDesc &desc, PGOObjKind kind) { auto key = GetKey(index); if (key.IsString()) { @@ -172,7 +181,7 @@ void LayoutInfo::DumpFieldIndexForProfile(int index, PGOHClassLayoutDesc &desc, TrackType type = attr.GetTrackType(); bool isAccessor = attr.IsAccessor(); auto keyString = EcmaStringAccessor(key).ToCString(); - desc.UpdateKeyAndDesc(keyString.c_str(), PGOHandler(type, isAccessor), kind); + desc.UpdateKeyAndDesc(keyString, PGOHandler(type, isAccessor), kind); } } } // namespace panda::ecmascript diff --git a/ecmascript/layout_info.h b/ecmascript/layout_info.h index 83e15f09dd23340880a7b06450b1249c900602b6..880ec19ff0e079a5d4fa1459fedc2403c10773de 100644 --- a/ecmascript/layout_info.h +++ b/ecmascript/layout_info.h @@ -29,7 +29,7 @@ struct Properties { class LayoutInfo : private TaggedArray { public: static constexpr int MIN_PROPERTIES_LENGTH = JSObject::MIN_PROPERTIES_LENGTH; - static constexpr int MAX_PROPERTIES_LENGTH = PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES; + static constexpr int MAX_PROPERTIES_LENGTH = PropertyAttributes::MAX_FAST_PROPS_CAPACITY; static constexpr uint32_t ELEMENTS_INDEX_LOG2 = 1; static constexpr uint32_t ATTR_INDEX_OFFSET = 1; @@ -39,6 +39,7 @@ public: return reinterpret_cast(obj); } + void Initialize(const JSThread *thread, int num = 0); int GetPropertiesCapacity() const; int NumberOfElements() const; void SetNumberOfElements(const JSThread *thread, int properties); @@ -80,13 +81,13 @@ public: int BinarySearch(JSTaggedValue key, int propertiesNumber); void GetAllKeys(const JSThread *thread, int end, int offset, TaggedArray *keyArray, const JSHandle object); - void GetAllKeys(int end, std::vector &keyVector, const JSHandle object); + void GetAllKeysForSerialization(int end, std::vector &keyVector); void GetAllKeysByFilter(const JSThread *thread, uint32_t numberOfProps, uint32_t &keyArrayEffectivelength, TaggedArray *keyArray, const JSHandle object, uint32_t filter); void GetAllEnumKeys(const JSThread *thread, int end, int offset, TaggedArray *keyArray, uint32_t *keys, const JSHandle object); - void DumpFieldIndexForProfile(int index, PGOHClassLayoutDesc &desc, PGOObjLayoutKind kind); + void DumpFieldIndexForProfile(int index, PGOHClassLayoutDesc &desc, PGOObjKind kind); DECL_DUMP() private: diff --git a/ecmascript/marker_cell.h b/ecmascript/marker_cell.h new file mode 100644 index 0000000000000000000000000000000000000000..3c072251f9327c503e97ea13f0b501fada69c472 --- /dev/null +++ b/ecmascript/marker_cell.h @@ -0,0 +1,52 @@ +/* + * 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_MARKER_CELL_H +#define ECMASCRIPT_MARKER_CELL_H + +#include "ecmascript/ecma_macros.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_tagged_value.h" + +namespace panda { +namespace ecmascript { +class MarkerCell : public TaggedObject { +public: + static MarkerCell *Cast(TaggedObject *object) + { + ASSERT(JSTaggedValue(object).IsMarkerCell()); + return static_cast(object); + } + + static constexpr size_t BIT_FIELD_OFFSET = TaggedObjectSize(); + ACCESSORS_BIT_FIELD(BitField, BIT_FIELD_OFFSET, LAST_OFFSET) + DEFINE_ALIGN_SIZE(LAST_OFFSET); + + // define BitField + static constexpr size_t IS_DETECTOR_INVALID_BITS = 1; + FIRST_BIT_FIELD(BitField, IsDetectorInvalid, bool, IS_DETECTOR_INVALID_BITS); + + DECL_DUMP() + + inline void InvalidatePropertyDetector() + { + SetIsDetectorInvalid(true); + } +}; + +} // namespace ecmascript +} // namespace panda + +#endif // ECMASCRIPT_MARKER_CELL_H diff --git a/ecmascript/mem/barriers-inl.h b/ecmascript/mem/barriers-inl.h index c679882ed5b8f916cc0287c461a22d83d33be815..d4308e498b6fe35c49da74a5d8cc1afb8e4c8296 100644 --- a/ecmascript/mem/barriers-inl.h +++ b/ecmascript/mem/barriers-inl.h @@ -55,6 +55,12 @@ inline void Barriers::SetObject([[maybe_unused]] const JSThread *thread, void *o *reinterpret_cast(reinterpret_cast(obj) + offset) = value; WriteBarrier(obj, offset, value); } + +inline void Barriers::SynchronizedSetClass(void *obj, JSTaggedType value) +{ + reinterpret_cast *>(obj)->store(value, std::memory_order_release); + WriteBarrier(obj, 0, value); +} } // namespace panda::ecmascript #endif // ECMASCRIPT_MEM_BARRIERS_INL_H diff --git a/ecmascript/mem/barriers.cpp b/ecmascript/mem/barriers.cpp index 70be849bbc96530b39aa9f98c78bbf4defde12c7..899c7162852fa66397b8fa4dcc20a11e5cd35b3f 100644 --- a/ecmascript/mem/barriers.cpp +++ b/ecmascript/mem/barriers.cpp @@ -20,7 +20,7 @@ namespace panda::ecmascript { void Barriers::Update(uintptr_t slotAddr, Region *objectRegion, TaggedObject *value, Region *valueRegion) { - JSThread* thread = valueRegion->GetJSThread(); + JSThread *thread = valueRegion->GetJSThread(); auto heap = thread->GetEcmaVM()->GetHeap(); if (heap->IsFullMark()) { if (valueRegion->InCollectSet() && !objectRegion->InYoungSpaceOrCSet()) { diff --git a/ecmascript/mem/barriers.h b/ecmascript/mem/barriers.h index e666b9096d83f973ed989a0103d57fd932b4b55a..6bb5f6fea351b6c845c7480ffb3435f44d28ff3d 100644 --- a/ecmascript/mem/barriers.h +++ b/ecmascript/mem/barriers.h @@ -44,6 +44,8 @@ public: template static void SetObject(const JSThread *thread, void *obj, size_t offset, JSTaggedType value); + static void SynchronizedSetClass(void *obj, JSTaggedType value); + template static inline T GetValue(const void *obj, size_t offset) { diff --git a/ecmascript/mem/c_containers.h b/ecmascript/mem/c_containers.h index a528adb891036ad42b5ec75f938681ca1d7c247c..f317f9e0b33d55f2e4be6b9326f3d01c7aefc715 100644 --- a/ecmascript/mem/c_containers.h +++ b/ecmascript/mem/c_containers.h @@ -37,6 +37,9 @@ using CVector = std::vector>; template using CList = std::list>; +template> +using CSet = std::set>; + template> using CMap = std::map>>; diff --git a/ecmascript/mem/chunk.h b/ecmascript/mem/chunk.h index c1cf14c220eb494ff4490c889d91e273fcc9eabd..4f58304c093b9e275c70ada91ed6ca2c08184d38 100644 --- a/ecmascript/mem/chunk.h +++ b/ecmascript/mem/chunk.h @@ -29,6 +29,7 @@ public: static constexpr size_t MEM_ALIGN = 8U; explicit Chunk(NativeAreaAllocator *allocator); + Chunk() = default; ~Chunk() { ReleaseMemory(); @@ -84,17 +85,19 @@ public: // do nothing } +protected: + uintptr_t ptr_ {0}; + uintptr_t end_ {0}; + + Area *currentArea_ {nullptr}; + NativeAreaAllocator *allocator_ {nullptr}; + private: uintptr_t Expand(size_t size); Area *NewArea(size_t size); void ReleaseMemory(); - uintptr_t ptr_ {0}; - uintptr_t end_ {0}; - - Area *currentArea_ {nullptr}; EcmaList areaList_ {}; - NativeAreaAllocator *allocator_ {nullptr}; }; class PUBLIC_API ChunkObject { diff --git a/ecmascript/mem/concurrent_marker.cpp b/ecmascript/mem/concurrent_marker.cpp index 3368611bdd9314e2722679a01903c8bdc7766600..b560be7ad6384d43e2198fabedfe18cda0e407a1 100644 --- a/ecmascript/mem/concurrent_marker.cpp +++ b/ecmascript/mem/concurrent_marker.cpp @@ -31,7 +31,7 @@ namespace panda::ecmascript { size_t ConcurrentMarker::taskCounts_ = 0; -os::memory::Mutex ConcurrentMarker::taskCountMutex_; +Mutex ConcurrentMarker::taskCountMutex_; ConcurrentMarker::ConcurrentMarker(Heap *heap, EnableConcurrentMarkType type) : heap_(heap), @@ -63,9 +63,6 @@ void ConcurrentMarker::Mark() MEM_ALLOCATE_AND_GC_TRACE(vm_, ConcurrentMarking); InitializeMarking(); Taskpool::GetCurrentTaskpool()->PostTask(std::make_unique(heap_->GetJSThread()->GetThreadId(), heap_)); - if (!heap_->IsFullMark() && heap_->IsParallelGCEnabled()) { - heap_->PostParallelGCTask(ParallelGCTaskPhase::CONCURRENT_HANDLE_OLD_TO_NEW_TASK); - } } void ConcurrentMarker::Finish() @@ -80,18 +77,13 @@ void ConcurrentMarker::ReMark() MEM_ALLOCATE_AND_GC_TRACE(vm_, ReMarking); Marker *nonMovableMarker = heap_->GetNonMovableMarker(); nonMovableMarker->MarkRoots(MAIN_THREAD_INDEX); - if (!heap_->IsFullMark() && !heap_->IsParallelGCEnabled()) { - nonMovableMarker->ProcessOldToNew(MAIN_THREAD_INDEX); - nonMovableMarker->ProcessSnapshotRSet(MAIN_THREAD_INDEX); - } else { - nonMovableMarker->ProcessMarkStack(MAIN_THREAD_INDEX); - } + nonMovableMarker->ProcessMarkStack(MAIN_THREAD_INDEX); heap_->WaitRunningTaskFinished(); } void ConcurrentMarker::HandleMarkingFinished() // js-thread wait for sweep { - os::memory::LockHolder lock(waitMarkingFinishedMutex_); + LockHolder lock(waitMarkingFinishedMutex_); if (notifyMarkingFinished_) { heap_->CollectGarbage(heap_->IsFullMark() ? TriggerGCType::OLD_GC : TriggerGCType::YOUNG_GC, GCReason::ALLOCATION_LIMIT); @@ -100,7 +92,7 @@ void ConcurrentMarker::HandleMarkingFinished() // js-thread wait for sweep void ConcurrentMarker::WaitMarkingFinished() // call in EcmaVm thread, wait for mark finished { - os::memory::LockHolder lock(waitMarkingFinishedMutex_); + LockHolder lock(waitMarkingFinishedMutex_); if (!notifyMarkingFinished_) { vmThreadWaitMarkingFinished_ = true; waitMarkingFinishedCV_.Wait(&waitMarkingFinishedMutex_); @@ -152,11 +144,17 @@ void ConcurrentMarker::InitializeMarking() heapObjectSize_ = heap_->GetNewSpace()->GetHeapObjectSize(); } workManager_->Initialize(TriggerGCType::OLD_GC, ParallelGCTaskPhase::CONCURRENT_HANDLE_GLOBAL_POOL_TASK); + if (!heap_->IsFullMark()) { + heap_->GetNonMovableMarker()->ProcessOldToNewNoMarkStack(MAIN_THREAD_INDEX); + heap_->GetNonMovableMarker()->ProcessSnapshotRSetNoMarkStack(MAIN_THREAD_INDEX); + } heap_->GetNonMovableMarker()->MarkRoots(MAIN_THREAD_INDEX); } bool ConcurrentMarker::MarkerTask::Run(uint32_t threadId) { + // Synchronizes-with. Ensure that WorkManager::Initialize must be seen by MarkerThreads. + while (!heap_->GetWorkManager()->HasInitialized()); ClockScope clockScope; heap_->GetNonMovableMarker()->ProcessMarkStack(threadId); heap_->WaitRunningTaskFinished(); @@ -166,7 +164,7 @@ bool ConcurrentMarker::MarkerTask::Run(uint32_t threadId) void ConcurrentMarker::FinishMarking(float spendTime) { - os::memory::LockHolder lock(waitMarkingFinishedMutex_); + LockHolder lock(waitMarkingFinishedMutex_); thread_->SetMarkStatus(MarkStatus::MARK_FINISHED); thread_->SetCheckSafePointStatus(); if (vmThreadWaitMarkingFinished_) { diff --git a/ecmascript/mem/concurrent_marker.h b/ecmascript/mem/concurrent_marker.h index 2fd317ec57223f97a479965c3aa963e9724dbdaf..5e72b7b78f6272db7d97c3e5318643f9e333a165 100644 --- a/ecmascript/mem/concurrent_marker.h +++ b/ecmascript/mem/concurrent_marker.h @@ -24,7 +24,7 @@ #include "ecmascript/mem/work_manager.h" #include "ecmascript/taskpool/task.h" -#include "libpandabase/os/mutex.h" +#include "ecmascript/platform/mutex.h" namespace panda::ecmascript { class EcmaVM; @@ -48,7 +48,7 @@ public: { size_t taskPoolSize = Taskpool::GetCurrentTaskpool()->GetTotalThreadNum(); { - os::memory::LockHolder holder(taskCountMutex_); + LockHolder holder(taskCountMutex_); // total counts of running concurrent mark tasks should be less than taskPoolSize if (taskCounts_ + 1 < taskPoolSize) { taskCounts_++; @@ -61,7 +61,7 @@ public: static void DecreaseTaskCounts() { - os::memory::LockHolder holder(taskCountMutex_); + LockHolder holder(taskCountMutex_); taskCounts_--; } @@ -145,7 +145,7 @@ private: void FinishMarking(float spendTime); static size_t taskCounts_; - static os::memory::Mutex taskCountMutex_; + static Mutex taskCountMutex_; Heap *heap_ {nullptr}; EcmaVM *vm_ {nullptr}; @@ -160,8 +160,8 @@ private: bool notifyMarkingFinished_ {false}; // notify js-thread that marking is finished and sweeping is needed bool vmThreadWaitMarkingFinished_ {false}; // jsMainThread waiting for concurrentGC FINISHED bool isConcurrentMarking_ {false}; - os::memory::Mutex waitMarkingFinishedMutex_; - os::memory::ConditionVariable waitMarkingFinishedCV_; + Mutex waitMarkingFinishedMutex_; + ConditionVariable waitMarkingFinishedCV_; }; } // namespace panda::ecmascript #endif // ECMASCRIPT_MEM_CONCURRENT_MARKER_H diff --git a/ecmascript/mem/concurrent_sweeper.cpp b/ecmascript/mem/concurrent_sweeper.cpp index 1adc8423b4ca582f5f8c987d3762e6d387a1aeb5..b7d5d1508cb2c6052c02d2c447ddbeb636a1876b 100644 --- a/ecmascript/mem/concurrent_sweeper.cpp +++ b/ecmascript/mem/concurrent_sweeper.cpp @@ -74,7 +74,7 @@ void ConcurrentSweeper::AsyncSweepSpace(MemSpaceType type, bool isMain) auto space = heap_->GetSpaceWithType(type); space->AsyncSweep(isMain); - os::memory::LockHolder holder(mutexs_[type]); + LockHolder holder(mutexs_[type]); if (--remainingTaskNum_[type] == 0) { cvs_[type].SignalAll(); } @@ -87,7 +87,7 @@ void ConcurrentSweeper::WaitAllTaskFinished() } for (int i = startSpaceType_; i < FREE_LIST_NUM; i++) { if (remainingTaskNum_[i] > 0) { - os::memory::LockHolder holder(mutexs_[i]); + LockHolder holder(mutexs_[i]); while (remainingTaskNum_[i] > 0) { cvs_[i].Wait(&mutexs_[i]); } @@ -123,11 +123,11 @@ void ConcurrentSweeper::WaitingTaskFinish(MemSpaceType type) { if (remainingTaskNum_[type] > 0) { { - os::memory::LockHolder holder(mutexs_[type]); + LockHolder holder(mutexs_[type]); remainingTaskNum_[type]++; } AsyncSweepSpace(type, true); - os::memory::LockHolder holder(mutexs_[type]); + LockHolder holder(mutexs_[type]); while (remainingTaskNum_[type] > 0) { cvs_[type].Wait(&mutexs_[type]); } diff --git a/ecmascript/mem/concurrent_sweeper.h b/ecmascript/mem/concurrent_sweeper.h index 2c4bd9919a829e1a8e5593dfd30fe8fb19dc6785..58e49e7726ba8cbd7be770a1c5204d34fbc958a8 100644 --- a/ecmascript/mem/concurrent_sweeper.h +++ b/ecmascript/mem/concurrent_sweeper.h @@ -22,7 +22,7 @@ #include "ecmascript/mem/space.h" #include "ecmascript/taskpool/task.h" -#include "libpandabase/os/mutex.h" +#include "ecmascript/platform/mutex.h" namespace panda::ecmascript { // CONFIG_DISABLE means concurrent sweeper is disabled by options or macros and cannot be changed. @@ -108,8 +108,8 @@ private: void WaitingTaskFinish(MemSpaceType type); - std::array mutexs_; - std::array cvs_; + std::array mutexs_; + std::array cvs_; std::array remainingTaskNum_ = {0, 0, 0}; Heap *heap_; diff --git a/ecmascript/mem/dyn_chunk.h b/ecmascript/mem/dyn_chunk.h index 7888e14f85082777997b459b433b28ae4519a3eb..01537baa7d362b0f5e3eabefebbc279da8e11703 100644 --- a/ecmascript/mem/dyn_chunk.h +++ b/ecmascript/mem/dyn_chunk.h @@ -19,12 +19,13 @@ #include #include "ecmascript/common.h" +#include "ecmascript/mem/regexp_cached_chunk.h" #include "ecmascript/mem/chunk.h" namespace panda::ecmascript { class PUBLIC_API DynChunk { public: - static constexpr size_t ALLOCATE_MIN_SIZE = 64; + static constexpr size_t ALLOCATE_MIN_SIZE = 256; static constexpr int FAILURE = -1; static constexpr int SUCCESS = 0; explicit DynChunk(Chunk *chunk) : chunk_(chunk) @@ -139,15 +140,16 @@ private: friend class RegExpOpCode; friend class RegExpExecutor; - DynChunk(uint8_t *buf, Chunk *chunk) : buf_(buf), chunk_(chunk) + DynChunk(uint8_t *buf, RegExpCachedChunk *regExpCachedChunk) : buf_(buf), regExpCachedChunk_(regExpCachedChunk) { - ASSERT(chunk_ != nullptr); + ASSERT(regExpCachedChunk_ != nullptr); }; uint8_t *buf_ {nullptr}; size_t size_ {0}; size_t allocatedSize_ {0}; bool error_ {false}; + RegExpCachedChunk *regExpCachedChunk_ {nullptr}; Chunk *chunk_ {nullptr}; }; } // namespace panda::ecmascript diff --git a/ecmascript/mem/full_gc.cpp b/ecmascript/mem/full_gc.cpp index 079c1ff44313140d51e57e951cdb67ec3e090132..80f0d7e61da4f587502262638d596de109e646ff 100644 --- a/ecmascript/mem/full_gc.cpp +++ b/ecmascript/mem/full_gc.cpp @@ -18,6 +18,8 @@ #include "ecmascript/ecma_vm.h" #include "ecmascript/mem/clock_scope.h" #include "ecmascript/mem/concurrent_marker.h" +#include "ecmascript/mem/concurrent_sweeper.h" +#include "ecmascript/mem/heap-inl.h" #include "ecmascript/mem/mark_stack.h" #include "ecmascript/mem/mem.h" #include "ecmascript/mem/parallel_marker-inl.h" @@ -131,6 +133,10 @@ void FullGC::Sweep() auto stringTable = heap_->GetEcmaVM()->GetEcmaStringTable(); WeakRootVisitor gcUpdateWeak = [this](TaggedObject *header) { Region *objectRegion = Region::ObjectAddressToRange(header); + if (!objectRegion) { + LOG_GC(ERROR) << "FullGC updateWeakReference: region is nullptr, header is " << header; + return reinterpret_cast(ToUintPtr(nullptr)); + } if (!HasEvacuated(objectRegion)) { if (objectRegion->Test(header)) { return header; diff --git a/ecmascript/mem/gc_stats.cpp b/ecmascript/mem/gc_stats.cpp index bb4323960424dc1a6741f4ce9c64c0260fc4193d..e64592c62a97777a515bb1359cf069a709bce3bc 100644 --- a/ecmascript/mem/gc_stats.cpp +++ b/ecmascript/mem/gc_stats.cpp @@ -147,7 +147,7 @@ void GCStats::PrintGCMemoryStatistic() << STATS_DESCRIPTION_FORMAT("Native memory usage size:") << STATS_DATA_FORMAT(sizeToMB(nativeAreaAllocator->GetNativeMemoryUsage())) << "MB\n" << STATS_DESCRIPTION_FORMAT("NativeBindingSize:") - << STATS_DATA_FORMAT(sizeToKB(heap_->GetNativeBindingSize())) << "MB"; + << STATS_DATA_FORMAT(sizeToKB(heap_->GetNativeBindingSize())) << "KB"; switch (gcType_) { case GCType::STW_YOUNG_GC: { @@ -561,9 +561,6 @@ void GCStats::InitializeRecordList() for (float &duration : scopeDuration_) { duration = 0.0f; } - for (int idx = 0; idx <= GetRecordDataIndex(RecordData::END_RECORD_OVERWRITE); idx++) { - recordData_[idx] = 0; - } concurrentMark_ = false; } diff --git a/ecmascript/mem/heap-inl.h b/ecmascript/mem/heap-inl.h index f5ea98d44098efd8e6b7cdc12eba0e7fc81195b1..ef9f0ee0d56bffc300655d9afc43c287376f5461 100644 --- a/ecmascript/mem/heap-inl.h +++ b/ecmascript/mem/heap-inl.h @@ -229,7 +229,7 @@ TaggedObject *Heap::AllocateNonMovableOrHugeObject(JSHClass *hclass, size_t size if (size > MAX_REGULAR_HEAP_OBJECT_SIZE) { return AllocateHugeObject(hclass, size); } - auto object = reinterpret_cast(nonMovableSpace_->Allocate(size)); + auto object = reinterpret_cast(nonMovableSpace_->CheckAndAllocate(size)); CHECK_OBJ_AND_THROW_OOM_ERROR(object, size, nonMovableSpace_, "Heap::AllocateNonMovableOrHugeObject"); object->SetClass(hclass); OnAllocateEvent(reinterpret_cast(object), size); @@ -302,27 +302,6 @@ uintptr_t Heap::AllocateSnapshotSpace(size_t size) return object; } -void Heap::OnAllocateEvent([[maybe_unused]] TaggedObject* address, [[maybe_unused]] size_t size) -{ -#if defined(ECMASCRIPT_SUPPORT_SNAPSHOT) - if (tracker_ != nullptr) { - BlockHookScope blockScope; - tracker_->AllocationEvent(address, size); - } -#endif -} - -void Heap::OnMoveEvent([[maybe_unused]] uintptr_t address, [[maybe_unused]] TaggedObject* forwardAddress, - [[maybe_unused]] size_t size) -{ -#if defined(ECMASCRIPT_SUPPORT_SNAPSHOT) - if (tracker_ != nullptr) { - BlockHookScope blockScope; - tracker_->MoveEvent(address, forwardAddress, size); - } -#endif -} - void Heap::SwapNewSpace() { activeSemiSpace_->Stop(); @@ -358,12 +337,15 @@ void Heap::ReclaimRegions(TriggerGCType gcType) region->ResetAliveObject(); region->ClearGCFlag(RegionGCFlags::IN_NEW_TO_NEW_SET); }); + size_t cachedSize = inactiveSemiSpace_->GetInitialCapacity(); if (gcType == TriggerGCType::FULL_GC) { compressSpace_->Reset(); + cachedSize = 0; } else if (gcType == TriggerGCType::OLD_GC) { oldSpace_->ReclaimCSet(); } - inactiveSemiSpace_->ReclaimRegions(); + + inactiveSemiSpace_->ReclaimRegions(cachedSize); sweeper_->WaitAllTaskFinished(); EnumerateNonNewSpaceRegionsWithRecord([] (Region *region) { @@ -371,7 +353,7 @@ void Heap::ReclaimRegions(TriggerGCType gcType) region->ClearCrossRegionRSet(); }); if (!clearTaskFinished_) { - os::memory::LockHolder holder(waitClearTaskFinishedMutex_); + LockHolder holder(waitClearTaskFinishedMutex_); clearTaskFinished_ = true; waitClearTaskFinishedCV_.SignalAll(); } @@ -407,9 +389,9 @@ size_t Heap::GetHeapObjectSize() const return result; } -int32_t Heap::GetHeapObjectCount() const +uint32_t Heap::GetHeapObjectCount() const { - int32_t count = 0; + uint32_t count = 0; sweeper_->EnsureAllTaskFinished(); this->IterateOverObjects([&count]([[maybe_unused]] TaggedObject *obj) { ++count; diff --git a/ecmascript/mem/heap.cpp b/ecmascript/mem/heap.cpp index cf584bf2867c8819e0ab40617510744f50f62977..40b7b29a84022885e0e4c9601e03f9c83a1d155f 100644 --- a/ecmascript/mem/heap.cpp +++ b/ecmascript/mem/heap.cpp @@ -37,7 +37,10 @@ #include "ecmascript/mem/gc_stats.h" #include "ecmascript/ecma_string_table.h" #include "ecmascript/runtime_call_id.h" - +#if !WIN_OR_MAC_OR_IOS_PLATFORM +#include "ecmascript/dfx/hprof/heap_profiler_interface.h" +#include "ecmascript/dfx/hprof/heap_profiler.h" +#endif #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER) #include "ecmascript/dfx/cpu_profiler/cpu_profiler.h" #endif @@ -329,7 +332,8 @@ TriggerGCType Heap::SelectGCType() const return YOUNG_GC; } if (!OldSpaceExceedLimit() && !OldSpaceExceedCapacity(activeSemiSpace_->GetCommittedSize()) && - GetHeapObjectSize() <= globalSpaceAllocLimit_ && !GlobalNativeSizeLargerThanLimit()) { + GetHeapObjectSize() <= globalSpaceAllocLimit_ + oldSpace_->GetOvershootSize() && + !GlobalNativeSizeLargerThanLimit()) { return YOUNG_GC; } return OLD_GC; @@ -346,6 +350,7 @@ void Heap::CollectGarbage(TriggerGCType gcType, GCReason reason) CHECK_NO_GC #if ECMASCRIPT_ENABLE_HEAP_VERIFY + LOG_ECMA(DEBUG) << "Enable heap verify"; isVerifying_ = true; // pre gc heap verify sweeper_->EnsureAllTaskFinished(); @@ -362,8 +367,13 @@ void Heap::CollectGarbage(TriggerGCType gcType, GCReason reason) if (fullGCRequested_ && thread_->IsReadyToMark() && gcType != TriggerGCType::FULL_GC) { gcType = TriggerGCType::FULL_GC; } + if (oldGCRequested_ && gcType != TriggerGCType::FULL_GC) { + gcType = TriggerGCType::OLD_GC; + } + oldGCRequested_ = false; + oldSpace_->AdjustOvershootSize(); + size_t originalNewSpaceSize = activeSemiSpace_->GetHeapObjectSize(); - size_t originalNewSpaceNativeSize = activeSemiSpace_->GetNativeBindingSize(); memController_->StartCalculationBeforeGC(); StatisticHeapObject(gcType); if (!GetJSThread()->IsReadyToMark() && markType_ == MarkType::MARK_FULL) { @@ -371,15 +381,27 @@ void Heap::CollectGarbage(TriggerGCType gcType, GCReason reason) } else { ecmaVm_->GetEcmaGCStats()->RecordStatisticBeforeGC(gcType, reason); } + gcType_ = gcType; + GetEcmaVM()->GetPGOProfiler()->PausePGODump(); switch (gcType) { case TriggerGCType::YOUNG_GC: // Use partial GC for young generation. if (!concurrentMarker_->IsEnabled() && !incrementalMarker_->IsTriggeredIncrementalMark()) { SetMarkType(MarkType::MARK_YOUNG); } + if (concurrentMarker_->IsEnabled() && markType_ == MarkType::MARK_FULL) { + // gcType_ must be sure. Functions ProcessNativeReferences need to use it. + gcType_ = TriggerGCType::OLD_GC; + } partialGC_->RunPhases(); break; - case TriggerGCType::OLD_GC: + case TriggerGCType::OLD_GC: { + bool fullConcurrentMarkRequested = false; + // Check whether it's needed to trigger full concurrent mark instead of trigger old gc + if (concurrentMarker_->IsEnabled() && (thread_->IsReadyToMark() || markType_ == MarkType::MARK_YOUNG) && + reason == GCReason::ALLOCATION_LIMIT) { + fullConcurrentMarkRequested = true; + } if (concurrentMarker_->IsEnabled() && markType_ == MarkType::MARK_YOUNG) { // Wait for existing concurrent marking tasks to be finished (if any), // and reset concurrent marker's status for full mark. @@ -389,8 +411,17 @@ void Heap::CollectGarbage(TriggerGCType gcType, GCReason reason) } } SetMarkType(MarkType::MARK_FULL); + if (fullConcurrentMarkRequested && idleTask_ == IdleTaskType::NO_TASK) { + LOG_ECMA(INFO) << "Trigger old gc here may cost long time, trigger full concurrent mark instead"; + oldSpace_->SetOvershootSize(GetEcmaVM()->GetEcmaParamConfiguration().GetOldSpaceOvershootSize()); + TriggerConcurrentMarking(); + oldGCRequested_ = true; + return; + } partialGC_->RunPhases(); + CheckNonMovableSpaceOOM(); break; + } case TriggerGCType::FULL_GC: fullGC_->SetForAppSpawn(false); fullGC_->RunPhases(); @@ -407,6 +438,7 @@ void Heap::CollectGarbage(TriggerGCType gcType, GCReason reason) UNREACHABLE(); break; } + GetEcmaVM()->GetPGOProfiler()->ResumePGODump(); // OOMError object is not allowed to be allocated during gc process, so throw OOMError after gc if (shouldThrowOOMError_) { @@ -419,7 +451,6 @@ void Heap::CollectGarbage(TriggerGCType gcType, GCReason reason) // Adjust the old space capacity and global limit for the first partial GC with full mark. // Trigger the full mark next time if the current survival rate is much less than half the average survival rates. AdjustBySurvivalRate(originalNewSpaceSize); - activeSemiSpace_->AdjustNativeLimit(originalNewSpaceNativeSize); memController_->StopCalculationAfterGC(gcType); if (gcType == TriggerGCType::FULL_GC || IsFullMark()) { // Only when the gc type is not semiGC and after the old space sweeping has been finished, @@ -454,11 +485,16 @@ void Heap::CollectGarbage(TriggerGCType gcType, GCReason reason) JSFinalizationRegistry::CheckAndCall(thread_); } -void Heap::ThrowOutOfMemoryError(size_t size, std::string functionName) +void Heap::ThrowOutOfMemoryError(size_t size, std::string functionName, bool NonMovableObjNearOOM) { GetEcmaVM()->GetEcmaGCStats()->PrintGCMemoryStatistic(); std::ostringstream oss; - oss << "OutOfMemory when trying to allocate " << size << " bytes" << " function name: " << functionName.c_str(); + if (NonMovableObjNearOOM) { + oss << "OutOfMemory when nonmovable live obj size: " << size << " bytes" + << " function name: " << functionName.c_str(); + } else { + oss << "OutOfMemory when trying to allocate " << size << " bytes" << " function name: " << functionName.c_str(); + } LOG_ECMA_MEM(ERROR) << oss.str().c_str(); THROW_OOM_ERROR(thread_, oss.str().c_str()); } @@ -575,6 +611,29 @@ void Heap::AdjustOldSpaceLimit() << " globalSpaceAllocLimit_: " << globalSpaceAllocLimit_; } +void Heap::OnAllocateEvent([[maybe_unused]] TaggedObject* address, [[maybe_unused]] size_t size) +{ +#if defined(ECMASCRIPT_SUPPORT_HEAPPROFILER) + HeapProfilerInterface *profiler = GetEcmaVM()->GetHeapProfile(); + if (profiler != nullptr) { + BlockHookScope blockScope; + profiler->AllocationEvent(address, size); + } +#endif +} + +void Heap::OnMoveEvent([[maybe_unused]] uintptr_t address, [[maybe_unused]] TaggedObject* forwardAddress, + [[maybe_unused]] size_t size) +{ +#if defined(ECMASCRIPT_SUPPORT_HEAPPROFILER) + HeapProfilerInterface *profiler = GetEcmaVM()->GetHeapProfile(); + if (profiler != nullptr) { + BlockHookScope blockScope; + profiler->MoveEvent(address, forwardAddress, size); + } +#endif +} + void Heap::AddToKeptObjects(JSHandle value) const { JSHandle env = ecmaVm_->GetGlobalEnv(); @@ -662,12 +721,22 @@ void Heap::RecomputeLimits() } } -void Heap::CheckAndTriggerOldGC(size_t size) +bool Heap::CheckAndTriggerOldGC(size_t size) { - if (OldSpaceExceedLimit() || OldSpaceExceedCapacity(size) || GetHeapObjectSize() > globalSpaceAllocLimit_ || - GlobalNativeSizeLargerThanLimit()) { + bool isFullMarking = IsFullMark() && GetJSThread()->IsMarking(); + bool isNativeSizeLargeTrigger = isFullMarking ? false : GlobalNativeSizeLargerThanLimit(); + if (isFullMarking && oldSpace_->GetOvershootSize() == 0) { + oldSpace_->SetOvershootSize(GetEcmaVM()->GetEcmaParamConfiguration().GetOldSpaceOvershootSize()); + } + if ((isNativeSizeLargeTrigger || OldSpaceExceedLimit() || OldSpaceExceedCapacity(size) || + GetHeapObjectSize() > globalSpaceAllocLimit_ + oldSpace_->GetOvershootSize()) && + !NeedStopCollection()) { CollectGarbage(TriggerGCType::OLD_GC, GCReason::ALLOCATION_LIMIT); + if (!oldGCRequested_) { + return true; + } } + return false; } bool Heap::CheckOngoingConcurrentMarking() @@ -714,7 +783,7 @@ void Heap::TryTriggerIdleCollection() double newSpaceMarkDuration = activeSemiSpace_->GetHeapObjectSize() / newSpaceConcurrentMarkSpeed; double newSpaceRemainSize = (newSpaceAllocToLimitDuration - newSpaceMarkDuration) * newSpaceAllocSpeed; // 2 means double - if (newSpaceRemainSize < 2 * DEFAULT_REGION_SIZE || activeSemiSpace_->NativeBindingSizeLargerThanLimit()) { + if (newSpaceRemainSize < 2 * DEFAULT_REGION_SIZE) { SetIdleTask(IdleTaskType::YOUNG_GC); SetMarkType(MarkType::MARK_YOUNG); EnableNotifyIdle(); @@ -787,8 +856,7 @@ void Heap::TryTriggerIncrementalMarking() double oldSpaceRemainSize = (oldSpaceAllocToLimitDuration - oldSpaceMarkDuration) * oldSpaceAllocSpeed; // mark finished before allocate limit - if ((oldSpaceRemainSize > 0 && oldSpaceRemainSize < DEFAULT_REGION_SIZE) || - GetHeapObjectSize() >= globalSpaceAllocLimit_) { + if ((oldSpaceRemainSize < DEFAULT_REGION_SIZE) || GetHeapObjectSize() >= globalSpaceAllocLimit_) { // The object allocated in incremental marking should lower than limit, // otherwise select trigger concurrent mark. size_t allocateSize = oldSpaceAllocSpeed * oldSpaceMarkDuration; @@ -820,7 +888,6 @@ void Heap::TryTriggerConcurrentMarking() TriggerConcurrentMarking(); return; } - bool isFullMarkNeeded = false; double oldSpaceMarkDuration = 0, newSpaceMarkDuration = 0, newSpaceRemainSize = 0, newSpaceAllocToLimitDuration = 0, oldSpaceAllocToLimitDuration = 0; double oldSpaceAllocSpeed = memController_->GetOldSpaceAllocationThroughputPerMS(); @@ -839,14 +906,20 @@ void Heap::TryTriggerConcurrentMarking() } else { if (oldSpaceHeapObjectSize >= oldSpaceAllocLimit || globalHeapObjectSize >= globalSpaceAllocLimit_ || GlobalNativeSizeLargerThanLimit()) { - isFullMarkNeeded = true; + markType_ = MarkType::MARK_FULL; + TriggerConcurrentMarking(); + OPTIONAL_LOG(ecmaVm_, INFO) << "Trigger full mark"; + return; } oldSpaceAllocToLimitDuration = (oldSpaceAllocLimit - oldSpaceHeapObjectSize) / oldSpaceAllocSpeed; oldSpaceMarkDuration = GetHeapObjectSize() / oldSpaceConcurrentMarkSpeed; // oldSpaceRemainSize means the predicted size which can be allocated after the full concurrent mark. double oldSpaceRemainSize = (oldSpaceAllocToLimitDuration - oldSpaceMarkDuration) * oldSpaceAllocSpeed; if (oldSpaceRemainSize > 0 && oldSpaceRemainSize < DEFAULT_REGION_SIZE) { - isFullMarkNeeded = true; + markType_ = MarkType::MARK_FULL; + TriggerConcurrentMarking(); + OPTIONAL_LOG(ecmaVm_, INFO) << "Trigger full mark"; + return; } } @@ -867,21 +940,7 @@ void Heap::TryTriggerConcurrentMarking() // newSpaceRemainSize means the predicted size which can be allocated after the semi concurrent mark. newSpaceRemainSize = (newSpaceAllocToLimitDuration - newSpaceMarkDuration) * newSpaceAllocSpeed; - if (isFullMarkNeeded) { - if (oldSpaceMarkDuration < newSpaceAllocToLimitDuration && - oldSpaceMarkDuration < oldSpaceAllocToLimitDuration) { - markType_ = MarkType::MARK_FULL; - TriggerConcurrentMarking(); - OPTIONAL_LOG(ecmaVm_, INFO) << "Trigger full mark by speed"; - } else { - if (oldSpaceHeapObjectSize >= oldSpaceAllocLimit || globalHeapObjectSize >= globalSpaceAllocLimit_ || - GlobalNativeSizeLargerThanLimit()) { - markType_ = MarkType::MARK_FULL; - TriggerConcurrentMarking(); - OPTIONAL_LOG(ecmaVm_, INFO) << "Trigger full mark by limit"; - } - } - } else if (newSpaceRemainSize < DEFAULT_REGION_SIZE || activeSemiSpace_->NativeBindingSizeLargerThanLimit()) { + if (newSpaceRemainSize < DEFAULT_REGION_SIZE) { markType_ = MarkType::MARK_YOUNG; TriggerConcurrentMarking(); OPTIONAL_LOG(ecmaVm_, INFO) << "Trigger semi mark"; @@ -894,24 +953,15 @@ void Heap::IncreaseNativeBindingSize(JSNativePointer *object) if (size == 0) { return; } - Region *region = Region::ObjectAddressToRange(reinterpret_cast(object)); - if (region->InYoungSpace()) { - activeSemiSpace_->IncreaseNativeBindingSize(size); - } else { - nonNewSpaceNativeBindingSize_ += size; - } + nativeBindingSize_ += size; } -void Heap::IncreaseNativeBindingSize(bool nonMovable, size_t size) +void Heap::IncreaseNativeBindingSize(size_t size) { if (size == 0) { return; } - if (!nonMovable) { - activeSemiSpace_->IncreaseNativeBindingSize(size); - } else { - nonNewSpaceNativeBindingSize_ += size; - } + nativeBindingSize_ += size; } void Heap::PrepareRecordRegionsForReclaim() @@ -926,9 +976,10 @@ void Heap::PrepareRecordRegionsForReclaim() void Heap::TriggerConcurrentMarking() { + ASSERT(idleTask_ != IdleTaskType::INCREMENTAL_MARK); if (idleTask_ == IdleTaskType::YOUNG_GC && IsFullMark()) { - SetMarkType(MarkType::MARK_YOUNG); - return; + ClearIdleTask(); + DisableNotifyIdle(); } if (concurrentMarker_->IsEnabled() && !fullGCRequested_ && ConcurrentMarker::TryIncreaseTaskCounts()) { concurrentMarker_->Mark(); @@ -937,7 +988,7 @@ void Heap::TriggerConcurrentMarking() void Heap::WaitRunningTaskFinished() { - os::memory::LockHolder holder(waitTaskFinishedMutex_); + LockHolder holder(waitTaskFinishedMutex_); while (runningTaskCount_ > 0) { waitTaskFinishedCV_.Wait(&waitTaskFinishedMutex_); } @@ -945,7 +996,7 @@ void Heap::WaitRunningTaskFinished() void Heap::WaitClearTaskFinished() { - os::memory::LockHolder holder(waitClearTaskFinishedMutex_); + LockHolder holder(waitClearTaskFinishedMutex_); while (!clearTaskFinished_) { waitClearTaskFinishedCV_.Wait(&waitClearTaskFinishedMutex_); } @@ -975,12 +1026,13 @@ void Heap::PostParallelGCTask(ParallelGCTaskPhase gcTask) void Heap::IncreaseTaskCount() { - os::memory::LockHolder holder(waitTaskFinishedMutex_); + LockHolder holder(waitTaskFinishedMutex_); runningTaskCount_++; } void Heap::ChangeGCParams(bool inBackground) { + inBackground_ = inBackground; if (inBackground) { LOG_GC(INFO) << "app is inBackground"; if (GetHeapObjectSize() - heapAliveSizeAfterGC_ > BACKGROUND_GROW_LIMIT) { @@ -994,6 +1046,7 @@ void Heap::ChangeGCParams(bool inBackground) sweeper_->EnableConcurrentSweep(EnableConcurrentSweepType::DISABLE); maxMarkTaskCount_ = 1; maxEvacuateTaskCount_ = 1; + Taskpool::GetCurrentTaskpool()->SetThreadPriority(false); } else { LOG_GC(INFO) << "app is not inBackground"; if (GetMemGrowingType() != MemGrowingType::PRESSURE) { @@ -1005,6 +1058,7 @@ void Heap::ChangeGCParams(bool inBackground) maxMarkTaskCount_ = std::min(ecmaVm_->GetJSOptions().GetGcThreadNum(), Taskpool::GetCurrentTaskpool()->GetTotalThreadNum() - 1); maxEvacuateTaskCount_ = Taskpool::GetCurrentTaskpool()->GetTotalThreadNum(); + Taskpool::GetCurrentTaskpool()->SetThreadPriority(true); } } @@ -1027,7 +1081,7 @@ void Heap::TriggerIdleCollection(int idleMicroSec) return; } - if (idleMicroSec < idlePredictDuration_ && idlePredictDuration_ < IDLE_TIME_LIMIT) { + if (idleMicroSec < idlePredictDuration_ && idleMicroSec < IDLE_TIME_LIMIT) { return; } @@ -1063,15 +1117,83 @@ void Heap::NotifyMemoryPressure(bool inHighMemoryPressure) } } +void Heap::NotifyFinishColdStart(bool isMainThread) +{ + { + LockHolder holder(finishColdStartMutex_); + if (!onStartupEvent_) { + return; + } + + // set overshoot size to increase gc threashold larger 8MB than current heap size. + int64_t semiRemainSize = GetNewSpace()->GetInitialCapacity() - GetNewSpace()->GetCommittedSize(); + int64_t overshootSize = GetEcmaVM()->GetEcmaParamConfiguration().GetOldSpaceOvershootSize() - semiRemainSize; + // overshoot size should be larger than 0. + GetNewSpace()->SetOverShootSize(std::max(overshootSize, (int64_t)0)); + GetNewSpace()->SetWaterLineWithoutGC(); + onStartupEvent_ = false; + } + + if (isMainThread) { + markType_ = MarkType::MARK_FULL; + TriggerConcurrentMarking(); + } +} + +void Heap::NotifyFinishColdStartSoon() +{ + if (!onStartupEvent_) { + return; + } + + // post 2s task + Taskpool::GetCurrentTaskpool()->PostTask( + std::make_unique(GetJSThread()->GetThreadId(), this)); +} + +void Heap::NotifyHighSensitive(bool isStart) +{ + onHighSensitiveEvent_ = isStart; + if (!onHighSensitiveEvent_ && !onStartupEvent_) { + // set overshoot size to increase gc threashold larger 8MB than current heap size. + int64_t semiRemainSize = GetNewSpace()->GetInitialCapacity() - GetNewSpace()->GetCommittedSize(); + int64_t overshootSize = GetEcmaVM()->GetEcmaParamConfiguration().GetOldSpaceOvershootSize() - semiRemainSize; + // overshoot size should be larger than 0. + GetNewSpace()->SetOverShootSize(std::max(overshootSize, (int64_t)0)); + GetNewSpace()->SetWaterLineWithoutGC(); + + TryTriggerIncrementalMarking(); + TryTriggerIdleCollection(); + TryTriggerConcurrentMarking(); + } +} + +bool Heap::NeedStopCollection() +{ + if (!InSensitiveStatus()) { + return false; + } + + if (GetHeapObjectSize() < ecmaVm_->GetEcmaParamConfiguration().GetMaxHeapSize() - + ecmaVm_->GetEcmaParamConfiguration().GetOldSpaceOvershootSize()) { + return true; + } + + GetNewSpace()->SetOverShootSize( + GetNewSpace()->GetCommittedSize() - GetNewSpace()->GetInitialCapacity() + + ecmaVm_->GetEcmaParamConfiguration().GetOldSpaceOvershootSize()); + return false; +} + bool Heap::CheckCanDistributeTask() { - os::memory::LockHolder holder(waitTaskFinishedMutex_); + LockHolder holder(waitTaskFinishedMutex_); return runningTaskCount_ < maxMarkTaskCount_; } void Heap::ReduceTaskCount() { - os::memory::LockHolder holder(waitTaskFinishedMutex_); + LockHolder holder(waitTaskFinishedMutex_); runningTaskCount_--; if (runningTaskCount_ == 0) { waitTaskFinishedCV_.SignalAll(); @@ -1080,6 +1202,8 @@ void Heap::ReduceTaskCount() bool Heap::ParallelGCTask::Run(uint32_t threadIndex) { + // Synchronizes-with. Ensure that WorkManager::Initialize must be seen by MarkerThreads. + while (!heap_->GetWorkManager()->HasInitialized()); switch (taskPhase_) { case ParallelGCTaskPhase::SEMI_HANDLE_THREAD_ROOTS_TASK: heap_->GetSemiGCMarker()->MarkRoots(threadIndex); @@ -1116,6 +1240,13 @@ bool Heap::AsyncClearTask::Run([[maybe_unused]] uint32_t threadIndex) return true; } +bool Heap::FinishColdStartTask::Run([[maybe_unused]] uint32_t threadIndex) +{ + usleep(2000000); // 2000000 means 2s + heap_->NotifyFinishColdStart(false); + return true; +} + size_t Heap::GetArrayBufferSize() const { size_t result = 0; diff --git a/ecmascript/mem/heap.h b/ecmascript/mem/heap.h index f44a3ac030b2d8d10c4254574db072c715babd5c..641bcbfde472880066726a26ceb093372d3e78fa 100644 --- a/ecmascript/mem/heap.h +++ b/ecmascript/mem/heap.h @@ -32,6 +32,10 @@ class EcmaVM; class FullGC; class HeapRegionAllocator; class HeapTracker; +#if !WIN_OR_MAC_OR_IOS_PLATFORM +class HeapProfilerInterface; +class HeapProfiler; +#endif class IncrementalMarker; class JSNativePointer; class Marker; @@ -220,6 +224,17 @@ public: return memController_; } + bool InSensitiveStatus() const + { + return onHighSensitiveEvent_ || onStartupEvent_; + } + + void NotifyPostFork() + { + LockHolder holder(finishColdStartMutex_); + onStartupEvent_ = true; + } + /* * For object allocations. */ @@ -263,8 +278,8 @@ public: void CollectGarbage(TriggerGCType gcType, GCReason reason = GCReason::OTHER); - void CheckAndTriggerOldGC(size_t size = 0); - + bool CheckAndTriggerOldGC(size_t size = 0); + TriggerGCType SelectGCType() const; /* * Parallel GC related configurations and utilities. */ @@ -356,7 +371,7 @@ public: inline size_t GetHeapObjectSize() const; - inline int32_t GetHeapObjectCount() const; + inline uint32_t GetHeapObjectCount() const; size_t GetPromotedSize() const { @@ -401,26 +416,32 @@ public: void ClearIdleTask(); - /* - * Heap tracking will be used by tools like heap profiler etc. - */ + bool IsEmptyIdleTask() const + { + return idleTask_ == IdleTaskType::NO_TASK; + } + + void NotifyFinishColdStart(bool isMainThread = true); + + void NotifyFinishColdStartSoon(); - void StartHeapTracking(HeapTracker *tracker) + void NotifyHighSensitive(bool isStart); + + bool NeedStopCollection(); + +#if defined(ECMASCRIPT_SUPPORT_HEAPPROFILER) + void StartHeapTracking() { WaitAllTasksFinished(); - parallelGC_ = false; - tracker_ = tracker; } void StopHeapTracking() { WaitAllTasksFinished(); - parallelGC_ = true; - tracker_ = nullptr; } - - inline void OnAllocateEvent(TaggedObject* address, size_t size); - inline void OnMoveEvent(uintptr_t address, TaggedObject* forwardAddress, size_t size); +#endif + void OnAllocateEvent(TaggedObject* address, size_t size); + void OnMoveEvent(uintptr_t address, TaggedObject* forwardAddress, size_t size); void AddToKeptObjects(JSHandle value) const; void ClearKeptObjects() const; @@ -448,13 +469,14 @@ public: bool OldSpaceExceedCapacity(size_t size) const { size_t totalSize = oldSpace_->GetCommittedSize() + hugeObjectSpace_->GetCommittedSize() + size; - return totalSize >= oldSpace_->GetMaximumCapacity() + oldSpace_->GetOutOfMemoryOvershootSize(); + return totalSize >= oldSpace_->GetMaximumCapacity() + oldSpace_->GetOvershootSize() + + oldSpace_->GetOutOfMemoryOvershootSize(); } bool OldSpaceExceedLimit() const { size_t totalSize = oldSpace_->GetHeapObjectSize() + hugeObjectSpace_->GetHeapObjectSize(); - return totalSize >= oldSpace_->GetInitialCapacity(); + return totalSize >= oldSpace_->GetInitialCapacity() + oldSpace_->GetOvershootSize(); } void AdjustSpaceSizeForAppSpawn(); @@ -489,19 +511,18 @@ public: mode_ = mode; } - void ThrowOutOfMemoryError(size_t size, std::string functionName); + void ThrowOutOfMemoryError(size_t size, std::string functionName, bool NonMovableObjNearOOM = false); - void IncreaseNativeBindingSize(bool nonMovable, size_t size); + void IncreaseNativeBindingSize(size_t size); void IncreaseNativeBindingSize(JSNativePointer *object); void ResetNativeBindingSize() { - activeSemiSpace_->ResetNativeBindingSize(); - nonNewSpaceNativeBindingSize_ = 0; + nativeBindingSize_ = 0; } size_t GetNativeBindingSize() const { - return activeSemiSpace_->GetNativeBindingSize() + nonNewSpaceNativeBindingSize_; + return nativeBindingSize_; } size_t GetGlobalNativeSize() const @@ -514,11 +535,6 @@ public: return GetGlobalNativeSize() >= globalSpaceNativeLimit_; } - size_t GetNonNewSpaceNativeBindingSize() const - { - return nonNewSpaceNativeBindingSize_; - } - void NotifyHeapAliveSizeAfterGC(size_t size) { heapAliveSizeAfterGC_ = size; @@ -528,8 +544,31 @@ public: { return heapAliveSizeAfterGC_; } + + bool IsInBackground() const + { + return inBackground_; + } + + bool IsYoungGC() const + { + return gcType_ == TriggerGCType::YOUNG_GC; + } + + bool GetOldGCRequested() + { + return oldGCRequested_; + } + + void CheckNonMovableSpaceOOM() + { + if (nonMovableSpace_->GetHeapObjectSize() > MAX_NONMOVABLE_LIVE_OBJ_SIZE) { + ThrowOutOfMemoryError(nonMovableSpace_->GetHeapObjectSize(), "Heap::CheckNonMovableSpaceOOM", true); + } + } + private: - static constexpr int IDLE_TIME_LIMIT = 15; // if idle time over 15ms we can do something + static constexpr int IDLE_TIME_LIMIT = 10; // if idle time over 10ms we can do something static constexpr int ALLOCATE_SIZE_LIMIT = 100_KB; static constexpr int IDLE_MAINTAIN_TIME = 500; static constexpr int BACKGROUND_GROW_LIMIT = 2_MB; @@ -538,13 +577,12 @@ private: void AdjustOldSpaceLimit(); // record lastRegion for each space, which will be used in ReclaimRegions() void PrepareRecordRegionsForReclaim(); - TriggerGCType SelectGCType() const; void IncreaseTaskCount(); void ReduceTaskCount(); void WaitClearTaskFinished(); void InvokeWeakNodeNativeFinalizeCallback(); inline void ReclaimRegions(TriggerGCType gcType); - + inline size_t CalculateCommittedCacheSize(); class ParallelGCTask : public Task { public: ParallelGCTask(int32_t id, Heap *heap, ParallelGCTaskPhase taskPhase) @@ -574,6 +612,19 @@ private: TriggerGCType gcType_; }; + class FinishColdStartTask : public Task { + public: + FinishColdStartTask(int32_t id, Heap *heap) + : Task(id), heap_(heap) {} + ~FinishColdStartTask() override = default; + bool Run(uint32_t threadIndex) override; + + NO_COPY_SEMANTIC(FinishColdStartTask); + NO_MOVE_SEMANTIC(FinishColdStartTask); + private: + Heap *heap_; + }; + EcmaVM *ecmaVm_ {nullptr}; JSThread *thread_ {nullptr}; @@ -646,6 +697,7 @@ private: bool parallelGC_ {true}; bool fullGCRequested_ {false}; + bool oldGCRequested_ {false}; bool fullMarkRequested_ {false}; bool oldSpaceLimitAdjusted_ {false}; bool shouldThrowOOMError_ {false}; @@ -656,20 +708,22 @@ private: size_t globalSpaceAllocLimit_ {0}; size_t promotedSize_ {0}; size_t semiSpaceCopiedSize_ {0}; - size_t nonNewSpaceNativeBindingSize_{0}; + size_t nativeBindingSize_{0}; size_t globalSpaceNativeLimit_ {0}; MemGrowingType memGrowingtype_ {MemGrowingType::HIGH_THROUGHPUT}; + TriggerGCType gcType_ {TriggerGCType::YOUNG_GC}; bool clearTaskFinished_ {true}; - os::memory::Mutex waitClearTaskFinishedMutex_; - os::memory::ConditionVariable waitClearTaskFinishedCV_; + Mutex waitClearTaskFinishedMutex_; + ConditionVariable waitClearTaskFinishedCV_; uint32_t runningTaskCount_ {0}; // parallel marker task number. uint32_t maxMarkTaskCount_ {0}; // parallel evacuator task number. uint32_t maxEvacuateTaskCount_ {0}; - os::memory::Mutex waitTaskFinishedMutex_; - os::memory::ConditionVariable waitTaskFinishedCV_; + Mutex finishColdStartMutex_; + Mutex waitTaskFinishedMutex_; + ConditionVariable waitTaskFinishedCV_; /* * The memory controller providing memory statistics (by allocations and coleections), @@ -681,10 +735,12 @@ private: NativeAreaAllocator *nativeAreaAllocator_ {nullptr}; HeapRegionAllocator *heapRegionAllocator_ {nullptr}; - // The tracker tracking heap object allocation and movement events. - HeapTracker *tracker_ {nullptr}; + // Application status + bool inBackground_ {false}; IdleNotifyStatusCallback notifyIdleStatusCallback {nullptr}; + bool onHighSensitiveEvent_ {false}; + bool onStartupEvent_ {false}; IdleTaskType idleTask_ {IdleTaskType::NO_TASK}; float idlePredictDuration_ {0.0f}; diff --git a/ecmascript/mem/heap_region_allocator.cpp b/ecmascript/mem/heap_region_allocator.cpp index 02752635f423ddc25696c241af94469d1515499f..4bf40c69bf19b1903a5485c1313a8956d8bdf648 100644 --- a/ecmascript/mem/heap_region_allocator.cpp +++ b/ecmascript/mem/heap_region_allocator.cpp @@ -58,7 +58,7 @@ Region *HeapRegionAllocator::AllocateAlignedRegion(Space *space, size_t capacity return new (ToVoidPtr(mem)) Region(thread, mem, begin, end, flags); } -void HeapRegionAllocator::FreeRegion(Region *region) +void HeapRegionAllocator::FreeRegion(Region *region, size_t cachedSize) { auto size = region->GetCapacity(); bool isRegular = !(region->InHugeObjectSpace()); @@ -73,6 +73,6 @@ void HeapRegionAllocator::FreeRegion(Region *region) UNREACHABLE(); } #endif - MemMapAllocator::GetInstance()->Free(ToVoidPtr(allocateBase), size, isRegular); + MemMapAllocator::GetInstance()->CacheOrFree(ToVoidPtr(allocateBase), size, isRegular, cachedSize); } } // namespace panda::ecmascript diff --git a/ecmascript/mem/heap_region_allocator.h b/ecmascript/mem/heap_region_allocator.h index 1925f8e91328dfde83763be9e09b4f95d50e6c4d..b45ecfd3ccd0b530cbe8d1f9243e79048418b6fc 100644 --- a/ecmascript/mem/heap_region_allocator.h +++ b/ecmascript/mem/heap_region_allocator.h @@ -31,7 +31,7 @@ public: virtual ~HeapRegionAllocator() = default; Region *AllocateAlignedRegion(Space *space, size_t capacity, JSThread* thread); - void FreeRegion(Region *region); + void FreeRegion(Region *region, size_t cachedSize); void IncreaseAnnoMemoryUsage(size_t bytes) { diff --git a/ecmascript/mem/incremental_marker.cpp b/ecmascript/mem/incremental_marker.cpp index 335a5aec5ba93b7da553a73687876cbc42efff98..0ca8149c50792a929dd6d95b9d5a7276c9cc67a4 100644 --- a/ecmascript/mem/incremental_marker.cpp +++ b/ecmascript/mem/incremental_marker.cpp @@ -21,6 +21,7 @@ #include "ecmascript/mem/allocator-inl.h" #include "ecmascript/mem/clock_scope.h" +#include "ecmascript/mem/concurrent_marker.h" #include "ecmascript/mem/heap-inl.h" #include "ecmascript/mem/mark_stack.h" #include "ecmascript/mem/mark_word.h" @@ -41,6 +42,7 @@ IncrementalMarker::IncrementalMarker(Heap *heap) void IncrementalMarker::TriggerIncrementalMark(int64_t idleMicroSec) { + ASSERT(!heap_->GetConcurrentMarker()->IsTriggeredConcurrentMark()); double startTime = GetCurrentTimeInMs(); switch (states_) { case IncrementalGCStates::ROOT_SCAN: diff --git a/ecmascript/mem/linear_space.cpp b/ecmascript/mem/linear_space.cpp index c2c8f59eec4f589c61f5086f5fc65cdaacda2eba..21bf97ee68cae22daf43a8c5643a6cd1f6b22713 100644 --- a/ecmascript/mem/linear_space.cpp +++ b/ecmascript/mem/linear_space.cpp @@ -20,13 +20,13 @@ #include "ecmascript/mem/allocator-inl.h" #include "ecmascript/mem/concurrent_marker.h" #include "ecmascript/mem/heap.h" +#include "ecmascript/mem/mem_controller.h" namespace panda::ecmascript { LinearSpace::LinearSpace(Heap *heap, MemSpaceType type, size_t initialCapacity, size_t maximumCapacity) : Space(heap->GetHeapRegionAllocator(), type, initialCapacity, maximumCapacity), heap_(heap), - waterLine_(0), - newSpaceNativeLimit_(initialCapacity) + waterLine_(0) { } @@ -42,20 +42,25 @@ uintptr_t LinearSpace::Allocate(size_t size, bool isPromoted) #endif return object; } - if (!isPromoted && heap_->GetConcurrentMarker()->IsConfigDisabled() && NativeBindingSizeLargerThanLimit()) { - // Native binding size is larger than limit. Trigger gc. - return object; - } if (Expand(isPromoted)) { - if (!isPromoted) { + if (!isPromoted && !heap_->NeedStopCollection()) { heap_->TryTriggerIncrementalMarking(); heap_->TryTriggerIdleCollection(); heap_->TryTriggerConcurrentMarking(); } object = allocator_.Allocate(size); - } else if (heap_->GetJSThread()->IsMarking()) { + } else if (heap_->GetJSThread()->IsMarking() || !heap_->IsEmptyIdleTask()) { // Temporary adjust semi space capacity - overShootSize_ = heap_->GetEcmaVM()->GetEcmaParamConfiguration().GetSemiSpaceOvershootSize(); + if (heap_->IsFullMark()) { + overShootSize_ = heap_->GetOldSpace()->GetMaximumCapacity() - heap_->GetOldSpace()->GetInitialCapacity(); + } else { + size_t stepOverShootSize = heap_->GetEcmaVM()->GetEcmaParamConfiguration().GetSemiSpaceStepOvershootSize(); + size_t maxOverShootSize = std::max(initialCapacity_ / 2, stepOverShootSize); // 2: half + if (overShootSize_ < maxOverShootSize) { + overShootSize_ += stepOverShootSize; + } + } + if (Expand(isPromoted)) { object = allocator_.Allocate(size); } @@ -70,7 +75,8 @@ uintptr_t LinearSpace::Allocate(size_t size, bool isPromoted) bool LinearSpace::Expand(bool isPromoted) { - if (committedSize_ >= initialCapacity_ + overShootSize_ + outOfMemoryOvershootSize_) { + if (committedSize_ >= initialCapacity_ + overShootSize_ + outOfMemoryOvershootSize_ && + !heap_->NeedStopCollection()) { return false; } @@ -179,13 +185,13 @@ void SemiSpace::Restart() uintptr_t SemiSpace::AllocateSync(size_t size) { - os::memory::LockHolder lock(lock_); + LockHolder lock(lock_); return Allocate(size, true); } bool SemiSpace::SwapRegion(Region *region, SemiSpace *fromSpace) { - os::memory::LockHolder lock(lock_); + LockHolder lock(lock_); if (committedSize_ + region->GetCapacity() > maximumCapacity_ + overShootSize_) { return false; } @@ -214,7 +220,27 @@ void SemiSpace::SetWaterLine() } }); survivalObjectSize_ += last->GetAllocatedBytes(waterLine_); + } else { + LOG_GC(INFO) << "SetWaterLine: No region survival in current gc, current region available size: " + << allocator_.Available(); + } +} + +void SemiSpace::SetWaterLineWithoutGC() +{ + waterLine_ = allocator_.GetTop(); + Region *last = GetCurrentRegion(); + if (last != nullptr) { + last->SetGCFlag(RegionGCFlags::HAS_AGE_MARK); + + EnumerateRegions([&last](Region *current) { + if (current != last) { + current->SetGCFlag(RegionGCFlags::BELOW_AGE_MARK); + } + }); + survivalObjectSize_ += allocateAfterLastGC_; } + allocateAfterLastGC_ = 0; } size_t SemiSpace::GetHeapObjectSize() const @@ -249,6 +275,10 @@ bool SemiSpace::AdjustCapacity(size_t allocatedSizeSinceGC) if (initialCapacity_ <= minimumCapacity_) { return false; } + double speed = heap_->GetMemController()->GetNewSpaceAllocationThroughputPerMS(); + if (speed > LOW_ALLOCATION_SPEED_PER_MS) { + return false; + } size_t newCapacity = initialCapacity_ / GROWING_FACTOR; SetInitialCapacity(std::max(newCapacity, minimumCapacity_)); return true; @@ -256,27 +286,6 @@ bool SemiSpace::AdjustCapacity(size_t allocatedSizeSinceGC) return false; } -void SemiSpace::AdjustNativeLimit(size_t previousNativeSize) -{ - if (newSpaceNativeBindingSize_ <= newSpaceNativeLimit_ * GROW_OBJECT_SURVIVAL_RATE / GROWING_FACTOR) { - return; - } - double curObjectSurvivalRate = static_cast(previousNativeSize) / newSpaceNativeBindingSize_; - if (curObjectSurvivalRate > GROW_OBJECT_SURVIVAL_RATE) { - if (newSpaceNativeLimit_ >= maximumCapacity_) { - return; - } - size_t newCapacity = newSpaceNativeLimit_ * GROWING_FACTOR; - newSpaceNativeLimit_ = std::min(newCapacity, maximumCapacity_); - } else if (curObjectSurvivalRate < SHRINK_OBJECT_SURVIVAL_RATE) { - if (newSpaceNativeLimit_ <= minimumCapacity_) { - return; - } - size_t newCapacity = newSpaceNativeLimit_ / GROWING_FACTOR; - newSpaceNativeLimit_ = std::max(newCapacity, minimumCapacity_); - } -} - size_t SemiSpace::GetAllocatedSizeSinceGC(uintptr_t top) const { size_t currentRegionSize = 0; diff --git a/ecmascript/mem/linear_space.h b/ecmascript/mem/linear_space.h index b2582461388d6370673d64d29618f8c127b45228..b85f834900025f1b76d2c00532724a0149bb95a9 100644 --- a/ecmascript/mem/linear_space.h +++ b/ecmascript/mem/linear_space.h @@ -41,23 +41,6 @@ public: { return allocator_.GetEndAddress(); } - void ResetNativeBindingSize() - { - newSpaceNativeBindingSize_ = 0; - } - void IncreaseNativeBindingSize(size_t size) - { - newSpaceNativeBindingSize_ += size; - } - size_t GetNativeBindingSize() - { - return newSpaceNativeBindingSize_; - } - - bool NativeBindingSizeLargerThanLimit() - { - return newSpaceNativeBindingSize_ > newSpaceNativeLimit_; - } void InvokeAllocationInspector(Address object, size_t size, size_t alignedSize); protected: @@ -68,8 +51,6 @@ protected: size_t allocateAfterLastGC_ {0}; size_t survivalObjectSize_ {0}; uintptr_t waterLine_ {0}; - size_t newSpaceNativeLimit_; - size_t newSpaceNativeBindingSize_ {0}; }; class SemiSpace : public LinearSpace { @@ -86,8 +67,8 @@ public: void SetOverShootSize(size_t size); bool AdjustCapacity(size_t allocatedSizeSinceGC); - void AdjustNativeLimit(size_t previousNativeSize); void SetWaterLine(); + void SetWaterLineWithoutGC(); uintptr_t GetWaterLine() const { @@ -105,7 +86,7 @@ public: private: static constexpr int GROWING_FACTOR = 2; - os::memory::Mutex lock_; + Mutex lock_; size_t minimumCapacity_; }; diff --git a/ecmascript/mem/mem.h b/ecmascript/mem/mem.h index 3a2b8fff542cb72aaf32f5258d51b94c31b1091e..63b04b39c561b5685c1f7a6b27656915a03ff72b 100644 --- a/ecmascript/mem/mem.h +++ b/ecmascript/mem/mem.h @@ -37,7 +37,7 @@ enum class MemAlignmentLog2 : uint8_t { MEM_ALIGN_REGION_LOG2 = 4, }; -static constexpr size_t MAX_HUGE_OBJECT_CAPACITY = 512_MB; +static constexpr size_t MAX_HUGE_OBJECT_CAPACITY = 1024_MB; static constexpr size_t LARGE_POOL_SIZE = 480_MB; static constexpr size_t MEDIUM_POOL_SIZE = 256_MB; static constexpr size_t LOW_POOL_SIZE = 64_MB; @@ -48,6 +48,8 @@ static constexpr size_t STANDARD_POOL_SIZE = WORKER_NUM * DEFAULT_WORKER_HEAP_SI static constexpr size_t MIN_OLD_SPACE_LIMIT = 2_MB; +static constexpr size_t MAX_NONMOVABLE_LIVE_OBJ_SIZE = 16_MB; + static constexpr size_t REGION_SIZE_LOG2 = 18U; static constexpr size_t MIN_HEAP_SIZE = 5_MB; @@ -60,7 +62,7 @@ static constexpr size_t DEFAULT_MARK_STACK_SIZE = 4_KB; static constexpr double MIN_OBJECT_SURVIVAL_RATE = 0.75; static constexpr double GROW_OBJECT_SURVIVAL_RATE = 0.8; static constexpr double SHRINK_OBJECT_SURVIVAL_RATE = 0.2; - +static constexpr double LOW_ALLOCATION_SPEED_PER_MS = 1000; // Objects which are larger than half of the region size are huge objects. // Regular objects will be allocated on regular regions and migrated on spaces. // They will never be moved to huge object space. So we take half of a regular @@ -72,6 +74,7 @@ static constexpr size_t CHUNK_ALIGN_SIZE = 4_KB; static constexpr size_t MIN_CHUNK_AREA_SIZE = 4_KB; static constexpr size_t MAX_CACHED_CHUNK_AREA_SIZE = 16_KB; static constexpr size_t MAX_CHUNK_AREA_SIZE = 1_MB; +static constexpr size_t MAX_REGEXP_CACHE_SIZE = 8_KB; // idle gc static constexpr size_t IDLE_GC_YOUNG_SPACE = 3_MB; diff --git a/ecmascript/mem/mem_map_allocator.cpp b/ecmascript/mem/mem_map_allocator.cpp index 9f990e495601218e798e96f5a1e6295992a46b88..e8751ff58cded5098741b4cc4e743425e8b8f194 100644 --- a/ecmascript/mem/mem_map_allocator.cpp +++ b/ecmascript/mem/mem_map_allocator.cpp @@ -33,6 +33,10 @@ MemMap MemMapAllocator::Allocate(size_t size, size_t alignment, bool regular, bo MemMap mem; if (regular) { + mem = memMapPool_.GetRegularMemFromCommitted(size); + if (mem.GetMem() != nullptr) { + return mem; + } mem = memMapPool_.GetMemFromCache(size); if (mem.GetMem() != nullptr) { memMapTotalSize_ += size; @@ -58,6 +62,24 @@ MemMap MemMapAllocator::Allocate(size_t size, size_t alignment, bool regular, bo return mem; } +void MemMapAllocator::CacheOrFree(void *mem, size_t size, bool isRegular, size_t cachedSize) +{ + if (isRegular && !memMapPool_.IsRegularCommittedFull(cachedSize)) { + // Cache regions to accelerate allocation. + memMapPool_.AddMemToCommittedCache(mem, size); + return; + } + Free(mem, size, isRegular); + if (isRegular && memMapPool_.ShouldFreeMore(cachedSize) > 0) { + int freeNum = memMapPool_.ShouldFreeMore(cachedSize); + for (int i = 0; i < freeNum; i++) { + void *freeMem = memMapPool_.GetRegularMemFromCommitted(size).GetMem(); + ASSERT(freeMem != nullptr); + Free(freeMem, size, isRegular); + } + } +} + void MemMapAllocator::Free(void *mem, size_t size, bool isRegular) { memMapTotalSize_ -= size; diff --git a/ecmascript/mem/mem_map_allocator.h b/ecmascript/mem/mem_map_allocator.h index 0fcb309182921e56aaa03bc997c27c5c1655c891..09bee58a3a682593b9cc6e7aaf862b23b96f5cde 100644 --- a/ecmascript/mem/mem_map_allocator.h +++ b/ecmascript/mem/mem_map_allocator.h @@ -25,7 +25,7 @@ #include "ecmascript/mem/mem_common.h" #include "ecmascript/log_wrapper.h" -#include "libpandabase/os/mutex.h" +#include "ecmascript/platform/mutex.h" namespace panda::ecmascript { // Regular region with length of DEFAULT_REGION_SIZE(256kb) @@ -36,10 +36,14 @@ public: void Finalize() { - os::memory::LockHolder lock(lock_); + LockHolder lock(lock_); for (auto &it : memMapVector_) { PageUnmap(it); } + for (auto &it : regularMapCommitted_) { + PageUnmap(it); + } + regularMapCommitted_.clear(); memMapVector_.clear(); memMapCache_.clear(); } @@ -50,7 +54,7 @@ public: MemMap GetMemFromCache([[maybe_unused]] size_t size) { ASSERT(size == REGULAR_MMAP_SIZE); - os::memory::LockHolder lock(lock_); + LockHolder lock(lock_); if (!memMapCache_.empty()) { MemMap mem = memMapCache_.front(); memMapCache_.pop_front(); @@ -59,16 +63,48 @@ public: return MemMap(); } + MemMap GetRegularMemFromCommitted([[maybe_unused]] size_t size) + { + ASSERT(size == REGULAR_MMAP_SIZE); + LockHolder lock(lock_); + if (!regularMapCommitted_.empty()) { + MemMap mem = regularMapCommitted_.back(); + regularMapCommitted_.pop_back(); + return mem; + } + return MemMap(); + } + + bool IsRegularCommittedFull(size_t cachedSize) { + LockHolder lock(lock_); + size_t size = regularMapCommitted_.size(); + return size > (cachedSize / REGULAR_MMAP_SIZE) ? true : false; + } + + int ShouldFreeMore(size_t cachedSize) { + LockHolder lock(lock_); + int result = regularMapCommitted_.size(); + return result - static_cast(cachedSize / REGULAR_MMAP_SIZE); + } + + void AddMemToCommittedCache(void *mem, size_t size) + { + ASSERT(size == REGULAR_MMAP_SIZE); + LockHolder lock(lock_); + regularMapCommitted_.emplace_back(mem, size); + } + + void AddMemToCache(void *mem, size_t size) { ASSERT(size == REGULAR_MMAP_SIZE); - os::memory::LockHolder lock(lock_); + LockHolder lock(lock_); memMapCache_.emplace_back(mem, size); } MemMap SplitMemFromCache(MemMap memMap) { - os::memory::LockHolder lock(lock_); + LockHolder lock(lock_); auto remainderMem = reinterpret_cast(memMap.GetMem()) + REGULAR_MMAP_SIZE; size_t remainderSize = AlignDown(memMap.GetSize() - REGULAR_MMAP_SIZE, REGULAR_MMAP_SIZE); size_t count = remainderSize / REGULAR_MMAP_SIZE; @@ -81,14 +117,15 @@ public: void InsertMemMap(MemMap memMap) { - os::memory::LockHolder lock(lock_); + LockHolder lock(lock_); memMapVector_.emplace_back(memMap); } private: static constexpr size_t REGULAR_MMAP_SIZE = 256_KB; - os::memory::Mutex lock_; + Mutex lock_; std::deque memMapCache_; + std::vector regularMapCommitted_; std::vector memMapVector_; }; @@ -146,7 +183,7 @@ public: LOG_GC(ERROR) << "Freelist pool oom: overflow(" << freeListPoolSize_ << ")"; return MemMap(); } - os::memory::LockHolder lock(lock_); + LockHolder lock(lock_); auto iterate = freeList_.lower_bound(size); if (iterate == freeList_.end()) { MergeList(); @@ -176,7 +213,7 @@ public: void AddMemToList(MemMap memMap) { - os::memory::LockHolder lock(lock_); + LockHolder lock(lock_); auto search = freeSet_.find(reinterpret_cast(memMap.GetMem())); if (UNLIKELY(search != freeSet_.end())) { freeSetPoolSize_ -= memMap.GetSize(); @@ -189,7 +226,7 @@ public: } private: - os::memory::Mutex lock_; + Mutex lock_; MemMap memMap_; std::multimap freeList_; std::set freeSet_; @@ -250,13 +287,13 @@ public: MemMap Allocate(size_t size, size_t alignment, bool regular, bool isMachineCode); - void Free(void *mem, size_t size, bool isRegular); + void CacheOrFree(void *mem, size_t size, bool isRegular, size_t cachedSize); private: static constexpr size_t REGULAR_REGION_MMAP_SIZE = 4_MB; void AdapterSuitablePoolCapacity(); - + void Free(void *mem, size_t size, bool isRegular); MemMapPool memMapPool_; MemMapFreeList memMapFreeList_; std::atomic_size_t memMapTotalSize_ {0}; diff --git a/ecmascript/mem/object_xray.h b/ecmascript/mem/object_xray.h index badfbaeef4cf182c832a776a907aa477e2f35a54..45b12e8118198f071640be00f8c57377363d8b94 100644 --- a/ecmascript/mem/object_xray.h +++ b/ecmascript/mem/object_xray.h @@ -22,6 +22,7 @@ #include "ecmascript/js_async_from_sync_iterator.h" #include "ecmascript/global_env.h" #include "ecmascript/ic/ic_handler.h" +#include "ecmascript/ic/profile_type_info.h" #include "ecmascript/ic/proto_change_details.h" #include "ecmascript/jobs/micro_job_queue.h" #include "ecmascript/jobs/pending_job.h" @@ -338,6 +339,9 @@ public: case JSType::TREE_STRING: TreeEcmaString::Cast(object)->VisitRangeSlot(visitor); break; + case JSType::SLICED_STRING: + SlicedString::Cast(object)->VisitRangeSlot(visitor); + break; case JSType::JS_NATIVE_POINTER: if (visitType == VisitType::SNAPSHOT_VISIT) { JSNativePointer::Cast(object)->VisitRangeSlotForNative(visitor); @@ -355,6 +359,9 @@ public: case JSType::CONSTANT_POOL: ConstantPool::Cast(object)->VisitRangeSlot(visitor); break; + case JSType::PROFILE_TYPE_INFO: + ProfileTypeInfo::Cast(object)->VisitRangeSlot(visitor); + break; case JSType::GLOBAL_ENV: GlobalEnv::Cast(object)->VisitRangeSlot(visitor); break; @@ -385,6 +392,11 @@ public: break; case JSType::PROTO_CHANGE_MARKER: break; + case JSType::MARKER_CELL: + break; + case JSType::TRACK_INFO: + TrackInfo::Cast(object)->VisitRangeSlot(visitor); + break; case JSType::PROTOTYPE_INFO: ProtoChangeDetails::Cast(object)->VisitRangeSlot(visitor); break; @@ -620,7 +632,7 @@ public: ClassLiteral::Cast(object)->VisitRangeSlot(visitor); break; default: - LOG_ECMA(FATAL) << "this branch is unreachable"; + LOG_ECMA(FATAL) << "this branch is unreachable, type: " << static_cast(type); UNREACHABLE(); } } diff --git a/ecmascript/mem/parallel_evacuator-inl.h b/ecmascript/mem/parallel_evacuator-inl.h index c8d54588e321c6380a00a17202fbd5fba6535038..d94effadf5159729aad65384b27ee3e446dfba2a 100644 --- a/ecmascript/mem/parallel_evacuator-inl.h +++ b/ecmascript/mem/parallel_evacuator-inl.h @@ -31,6 +31,30 @@ bool ParallelEvacuator::IsWholeRegionEvacuate(Region *region) !region->HasAgeMark(); } +template +bool ParallelEvacuator::VisitBodyInObj( + TaggedObject *root, ObjectSlot start, ObjectSlot end, Callback callback) +{ + auto hclass = root->GetClass(); + if (hclass->IsAllTaggedProp()) { + return false; + } + int index = 0; + for (ObjectSlot slot = start; slot < end; slot++) { + TaggedObject *dst = hclass->GetLayout().GetTaggedObject(); + MarkWord markWord(dst); + if (markWord.IsForwardingAddress()) { + dst = markWord.ToForwardingAddress(); + } + auto layout = LayoutInfo::Cast(dst); + auto attr = layout->GetAttr(index++); + if (attr.IsTaggedRep()) { + callback(slot); + } + } + return true; +} + bool ParallelEvacuator::UpdateOldToNewObjectSlot(ObjectSlot &slot) { JSTaggedValue value(slot.GetTaggedType()); @@ -120,28 +144,36 @@ void ParallelEvacuator::UpdateWeakObjectSlot(TaggedObject *value, ObjectSlot &sl void ParallelEvacuator::SetObjectFieldRSet(TaggedObject *object, JSHClass *cls) { Region *region = Region::ObjectAddressToRange(object); - auto callbackWithCSet = [region]([[maybe_unused]] TaggedObject *root, ObjectSlot start, ObjectSlot end, - [[maybe_unused]] bool isNative) { + auto callbackWithCSet = [this, region](TaggedObject *root, ObjectSlot start, ObjectSlot end, VisitObjectArea area) { + if (area == VisitObjectArea::IN_OBJECT) { + if (VisitBodyInObj(root, start, end, [&](ObjectSlot slot) { SetObjectRSet(slot, region); })) { + return; + }; + } for (ObjectSlot slot = start; slot < end; slot++) { - JSTaggedType value = slot.GetTaggedType(); - if (!JSTaggedValue(value).IsHeapObject()) { - continue; - } - Region *valueRegion = Region::ObjectAddressToRange(value); - if (valueRegion->InYoungSpace()) { - region->InsertOldToNewRSet(slot.SlotAddress()); - } else if (valueRegion->InCollectSet() || JSTaggedValue(value).IsWeakForHeapObject()) { - region->InsertCrossRegionRSet(slot.SlotAddress()); - } + SetObjectRSet(slot, region); } }; objXRay_.VisitObjectBody(object, cls, callbackWithCSet); } +void ParallelEvacuator::SetObjectRSet(ObjectSlot slot, Region *region) +{ + JSTaggedType value = slot.GetTaggedType(); + if (!JSTaggedValue(value).IsHeapObject()) { + return; + } + Region *valueRegion = Region::ObjectAddressToRange(value); + if (valueRegion->InYoungSpace()) { + region->InsertOldToNewRSet(slot.SlotAddress()); + } else if (valueRegion->InCollectSet() || JSTaggedValue(value).IsWeakForHeapObject()) { + region->InsertCrossRegionRSet(slot.SlotAddress()); + } +} std::unique_ptr ParallelEvacuator::GetWorkloadSafe() { - os::memory::LockHolder holder(mutex_); + LockHolder holder(mutex_); std::unique_ptr unit; if (!workloads_.empty()) { unit = std::move(workloads_.back()); diff --git a/ecmascript/mem/parallel_evacuator.cpp b/ecmascript/mem/parallel_evacuator.cpp index 6014dcf3d77384ac04a62db2b19cc7848ad7350f..a2ea8b6b88897a30dbc2fb92797ec6fc334dd41c 100644 --- a/ecmascript/mem/parallel_evacuator.cpp +++ b/ecmascript/mem/parallel_evacuator.cpp @@ -18,8 +18,9 @@ #include "ecmascript/js_hclass-inl.h" #include "ecmascript/mem/barriers-inl.h" #include "ecmascript/mem/clock_scope.h" +#include "ecmascript/mem/concurrent_sweeper.h" #include "ecmascript/mem/gc_bitset.h" -#include "ecmascript/mem/heap.h" +#include "ecmascript/mem/heap-inl.h" #include "ecmascript/mem/mem.h" #include "ecmascript/mem/space-inl.h" #include "ecmascript/mem/tlab_allocator-inl.h" @@ -64,7 +65,7 @@ void ParallelEvacuator::EvacuateSpace() AddWorkload(std::make_unique(this, current)); }); if (heap_->IsParallelGCEnabled()) { - os::memory::LockHolder holder(mutex_); + LockHolder holder(mutex_); parallel_ = CalculateEvacuationThreadNum(); for (int i = 0; i < parallel_; i++) { Taskpool::GetCurrentTaskpool()->PostTask( @@ -85,7 +86,7 @@ bool ParallelEvacuator::EvacuateSpace(TlabAllocator *allocator, bool isMain) } allocator->Finalize(); if (!isMain) { - os::memory::LockHolder holder(mutex_); + LockHolder holder(mutex_); if (--parallel_ <= 0) { condition_.SignalAll(); } @@ -149,27 +150,37 @@ void ParallelEvacuator::VerifyHeapObject(TaggedObject *object) { auto klass = object->GetClass(); objXRay_.VisitObjectBody(object, klass, - [&]([[maybe_unused]] TaggedObject *root, ObjectSlot start, ObjectSlot end, [[maybe_unused]] bool isNative) { + [&](TaggedObject *root, ObjectSlot start, ObjectSlot end, VisitObjectArea area) { + if (area == VisitObjectArea::IN_OBJECT) { + if (VisitBodyInObj(root, start, end, [&](ObjectSlot slot) { VerifyValue(object, slot); })) { + return; + }; + } for (ObjectSlot slot = start; slot < end; slot++) { - JSTaggedValue value(slot.GetTaggedType()); - if (value.IsHeapObject()) { - if (value.IsWeakForHeapObject()) { - continue; - } - Region *objectRegion = Region::ObjectAddressToRange(value.GetTaggedObject()); - if (!heap_->IsFullMark() && !objectRegion->InYoungSpace()) { - continue; - } - if (!objectRegion->Test(value.GetTaggedObject()) && !objectRegion->InAppSpawnSpace()) { - LOG_GC(FATAL) << "Miss mark value: " << value.GetTaggedObject() - << ", body address:" << slot.SlotAddress() - << ", header address:" << object; - } - } + VerifyValue(object, slot); } }); } +void ParallelEvacuator::VerifyValue(TaggedObject *object, ObjectSlot slot) +{ + JSTaggedValue value(slot.GetTaggedType()); + if (value.IsHeapObject()) { + if (value.IsWeakForHeapObject()) { + return; + } + Region *objectRegion = Region::ObjectAddressToRange(value.GetTaggedObject()); + if (!heap_->IsFullMark() && !objectRegion->InYoungSpace()) { + return; + } + if (!objectRegion->Test(value.GetTaggedObject()) && !objectRegion->InAppSpawnSpace()) { + LOG_GC(FATAL) << "Miss mark value: " << value.GetTaggedObject() + << ", body address:" << slot.SlotAddress() + << ", header address:" << object; + } + } +} + void ParallelEvacuator::UpdateReference() { TRACE_GC(GCStats::Scope::ScopeId::UpdateReference, heap_->GetEcmaVM()->GetEcmaGCStats()); @@ -203,7 +214,7 @@ void ParallelEvacuator::UpdateReference() << "old space region count:" << oldRegionCount; if (heap_->IsParallelGCEnabled()) { - os::memory::LockHolder holder(mutex_); + LockHolder holder(mutex_); parallel_ = CalculateUpdateThreadNum(); for (int i = 0; i < parallel_; i++) { Taskpool::GetCurrentTaskpool()->PostTask( @@ -266,6 +277,10 @@ void ParallelEvacuator::UpdateWeakReference() bool isFullMark = heap_->IsFullMark(); WeakRootVisitor gcUpdateWeak = [isFullMark](TaggedObject *header) { Region *objectRegion = Region::ObjectAddressToRange(reinterpret_cast(header)); + if (!objectRegion) { + LOG_GC(ERROR) << "PartialGC updateWeakReference: region is nullptr, header is " << header; + return reinterpret_cast(ToUintPtr(nullptr)); + } if (objectRegion->InYoungSpaceOrCSet()) { if (objectRegion->InNewToNewSet()) { if (objectRegion->Test(header)) { @@ -286,8 +301,11 @@ void ParallelEvacuator::UpdateWeakReference() } return header; }; + if (isFullMark) { + // Only old gc will sweep string table. + stringTable->SweepWeakReference(gcUpdateWeak); + } - stringTable->SweepWeakReference(gcUpdateWeak); heap_->GetEcmaVM()->GetJSThread()->IterateWeakEcmaGlobalStorage(gcUpdateWeak); heap_->GetEcmaVM()->ProcessReferences(gcUpdateWeak); } @@ -376,7 +394,12 @@ void ParallelEvacuator::UpdateAndSweepNewRegionReference(Region *region) void ParallelEvacuator::UpdateNewObjectField(TaggedObject *object, JSHClass *cls) { objXRay_.VisitObjectBody(object, cls, - [this]([[maybe_unused]] TaggedObject *root, ObjectSlot start, ObjectSlot end, [[maybe_unused]] bool isNative) { + [this](TaggedObject *root, ObjectSlot start, ObjectSlot end, VisitObjectArea area) { + if (area == VisitObjectArea::IN_OBJECT) { + if (VisitBodyInObj(root, start, end, [&](ObjectSlot slot) { UpdateObjectSlot(slot); })) { + return; + }; + } for (ObjectSlot slot = start; slot < end; slot++) { UpdateObjectSlot(slot); } @@ -387,7 +410,7 @@ void ParallelEvacuator::WaitFinished() { MEM_ALLOCATE_AND_GC_TRACE(heap_->GetEcmaVM(), WaitUpdateFinished); if (parallel_ > 0) { - os::memory::LockHolder holder(mutex_); + LockHolder holder(mutex_); while (parallel_ > 0) { condition_.Wait(&mutex_); } @@ -402,7 +425,7 @@ bool ParallelEvacuator::ProcessWorkloads(bool isMain) region = GetWorkloadSafe(); } if (!isMain) { - os::memory::LockHolder holder(mutex_); + LockHolder holder(mutex_); if (--parallel_ <= 0) { condition_.SignalAll(); } diff --git a/ecmascript/mem/parallel_evacuator.h b/ecmascript/mem/parallel_evacuator.h index 237ed41c459ef860bed35f15f0a92d07d3e1c154..6950a987aa51b26fbacdcf80011ff79d5dd1837c 100644 --- a/ecmascript/mem/parallel_evacuator.h +++ b/ecmascript/mem/parallel_evacuator.h @@ -28,7 +28,7 @@ #include "ecmascript/mem/tlab_allocator.h" #include "ecmascript/taskpool/task.h" -#include "libpandabase/os/mutex.h" +#include "ecmascript/platform/mutex.h" namespace panda::ecmascript { class ParallelEvacuator { @@ -127,8 +127,10 @@ private: bool EvacuateSpace(TlabAllocator *allocation, bool isMain = false); void EvacuateRegion(TlabAllocator *allocator, Region *region); inline void SetObjectFieldRSet(TaggedObject *object, JSHClass *cls); + inline void SetObjectRSet(ObjectSlot slot, Region *region); inline bool IsWholeRegionEvacuate(Region *region); + void VerifyValue(TaggedObject *object, ObjectSlot slot); void VerifyHeapObject(TaggedObject *object); void UpdateReference(); @@ -140,6 +142,8 @@ private: void UpdateAndSweepNewRegionReference(Region *region); void UpdateNewObjectField(TaggedObject *object, JSHClass *cls); + template + inline bool VisitBodyInObj(TaggedObject *root, ObjectSlot start, ObjectSlot end, Callback callback); inline bool UpdateOldToNewObjectSlot(ObjectSlot &slot); inline void UpdateObjectSlot(ObjectSlot &slot); inline void UpdateWeakObjectSlot(TaggedObject *object, ObjectSlot &slot); @@ -158,8 +162,8 @@ private: uintptr_t waterLine_ = 0; std::vector> workloads_; std::atomic_int parallel_ = 0; - os::memory::Mutex mutex_; - os::memory::ConditionVariable condition_; + Mutex mutex_; + ConditionVariable condition_; std::atomic promotedSize_ = 0; }; } // namespace panda::ecmascript diff --git a/ecmascript/mem/parallel_marker-inl.h b/ecmascript/mem/parallel_marker-inl.h index 574573ab9791d9bf610baeea77948458e3a81f64..f3d9e1b55a8406fd276159de9760e7da0386a930 100644 --- a/ecmascript/mem/parallel_marker-inl.h +++ b/ecmascript/mem/parallel_marker-inl.h @@ -27,6 +27,45 @@ namespace panda::ecmascript { constexpr size_t HEAD_SIZE = TaggedObject::TaggedObjectSize(); +template +inline bool NonMovableMarker::VisitBodyInObj(TaggedObject *root, ObjectSlot start, ObjectSlot end, Callback callback) +{ + auto hclass = root->SynchronizedGetClass(); + if (hclass->IsAllTaggedProp()) { + return false; + } + int index = 0; + for (ObjectSlot slot = start; slot < end; slot++) { + auto layout = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject()); + auto attr = layout->GetAttr(index++); + if (attr.IsTaggedRep()) { + callback(slot); + } + } + return true; +} + +inline void NonMovableMarker::MarkValue(uint32_t threadId, ObjectSlot &slot, Region *rootRegion, bool needBarrier) +{ + JSTaggedValue value(slot.GetTaggedType()); + if (value.IsHeapObject()) { + TaggedObject *obj = nullptr; + if (!value.IsWeakForHeapObject()) { + obj = value.GetTaggedObject(); + MarkObject(threadId, obj); + } else { + RecordWeakReference(threadId, reinterpret_cast(slot.SlotAddress()), rootRegion); + obj = value.GetWeakReferentUnChecked(); + } + if (needBarrier) { + Region *valueRegion = Region::ObjectAddressToRange(obj); + if (valueRegion->InCollectSet()) { + rootRegion->AtomicInsertCrossRegionRSet(slot.SlotAddress()); + } + } + } +} + inline void NonMovableMarker::MarkObject(uint32_t threadId, TaggedObject *object) { Region *objectRegion = Region::ObjectAddressToRange(object); @@ -75,21 +114,11 @@ inline void NonMovableMarker::HandleOldToNewRSet(uint32_t threadId, Region *regi ObjectSlot slot(ToUintPtr(mem)); JSTaggedValue value(slot.GetTaggedType()); if (value.IsHeapObject()) { - if (value.IsInvalidValue()) { - LOG_ECMA_MEM(INFO) << "HandleOldToNew found an invalid value: " << value.GetRawData() - << " " << slot.GetTaggedType(); - return true; - } if (value.IsWeakForHeapObject()) { RecordWeakReference(threadId, reinterpret_cast(mem), region); } else { MarkObject(threadId, value.GetTaggedObject()); } - if (value.GetRawData() != slot.GetTaggedType()) { - LOG_ECMA_MEM(INFO) << "HandleOldToNew mark an overdue value : " << value.GetRawData() << " " - << slot.GetTaggedType() << " " - << *reinterpret_cast(value.GetRawData()); - } } return true; }); @@ -104,6 +133,29 @@ inline void NonMovableMarker::RecordWeakReference(uint32_t threadId, JSTaggedTyp } } +template +inline bool MovableMarker::VisitBodyInObj(TaggedObject *root, ObjectSlot start, ObjectSlot end, Callback callback) +{ + auto hclass = root->GetClass(); + if (hclass->IsAllTaggedProp()) { + return false; + } + int index = 0; + for (ObjectSlot slot = start; slot < end; slot++) { + TaggedObject *dst = hclass->GetLayout().GetTaggedObject(); + MarkWord markWord(dst); + if (markWord.IsForwardingAddress()) { + dst = markWord.ToForwardingAddress(); + } + auto layout = LayoutInfo::Cast(dst); + auto attr = layout->GetAttr(index++); + if (attr.IsTaggedRep()) { + callback(slot); + } + } + return true; +} + inline void MovableMarker::HandleRoots(uint32_t threadId, [[maybe_unused]] Root type, ObjectSlot slot) { JSTaggedValue value(slot.GetTaggedType()); @@ -209,6 +261,23 @@ inline bool MovableMarker::UpdateForwardAddressIfFailed(TaggedObject *object, ui return Region::ObjectAddressToRange(dst)->InYoungSpace(); } +inline void SemiGCMarker::MarkValue(uint32_t threadId, TaggedObject *root, ObjectSlot slot) +{ + JSTaggedValue value(slot.GetTaggedType()); + if (value.IsHeapObject()) { + Region *rootRegion = Region::ObjectAddressToRange(root); + if (value.IsWeakForHeapObject()) { + RecordWeakReference(threadId, reinterpret_cast(slot.SlotAddress()), rootRegion); + return; + } + auto slotStatus = MarkObject(threadId, value.GetTaggedObject(), slot); + if (!rootRegion->InYoungSpace() && slotStatus == SlotStatus::KEEP_SLOT) { + SlotNeedUpdate waitUpdate(reinterpret_cast(root), slot); + workManager_->PushSlotNeedUpdate(threadId, waitUpdate); + } + } +} + inline SlotStatus SemiGCMarker::MarkObject(uint32_t threadId, TaggedObject *object, ObjectSlot slot) { Region *objectRegion = Region::ObjectAddressToRange(object); @@ -260,6 +329,19 @@ inline void SemiGCMarker::RecordWeakReference(uint32_t threadId, JSTaggedType *r } } +inline void CompressGCMarker::MarkValue(uint32_t threadId, ObjectSlot slot) +{ + JSTaggedValue value(slot.GetTaggedType()); + if (value.IsHeapObject()) { + if (value.IsWeakForHeapObject()) { + // It is unnecessary to use region pointer in compressGCMarker. + RecordWeakReference(threadId, reinterpret_cast(slot.SlotAddress())); + return; + } + MarkObject(threadId, value.GetTaggedObject(), slot); + } +} + inline SlotStatus CompressGCMarker::MarkObject(uint32_t threadId, TaggedObject *object, ObjectSlot slot) { Region *objectRegion = Region::ObjectAddressToRange(object); @@ -281,7 +363,7 @@ inline SlotStatus CompressGCMarker::MarkObject(uint32_t threadId, TaggedObject * inline uintptr_t CompressGCMarker::AllocateReadOnlySpace(size_t size) { - os::memory::LockHolder lock(mutex_); + LockHolder lock(mutex_); uintptr_t forwardAddress = heap_->GetReadOnlySpace()->Allocate(size); if (UNLIKELY(forwardAddress == 0)) { LOG_ECMA_MEM(FATAL) << "Evacuate Read only Object: alloc failed: " @@ -293,7 +375,7 @@ inline uintptr_t CompressGCMarker::AllocateReadOnlySpace(size_t size) inline uintptr_t CompressGCMarker::AllocateAppSpawnSpace(size_t size) { - os::memory::LockHolder lock(mutex_); + LockHolder lock(mutex_); uintptr_t forwardAddress = heap_->GetAppSpawnSpace()->Allocate(size); if (UNLIKELY(forwardAddress == 0)) { LOG_ECMA_MEM(FATAL) << "Evacuate AppSpawn Object: alloc failed: " diff --git a/ecmascript/mem/parallel_marker.cpp b/ecmascript/mem/parallel_marker.cpp index f6ac6bac0e0d35458ae2e3a87df04d42fc04e174..1919d0b56c4e17c6a7bb01febfad9caa6795b4af 100644 --- a/ecmascript/mem/parallel_marker.cpp +++ b/ecmascript/mem/parallel_marker.cpp @@ -39,6 +39,11 @@ void Marker::ProcessOldToNew(uint32_t threadId) ProcessMarkStack(threadId); } +void Marker::ProcessOldToNewNoMarkStack(uint32_t threadId) +{ + heap_->EnumerateOldSpaceRegions(std::bind(&Marker::HandleOldToNewRSet, this, threadId, std::placeholders::_1)); +} + void Marker::ProcessOldToNew(uint32_t threadId, Region *region) { heap_->EnumerateOldSpaceRegions(std::bind(&Marker::HandleOldToNewRSet, this, threadId, std::placeholders::_1), @@ -52,33 +57,28 @@ void Marker::ProcessSnapshotRSet(uint32_t threadId) ProcessMarkStack(threadId); } +void Marker::ProcessSnapshotRSetNoMarkStack(uint32_t threadId) +{ + heap_->EnumerateSnapshotSpaceRegions(std::bind(&Marker::HandleOldToNewRSet, this, threadId, std::placeholders::_1)); +} + void NonMovableMarker::ProcessMarkStack(uint32_t threadId) { TRACE_GC(GCStats::Scope::ScopeId::ProcessMarkStack, heap_->GetEcmaVM()->GetEcmaGCStats()); bool isFullMark = heap_->IsFullMark(); auto visitor = [this, threadId, isFullMark](TaggedObject *root, ObjectSlot start, ObjectSlot end, - [[maybe_unused]] bool isNative) { + VisitObjectArea area) { Region *rootRegion = Region::ObjectAddressToRange(root); bool needBarrier = isFullMark && !rootRegion->InYoungSpaceOrCSet(); - for (ObjectSlot slot = start; slot < end; slot++) { - JSTaggedValue value(slot.GetTaggedType()); - if (value.IsHeapObject()) { - TaggedObject *obj = nullptr; - if (!value.IsWeakForHeapObject()) { - obj = value.GetTaggedObject(); - MarkObject(threadId, obj); - } else { - RecordWeakReference(threadId, reinterpret_cast(slot.SlotAddress()), rootRegion); - obj = value.GetWeakReferentUnChecked(); - } - if (needBarrier) { - Region *valueRegion = Region::ObjectAddressToRange(obj); - if (valueRegion->InCollectSet()) { - rootRegion->AtomicInsertCrossRegionRSet(slot.SlotAddress()); - } - } + if (area == VisitObjectArea::IN_OBJECT) { + if (VisitBodyInObj(root, start, end, + [&](ObjectSlot slot) { MarkValue(threadId, slot, rootRegion, needBarrier); })) { + return; } } + for (ObjectSlot slot = start; slot < end; slot++) { + MarkValue(threadId, slot, rootRegion, needBarrier); + } }; TaggedObject *obj = nullptr; while (true) { @@ -87,7 +87,7 @@ void NonMovableMarker::ProcessMarkStack(uint32_t threadId) break; } - JSHClass *jsHclass = obj->GetClass(); + JSHClass *jsHclass = obj->SynchronizedGetClass(); MarkObject(threadId, jsHclass); objXRay_.VisitObjectBody(obj, jsHclass, visitor); } @@ -99,29 +99,19 @@ void NonMovableMarker::ProcessIncrementalMarkStack(uint32_t threadId, uint32_t m bool isFullMark = heap_->IsFullMark(); uint32_t visitAddrNum = 0; auto visitor = [this, threadId, isFullMark, &visitAddrNum](TaggedObject *root, ObjectSlot start, ObjectSlot end, - [[maybe_unused]] bool isNative) { + VisitObjectArea area) { Region *rootRegion = Region::ObjectAddressToRange(root); visitAddrNum += end.SlotAddress() - start.SlotAddress(); bool needBarrier = isFullMark && !rootRegion->InYoungSpaceOrCSet(); - for (ObjectSlot slot = start; slot < end; slot++) { - JSTaggedValue value(slot.GetTaggedType()); - if (value.IsHeapObject()) { - TaggedObject *obj = nullptr; - if (!value.IsWeakForHeapObject()) { - obj = value.GetTaggedObject(); - MarkObject(threadId, obj); - } else { - RecordWeakReference(threadId, reinterpret_cast(slot.SlotAddress()), rootRegion); - obj = value.GetWeakReferentUnChecked(); - } - if (needBarrier) { - Region *valueRegion = Region::ObjectAddressToRange(obj); - if (valueRegion->InCollectSet()) { - rootRegion->AtomicInsertCrossRegionRSet(slot.SlotAddress()); - } - } + if (area == VisitObjectArea::IN_OBJECT) { + if (VisitBodyInObj(root, start, end, + [&](ObjectSlot slot) { MarkValue(threadId, slot, rootRegion, needBarrier); })) { + return; } } + for (ObjectSlot slot = start; slot < end; slot++) { + MarkValue(threadId, slot, rootRegion, needBarrier); + } }; TaggedObject *obj = nullptr; double startTime = heap_->GetIncrementalMarker()->GetCurrentTimeInMs(); @@ -157,22 +147,15 @@ void SemiGCMarker::ProcessMarkStack(uint32_t threadId) { TRACE_GC(GCStats::Scope::ScopeId::ProcessMarkStack, heap_->GetEcmaVM()->GetEcmaGCStats()); auto visitor = [this, threadId](TaggedObject *root, ObjectSlot start, ObjectSlot end, - [[maybe_unused]] bool isNative) { - for (ObjectSlot slot = start; slot < end; slot++) { - JSTaggedValue value(slot.GetTaggedType()); - if (value.IsHeapObject()) { - Region *rootRegion = Region::ObjectAddressToRange(root); - if (value.IsWeakForHeapObject()) { - RecordWeakReference(threadId, reinterpret_cast(slot.SlotAddress()), rootRegion); - continue; - } - auto slotStatus = MarkObject(threadId, value.GetTaggedObject(), slot); - if (!rootRegion->InYoungSpace() && slotStatus == SlotStatus::KEEP_SLOT) { - SlotNeedUpdate waitUpdate(reinterpret_cast(root), slot); - workManager_->PushSlotNeedUpdate(threadId, waitUpdate); - } + VisitObjectArea area) { + if (area == VisitObjectArea::IN_OBJECT) { + if (VisitBodyInObj(root, start, end, [&](ObjectSlot slot) { MarkValue(threadId, root, slot); })) { + return; } } + for (ObjectSlot slot = start; slot < end; slot++) { + MarkValue(threadId, root, slot); + } }; TaggedObject *obj = nullptr; while (true) { @@ -189,19 +172,16 @@ void SemiGCMarker::ProcessMarkStack(uint32_t threadId) void CompressGCMarker::ProcessMarkStack(uint32_t threadId) { TRACE_GC(GCStats::Scope::ScopeId::ProcessMarkStack, heap_->GetEcmaVM()->GetEcmaGCStats()); - auto visitor = [this, threadId]([[maybe_unused]] TaggedObject *root, ObjectSlot start, ObjectSlot end, - [[maybe_unused]] bool isNative) { - for (ObjectSlot slot = start; slot < end; slot++) { - JSTaggedValue value(slot.GetTaggedType()); - if (value.IsHeapObject()) { - if (value.IsWeakForHeapObject()) { - // It is unnecessary to use region pointer in compressGCMarker. - RecordWeakReference(threadId, reinterpret_cast(slot.SlotAddress())); - continue; - } - MarkObject(threadId, value.GetTaggedObject(), slot); + auto visitor = [this, threadId](TaggedObject *root, ObjectSlot start, ObjectSlot end, + VisitObjectArea area) { + if (area == VisitObjectArea::IN_OBJECT) { + if (VisitBodyInObj(root, start, end, [&](ObjectSlot slot) { MarkValue(threadId, slot); })) { + return; } } + for (ObjectSlot slot = start; slot < end; slot++) { + MarkValue(threadId, slot); + } }; TaggedObject *obj = nullptr; while (true) { diff --git a/ecmascript/mem/parallel_marker.h b/ecmascript/mem/parallel_marker.h index ad0e0ba0b1e338aa9d1778ee75d44ee16ca61cfd..560448bd240ddf42278eb23d68da0856ee996d7d 100644 --- a/ecmascript/mem/parallel_marker.h +++ b/ecmascript/mem/parallel_marker.h @@ -41,8 +41,10 @@ public: void MarkRoots(uint32_t threadId); void ProcessOldToNew(uint32_t threadId); // for HPPGC only semi mode + void ProcessOldToNewNoMarkStack(uint32_t threadId); void ProcessOldToNew(uint32_t threadId, Region *region); // for SemiGC void ProcessSnapshotRSet(uint32_t threadId); // for SemiGC + void ProcessSnapshotRSetNoMarkStack(uint32_t threadId); virtual void ProcessMarkStack([[maybe_unused]] uint32_t threadId) { @@ -93,6 +95,9 @@ public: protected: void ProcessMarkStack(uint32_t threadId) override; + template + inline bool VisitBodyInObj(TaggedObject *root, ObjectSlot start, ObjectSlot end, Callback callback); + inline void MarkValue(uint32_t threadId, ObjectSlot &slot, Region *rootRegion, bool needBarrier); inline void MarkObject(uint32_t threadId, TaggedObject *object) override; inline void HandleRoots(uint32_t threadId, [[maybe_unused]] Root type, ObjectSlot slot) override; inline void HandleRangeRoots(uint32_t threadId, [[maybe_unused]] Root type, ObjectSlot start, @@ -111,6 +116,8 @@ public: ~MovableMarker() override = default; protected: + template + inline bool VisitBodyInObj(TaggedObject *root, ObjectSlot start, ObjectSlot end, Callback callback); inline void HandleRoots(uint32_t threadId, [[maybe_unused]] Root type, ObjectSlot slot) override; inline void HandleRangeRoots(uint32_t threadId, [[maybe_unused]] Root type, ObjectSlot start, ObjectSlot end) override; @@ -137,6 +144,7 @@ public: protected: void ProcessMarkStack(uint32_t threadId) override; + inline void MarkValue(uint32_t threadId, TaggedObject *root, ObjectSlot slot); inline SlotStatus MarkObject(uint32_t threadId, TaggedObject *object, ObjectSlot slot) override; inline SlotStatus EvacuateObject(uint32_t threadId, TaggedObject *object, const MarkWord &markWord, ObjectSlot slot) override; @@ -161,6 +169,7 @@ public: protected: void ProcessMarkStack(uint32_t threadId) override; + inline void MarkValue(uint32_t threadId, ObjectSlot slot); inline SlotStatus MarkObject(uint32_t threadId, TaggedObject *object, ObjectSlot slot) override; inline SlotStatus EvacuateObject(uint32_t threadId, TaggedObject *object, const MarkWord &markWord, @@ -172,7 +181,7 @@ protected: private: bool isAppSpawn_ {false}; - os::memory::Mutex mutex_; + Mutex mutex_; }; } // namespace panda::ecmascript #endif // ECMASCRIPT_MEM_PARALLEL_MARKER_H diff --git a/ecmascript/mem/regexp_cached_chunk.cpp b/ecmascript/mem/regexp_cached_chunk.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4ad0d2748db4ef2a89043be7d156b79573a800c9 --- /dev/null +++ b/ecmascript/mem/regexp_cached_chunk.cpp @@ -0,0 +1,29 @@ +/* + * 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/mem/regexp_cached_chunk.h" + +#include "ecmascript/mem/heap.h" + +namespace panda::ecmascript { + +RegExpCachedChunk::RegExpCachedChunk(JSThread *JSThread) : jsThread_(JSThread) +{ + allocator_ = jsThread_->GetNativeAreaAllocator(); + currentArea_ = jsThread_->GetOrCreateRegExpCache(); + ptr_ = currentArea_->GetBegin(); + end_ = currentArea_->GetEnd(); +} +} // namespace panda::ecmascript diff --git a/ecmascript/mem/regexp_cached_chunk.h b/ecmascript/mem/regexp_cached_chunk.h new file mode 100644 index 0000000000000000000000000000000000000000..6b7b3457ab0f9c1aca629f5da0e5cc3bcf3c1f00 --- /dev/null +++ b/ecmascript/mem/regexp_cached_chunk.h @@ -0,0 +1,39 @@ +/* + * 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_MEM_REGEXP_CACHED_CHUNK_H +#define ECMASCRIPT_MEM_REGEXP_CACHED_CHUNK_H + +#include "ecmascript/common.h" +#include "ecmascript/log_wrapper.h" +#include "ecmascript/mem/ecma_list.h" +#include "ecmascript/mem/area.h" +#include "ecmascript/mem/chunk.h" + +namespace panda::ecmascript { +class NativeAreaAllocator; +class RegExpCachedChunk : public Chunk { +public: + + explicit RegExpCachedChunk(JSThread *JSThread); + + NO_COPY_SEMANTIC(RegExpCachedChunk); + NO_MOVE_SEMANTIC(RegExpCachedChunk); + +private: + JSThread *jsThread_ ; +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_MEM_REGEXP_CACHED_CHUNK_H \ No newline at end of file diff --git a/ecmascript/mem/region-inl.h b/ecmascript/mem/region-inl.h index aced3be8038ea16e671a80743b96bd0fe47d0363..7ba95c13e780340746ff403c960f362940e7efd6 100644 --- a/ecmascript/mem/region-inl.h +++ b/ecmascript/mem/region-inl.h @@ -35,7 +35,7 @@ inline RememberedSet *Region::CreateRememberedSet() inline RememberedSet *Region::GetOrCreateCrossRegionRememberedSet() { if (UNLIKELY(crossRegionSet_ == nullptr)) { - os::memory::LockHolder lock(*lock_); + LockHolder lock(*lock_); if (crossRegionSet_ == nullptr) { crossRegionSet_ = CreateRememberedSet(); } @@ -46,7 +46,7 @@ inline RememberedSet *Region::GetOrCreateCrossRegionRememberedSet() inline RememberedSet *Region::GetOrCreateOldToNewRememberedSet() { if (UNLIKELY(packedData_.oldToNewSet_ == nullptr)) { - os::memory::LockHolder lock(*lock_); + LockHolder lock(*lock_); if (packedData_.oldToNewSet_ == nullptr) { if (sweepingRSet_ != nullptr && IsGCFlagSet(RegionGCFlags::HAS_BEEN_SWEPT)) { packedData_.oldToNewSet_ = sweepingRSet_; diff --git a/ecmascript/mem/region.h b/ecmascript/mem/region.h index 72e991e8920fb9d2eb1b46532a78719fa43d18e4..00c630a8cd7f554d9611cfb4a52922d1dfd90998 100644 --- a/ecmascript/mem/region.h +++ b/ecmascript/mem/region.h @@ -25,7 +25,7 @@ #include "ecmascript/mem/mem_common.h" #include "ecmascript/platform/map.h" -#include "libpandabase/os/mutex.h" +#include "ecmascript/platform/mutex.h" #include "securec.h" @@ -109,7 +109,7 @@ public: wasted_(0), snapshotData_(0) { - lock_ = new os::memory::Mutex(); + lock_ = new Mutex(); } ~Region() = default; @@ -593,7 +593,7 @@ private: RememberedSet *crossRegionSet_ {nullptr}; RememberedSet *sweepingRSet_ {nullptr}; Span freeObjectSets_; - os::memory::Mutex *lock_ {nullptr}; + Mutex *lock_ {nullptr}; uint64_t wasted_; // snapshotdata_ is used to encode the region for snapshot. Its upper 32 bits are used to store the size of // the huge object, and the lower 32 bits are used to store the region index diff --git a/ecmascript/mem/space-inl.h b/ecmascript/mem/space-inl.h index 6c14402cb18ce57e2e040a3f04bace5638684a01..602ee28a9819da63ed425914c59a5037184387a5 100644 --- a/ecmascript/mem/space-inl.h +++ b/ecmascript/mem/space-inl.h @@ -38,6 +38,7 @@ void Space::RemoveRegion(Region *region) template void Space::EnumerateRegions(const Callback &cb, Region *end) const { + LOG_ECMA_MEM(DEBUG) << "EnumerateRegions with " << ToSpaceTypeName(spaceType_) << " space"; Region *current = regionList_.GetFirst(); if (current == nullptr) { return; @@ -59,7 +60,9 @@ void Space::EnumerateRegions(const Callback &cb, Region *end) const template void Space::EnumerateRegionsWithRecord(const Callback &cb) const { - EnumerateRegions(cb, recordRegion_); + if (recordRegion_ != nullptr) { + EnumerateRegions(cb, recordRegion_); + } } RegionSpaceFlag Space::GetRegionFlag() const diff --git a/ecmascript/mem/space.cpp b/ecmascript/mem/space.cpp index bdc4e38bebeac1fba30880f172bb00438908066a..d9c3a989dfe4d6d69068fae94920f0deae05e53d 100644 --- a/ecmascript/mem/space.cpp +++ b/ecmascript/mem/space.cpp @@ -53,14 +53,14 @@ void Space::Destroy() ReclaimRegions(); } -void Space::ReclaimRegions() +void Space::ReclaimRegions(size_t cachedSize) { - EnumerateRegions([this](Region *current) { ClearAndFreeRegion(current); }); + EnumerateRegions([this, &cachedSize](Region *current) { ClearAndFreeRegion(current, cachedSize); }); regionList_.Clear(); committedSize_ = 0; } -void Space::ClearAndFreeRegion(Region *region) +void Space::ClearAndFreeRegion(Region *region, size_t cachedSize) { LOG_ECMA_MEM(DEBUG) << "Clear region from:" << region << " to " << ToSpaceTypeName(spaceType_); region->DeleteCrossRegionRSet(); @@ -73,7 +73,7 @@ void Space::ClearAndFreeRegion(Region *region) spaceType_ == MemSpaceType::APPSPAWN_SPACE) { region->DestroyFreeObjectSets(); } - heapRegionAllocator_->FreeRegion(region); + heapRegionAllocator_->FreeRegion(region, cachedSize); } HugeObjectSpace::HugeObjectSpace(Heap *heap, HeapRegionAllocator *heapRegionAllocator, diff --git a/ecmascript/mem/space.h b/ecmascript/mem/space.h index 59cb1bf644a117c627568ed01ea7c5539ae20c55..01166906dbb0f282b585227097650c020f3017aa 100644 --- a/ecmascript/mem/space.h +++ b/ecmascript/mem/space.h @@ -206,10 +206,10 @@ public: virtual void Initialize() {}; void Destroy(); - void ReclaimRegions(); + void ReclaimRegions(size_t cachedSize = 0); protected: - void ClearAndFreeRegion(Region *region); + void ClearAndFreeRegion(Region *region, size_t cachedSize = 0); HeapRegionAllocator *heapRegionAllocator_ {nullptr}; EcmaList regionList_ {}; diff --git a/ecmascript/mem/sparse_space.cpp b/ecmascript/mem/sparse_space.cpp index ed0acde2434e3d082d44d88c4efa3dfb553de703..de15e11c476f4a0847beb374992291a6717ad01d 100644 --- a/ecmascript/mem/sparse_space.cpp +++ b/ecmascript/mem/sparse_space.cpp @@ -16,9 +16,10 @@ #include "ecmascript/mem/sparse_space.h" #include "ecmascript/js_hclass-inl.h" +#include "ecmascript/mem/allocator-inl.h" #include "ecmascript/mem/concurrent_sweeper.h" #include "ecmascript/mem/free_object_set.h" -#include "ecmascript/mem/heap.h" +#include "ecmascript/mem/heap-inl.h" #include "ecmascript/mem/mem_controller.h" #include "ecmascript/runtime_call_id.h" @@ -59,19 +60,19 @@ uintptr_t SparseSpace::Allocate(size_t size, bool allowGC) CHECK_OBJECT_AND_INC_OBJ_SIZE(size); } - if (allowGC) { - // Check whether it is necessary to trigger Old GC before expanding to avoid OOM risk. - heap_->CheckAndTriggerOldGC(); + // Check whether it is necessary to trigger Old GC before expanding to avoid OOM risk. + if (allowGC && heap_->CheckAndTriggerOldGC()) { + object = allocator_->Allocate(size); + CHECK_OBJECT_AND_INC_OBJ_SIZE(size); } if (Expand()) { object = allocator_->Allocate(size); CHECK_OBJECT_AND_INC_OBJ_SIZE(size); - return object; } if (allowGC) { - heap_->CollectGarbage(TriggerGCType::OLD_GC, GCReason::ALLOCATION_LIMIT); + heap_->CollectGarbage(TriggerGCType::OLD_GC, GCReason::ALLOCATION_FAILED); object = Allocate(size, false); // Size is already increment } @@ -80,7 +81,7 @@ uintptr_t SparseSpace::Allocate(size_t size, bool allowGC) bool SparseSpace::Expand() { - if (committedSize_ >= maximumCapacity_ + outOfMemoryOvershootSize_) { + if (committedSize_ >= maximumCapacity_ + overshootSize_ + outOfMemoryOvershootSize_) { LOG_ECMA_MEM(INFO) << "Expand::Committed size " << committedSize_ << " of Sparse Space is too big. "; return false; } @@ -188,7 +189,7 @@ void SparseSpace::SortSweepingRegion() Region *SparseSpace::GetSweepingRegionSafe() { - os::memory::LockHolder holder(lock_); + LockHolder holder(lock_); Region *region = nullptr; if (!sweepingList_.empty()) { region = sweepingList_.back(); @@ -199,13 +200,13 @@ Region *SparseSpace::GetSweepingRegionSafe() void SparseSpace::AddSweptRegionSafe(Region *region) { - os::memory::LockHolder holder(lock_); + LockHolder holder(lock_); sweptList_.emplace_back(region); } Region *SparseSpace::GetSweptRegionSafe() { - os::memory::LockHolder holder(lock_); + LockHolder holder(lock_); Region *region = nullptr; if (!sweptList_.empty()) { region = sweptList_.back(); @@ -230,7 +231,7 @@ Region *SparseSpace::TryToGetSuitableSweptRegion(size_t size) if (sweptList_.empty()) { return nullptr; } - os::memory::LockHolder holder(lock_); + LockHolder holder(lock_); for (auto iter = sweptList_.begin(); iter != sweptList_.end(); iter++) { if (allocator_->MatchFreeObjectSet(*iter, size)) { Region *region = *iter; @@ -372,7 +373,7 @@ Region *OldSpace::TrySweepToGetSuitableRegion(size_t size) Region *OldSpace::TryToGetExclusiveRegion(size_t size) { - os::memory::LockHolder lock(lock_); + LockHolder lock(lock_); uintptr_t result = allocator_->LookupSuitableFreeObject(size); if (result != 0) { // Remove region from global old space @@ -396,7 +397,7 @@ Region *OldSpace::TryToGetExclusiveRegion(size_t size) void OldSpace::Merge(LocalSpace *localSpace) { localSpace->FreeBumpPoint(); - os::memory::LockHolder lock(lock_); + LockHolder lock(lock_); size_t oldCommittedSize = committedSize_; localSpace->EnumerateRegions([&](Region *region) { localSpace->DetachFreeObjectSet(region); @@ -426,11 +427,22 @@ void OldSpace::SelectCSet() } CheckRegionSize(); // 1、Select region which alive object larger than limit - EnumerateRegions([this](Region *region) { - if (region->BelowCompressThreasholdAlive() || !region->MostObjectAlive()) { - collectRegionSet_.emplace_back(region); - } - }); + int64_t evacuateSizeLimit = 0; + if (!heap_->IsInBackground()) { + evacuateSizeLimit = PARTIAL_GC_MAX_EVACUATION_SIZE_FOREGROUND; + EnumerateRegions([this](Region *region) { + if (!region->MostObjectAlive()) { + collectRegionSet_.emplace_back(region); + } + }); + } else { + evacuateSizeLimit = PARTIAL_GC_MAX_EVACUATION_SIZE_BACKGROUND; + EnumerateRegions([this](Region *region) { + if (region->BelowCompressThreasholdAlive() || !region->MostObjectAlive()) { + collectRegionSet_.emplace_back(region); + } + }); + } if (collectRegionSet_.size() < PARTIAL_GC_MIN_COLLECT_REGION_SIZE) { LOG_ECMA_MEM(DEBUG) << "Select CSet failure: number is too few"; collectRegionSet_.clear(); @@ -438,13 +450,13 @@ void OldSpace::SelectCSet() } // sort std::sort(collectRegionSet_.begin(), collectRegionSet_.end(), [](Region *first, Region *second) { - return first->GetGCAliveSize() < second->GetGCAliveSize(); + return first->AliveObject() < second->AliveObject(); }); // Limit cset size unsigned long selectedRegionNumber = 0; int64_t expectFreeSize = static_cast(heap_->GetCommittedSize() - heap_->GetHeapAliveSizeAfterGC()); - int64_t evacuateSize = std::min(PARTIAL_GC_MAX_EVACUATION_SIZE, expectFreeSize); + int64_t evacuateSize = std::min(evacuateSizeLimit, expectFreeSize); EnumerateCollectRegionSet([&](Region *current) { if (evacuateSize > 0) { selectedRegionNumber++; @@ -502,12 +514,13 @@ void OldSpace::RevertCSet() void OldSpace::ReclaimCSet() { - EnumerateCollectRegionSet([this](Region *region) { + size_t cachedSize = heap_->GetNewSpace()->GetInitialCapacity(); + EnumerateCollectRegionSet([this, &cachedSize](Region *region) { region->DeleteCrossRegionRSet(); region->DeleteOldToNewRSet(); region->DeleteSweepingRSet(); region->DestroyFreeObjectSets(); - heapRegionAllocator_->FreeRegion(region); + heapRegionAllocator_->FreeRegion(region, cachedSize); }); collectRegionSet_.clear(); } @@ -539,6 +552,15 @@ void LocalSpace::Stop() } } +uintptr_t NonMovableSpace::CheckAndAllocate(size_t size) +{ + if (maximumCapacity_ == committedSize_ && GetHeapObjectSize() > MAX_NONMOVABLE_LIVE_OBJ_SIZE && + !heap_->GetOldGCRequested()) { + heap_->CollectGarbage(TriggerGCType::OLD_GC, GCReason::ALLOCATION_LIMIT); + } + return Allocate(size); +} + NonMovableSpace::NonMovableSpace(Heap *heap, size_t initialCapacity, size_t maximumCapacity) : SparseSpace(heap, MemSpaceType::NON_MOVABLE, initialCapacity, maximumCapacity) { diff --git a/ecmascript/mem/sparse_space.h b/ecmascript/mem/sparse_space.h index fe9b390931d9e10ecae5bcfd7bd2d12b0e77db5e..e4ada4b58d12d5feca3aea4931009f97811a892b 100644 --- a/ecmascript/mem/sparse_space.h +++ b/ecmascript/mem/sparse_space.h @@ -105,6 +105,24 @@ public: liveObjectSize_ -= size; } + void SetOvershootSize(size_t size) + { + overshootSize_ = size; + } + + size_t GetOvershootSize() + { + return overshootSize_; + } + + void AdjustOvershootSize() + { + if (overshootSize_ > 0 && maximumCapacity_ > committedSize_) { + size_t size = maximumCapacity_ - committedSize_; + overshootSize_ = overshootSize_ > size ? overshootSize_ - size : 0; + } + } + size_t GetTotalAllocatedSize() const; void InvokeAllocationInspector(Address object, size_t size, size_t alignedSize); @@ -118,10 +136,11 @@ private: // For sweeping uintptr_t AllocateAfterSweepingCompleted(size_t size); - os::memory::Mutex lock_; + Mutex lock_; std::vector sweepingList_; std::vector sweptList_; size_t liveObjectSize_ {0}; + size_t overshootSize_ {0}; }; class OldSpace : public SparseSpace { @@ -172,13 +191,14 @@ public: void Merge(LocalSpace *localSpace); private: - static constexpr int64_t PARTIAL_GC_MAX_EVACUATION_SIZE = 6_MB; + static constexpr int64_t PARTIAL_GC_MAX_EVACUATION_SIZE_FOREGROUND = 2_MB; + static constexpr int64_t PARTIAL_GC_MAX_EVACUATION_SIZE_BACKGROUND = 6_MB; static constexpr unsigned long long PARTIAL_GC_MAX_COLLECT_REGION_RATE = 2_MB; static constexpr unsigned long long PARTIAL_GC_INITIAL_COLLECT_REGION_SIZE = 24; static constexpr size_t PARTIAL_GC_MIN_COLLECT_REGION_SIZE = 5; CVector collectRegionSet_; - os::memory::Mutex lock_; + Mutex lock_; size_t mergeSize_ {0}; }; @@ -188,6 +208,8 @@ public: ~NonMovableSpace() override = default; NO_COPY_SEMANTIC(NonMovableSpace); NO_MOVE_SEMANTIC(NonMovableSpace); + + uintptr_t CheckAndAllocate(size_t size); }; class AppSpawnSpace : public SparseSpace { diff --git a/ecmascript/mem/stw_young_gc.h b/ecmascript/mem/stw_young_gc.h index e5db94661c351701d87532bd2d16bec0db85aa0d..b5bde4c90084af37dc822798ee36a8107168c8c9 100644 --- a/ecmascript/mem/stw_young_gc.h +++ b/ecmascript/mem/stw_young_gc.h @@ -28,7 +28,7 @@ #include "ecmascript/mem/visitor.h" #include "ecmascript/mem/work_manager.h" -#include "libpandabase/os/mutex.h" +#include "ecmascript/platform/mutex.h" namespace panda { namespace ecmascript { diff --git a/ecmascript/mem/tagged_object-inl.h b/ecmascript/mem/tagged_object-inl.h index bd86e2a6c835f5965f088bb91dfa53633db85732..61f60405a00545b1d68b7b6e29661912f6c03c1f 100644 --- a/ecmascript/mem/tagged_object-inl.h +++ b/ecmascript/mem/tagged_object-inl.h @@ -48,14 +48,13 @@ inline JSHClass *TaggedObject::GetClass() const inline void TaggedObject::SynchronizedSetClass(JSHClass *hclass) { - reinterpret_cast *>(this)->store(reinterpret_cast(hclass), - std::memory_order_release); + Barriers::SynchronizedSetClass(this, JSTaggedValue(hclass).GetRawData()); } inline JSHClass *TaggedObject::SynchronizedGetClass() const { return reinterpret_cast( - reinterpret_cast *>(ToUintPtr(this))->load(std::memory_order_acquire)); + reinterpret_cast *>(ToUintPtr(this))->load(std::memory_order_acquire)); } inline JSThread *TaggedObject::GetJSThread() const diff --git a/ecmascript/mem/tlab_allocator-inl.h b/ecmascript/mem/tlab_allocator-inl.h index 114838eb4f817b8cfb9eba4e5f6d24a3f1d8609b..654ee31cabf1545c32a6008e7adb1ff7c9bb7a79 100644 --- a/ecmascript/mem/tlab_allocator-inl.h +++ b/ecmascript/mem/tlab_allocator-inl.h @@ -20,6 +20,7 @@ #include "ecmascript/free_object.h" #include "ecmascript/mem/full_gc.h" +#include "ecmascript/mem/heap-inl.h" namespace panda::ecmascript { static constexpr size_t MIN_BUFFER_SIZE = 31_KB; diff --git a/ecmascript/mem/verification.cpp b/ecmascript/mem/verification.cpp index 18c56b9e4e1bb8dd316427d128bc9485abeb4990..aef90847b1b159fd18ac131e7c9c87236506a280 100644 --- a/ecmascript/mem/verification.cpp +++ b/ecmascript/mem/verification.cpp @@ -25,27 +25,46 @@ void VerifyObjectVisitor::VisitAllObjects(TaggedObject *obj) { auto jsHclass = obj->GetClass(); objXRay_.VisitObjectBody( - obj, jsHclass, [this]([[maybe_unused]] TaggedObject *root, ObjectSlot start, ObjectSlot end, - [[maybe_unused]] bool isNative) { - for (ObjectSlot slot = start; slot < end; slot++) { - JSTaggedValue value(slot.GetTaggedType()); - if (value.IsWeak()) { - if (!heap_->IsAlive(value.GetTaggedWeakRef())) { - LOG_GC(ERROR) << "Heap verify detected a dead weak object " << value.GetTaggedObject() - << " at object:" << slot.SlotAddress(); - ++(*failCount_); - } - } else if (value.IsHeapObject()) { - if (!heap_->IsAlive(value.GetTaggedObject())) { - LOG_GC(ERROR) << "Heap verify detected a dead object at " << value.GetTaggedObject() - << " at object:" << slot.SlotAddress(); - ++(*failCount_); + obj, jsHclass, [this](TaggedObject *root, ObjectSlot start, ObjectSlot end, + VisitObjectArea area) { + if (area == VisitObjectArea::IN_OBJECT) { + auto hclass = root->GetClass(); + if (!hclass->IsAllTaggedProp()) { + int index = 0; + for (ObjectSlot slot = start; slot < end; slot++) { + auto layout = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject()); + auto attr = layout->GetAttr(index++); + if (attr.IsTaggedRep()) { + VisitObject(slot); + } } + return; } } + for (ObjectSlot slot = start; slot < end; slot++) { + VisitObject(slot); + } }); } +void VerifyObjectVisitor::VisitObject(ObjectSlot slot) +{ + JSTaggedValue value(slot.GetTaggedType()); + if (value.IsWeak()) { + if (!heap_->IsAlive(value.GetTaggedWeakRef())) { + LOG_GC(ERROR) << "Heap verify detected a dead weak object " << value.GetTaggedObject() + << " at object:" << slot.SlotAddress(); + ++(*failCount_); + } + } else if (value.IsHeapObject()) { + if (!heap_->IsAlive(value.GetTaggedObject())) { + LOG_GC(ERROR) << "Heap verify detected a dead object at " << value.GetTaggedObject() + << " at object:" << slot.SlotAddress(); + ++(*failCount_); + } + } +} + void VerifyObjectVisitor::operator()(TaggedObject *obj, JSTaggedValue value) { ObjectSlot slot(reinterpret_cast(obj)); diff --git a/ecmascript/mem/verification.h b/ecmascript/mem/verification.h index ba144f7d744c5a1a54a5b7d697fba18cef9a3f02..a72b95b089176e351929fa0f89182791f22d4058 100644 --- a/ecmascript/mem/verification.h +++ b/ecmascript/mem/verification.h @@ -49,6 +49,7 @@ public: private: void VisitAllObjects(TaggedObject *obj); + void VisitObject(ObjectSlot slot); const Heap* const heap_ {nullptr}; size_t* const failCount_ {nullptr}; diff --git a/ecmascript/mem/visitor.h b/ecmascript/mem/visitor.h index ed9daa8b661ea2de3473f8ac1125255d50cf1056..a6d633060e458767c05cdc7fde89a3bbb453e3a6 100644 --- a/ecmascript/mem/visitor.h +++ b/ecmascript/mem/visitor.h @@ -30,6 +30,12 @@ enum class Root { ROOT_INTERNAL_CALL_PARAMS, }; +enum class VisitObjectArea { + NORMAL, + NATIVE_POINTER, + IN_OBJECT +}; + enum class VisitType : size_t { SEMI_GC_VISIT, OLD_GC_VISIT, SNAPSHOT_VISIT }; using RootVisitor = std::function; @@ -37,7 +43,7 @@ using RootRangeVisitor = std::function; using EcmaObjectRangeVisitor = std::function; + VisitObjectArea area)>; using WeakRootVisitor = std::function; } // namespace panda::ecmascript #endif // ECMASCRIPT_MEM_VISITOR_H diff --git a/ecmascript/mem/work_manager.cpp b/ecmascript/mem/work_manager.cpp index aebf6853fef756c9f4eed857fcfafcd99eed6305..62fa15e8874645a1eb67c9cde25b2e6ecf3a9e7a 100644 --- a/ecmascript/mem/work_manager.cpp +++ b/ecmascript/mem/work_manager.cpp @@ -132,6 +132,7 @@ size_t WorkManager::Finish() agedSpaces_.back())); agedSpaces_.pop_back(); } + initialized_.store(false, std::memory_order_release); return aliveSize; } @@ -142,6 +143,7 @@ void WorkManager::Finish(size_t &aliveSize, size_t &promotedSize) WorkNodeHolder &holder = works_[i]; promotedSize += holder.promotedSize_; } + initialized_.store(false, std::memory_order_release); } void WorkManager::Initialize(TriggerGCType gcType, ParallelGCTaskPhase taskPhase) @@ -161,6 +163,7 @@ void WorkManager::Initialize(TriggerGCType gcType, ParallelGCTaskPhase taskPhase holder.allocator_ = new TlabAllocator(heap_); } } + initialized_.store(true, std::memory_order_release); } WorkNode *WorkManager::AllocateWorkNode() @@ -175,7 +178,7 @@ WorkNode *WorkManager::AllocateWorkNode() do { begin = atomicField->load(std::memory_order_acquire); if (begin + totalSize >= spaceEnd_) { - os::memory::LockHolder lock(mtx_); + LockHolder lock(mtx_); begin = atomicField->load(std::memory_order_acquire); if (begin + totalSize >= spaceEnd_) { agedSpaces_.emplace_back(workSpace_); diff --git a/ecmascript/mem/work_manager.h b/ecmascript/mem/work_manager.h index 883d91bc24d268b6bf04a418c79c5da4633a8b03..b84e7f5bc56d57371f22046ffa80bed8f66d82ac 100644 --- a/ecmascript/mem/work_manager.h +++ b/ecmascript/mem/work_manager.h @@ -108,14 +108,14 @@ public: if (node == nullptr) { return; } - os::memory::LockHolder lock(mtx_); + LockHolder lock(mtx_); node->SetNext(top_); top_ = node; } bool Pop(WorkNode **node) { - os::memory::LockHolder lock(mtx_); + LockHolder lock(mtx_); if (top_ == nullptr) { return false; } @@ -126,7 +126,7 @@ public: private: WorkNode *top_ {nullptr}; - os::memory::Mutex mtx_; + Mutex mtx_; }; struct WorkNodeHolder { @@ -201,6 +201,10 @@ public: { return threadNum_; } + inline bool HasInitialized() const + { + return initialized_.load(std::memory_order_acquire); + } private: NO_COPY_SEMANTIC(WorkManager); @@ -217,8 +221,9 @@ private: uintptr_t spaceStart_; uintptr_t spaceEnd_; std::vector agedSpaces_; - os::memory::Mutex mtx_; + Mutex mtx_; ParallelGCTaskPhase parallelGCTaskPhase_; + std::atomic initialized_ {false}; }; } // namespace panda::ecmascript #endif // ECMASCRIPT_MEM_WORK_MANAGER_H diff --git a/ecmascript/message_string.h b/ecmascript/message_string.h index b569369e9d19f6ab94c1feb63ef3d167a9fe1dea..ace3664c5fa90c912def1349a036b010d5cf54bc 100644 --- a/ecmascript/message_string.h +++ b/ecmascript/message_string.h @@ -77,7 +77,7 @@ public: #undef DEF_MESSAGE_ID MAX_MESSAGE_COUNT, ASM_INTERPRETER_START = Message_INT32_VALUE + 1, - BUILTINS_STUB_START = Message_CharCodeAt, + BUILTINS_STUB_START = Message_StringCharCodeAt, BUILTINS_STUB_LAST = Message_ArrayConstructor, }; static const std::string& GetMessageString(int id); diff --git a/ecmascript/method.cpp b/ecmascript/method.cpp index 2c62735ef75c173b1270b970ba5a1e4f0ee838b6..11e1f4944155b2ad9ddaf67faedf7568b40c6133 100644 --- a/ecmascript/method.cpp +++ b/ecmascript/method.cpp @@ -36,7 +36,7 @@ const char *Method::GetMethodName(const JSPandaFile *file) const return MethodLiteral::GetMethodName(file, GetMethodId()); } -const CString Method::GetRecordName() const +const CString Method::GetRecordNameStr() const { const JSPandaFile *jsPandaFile = GetJSPandaFile(); return MethodLiteral::GetRecordName(jsPandaFile, GetMethodId()); @@ -103,4 +103,22 @@ JSHandle Method::Create(JSThread *thread, const JSPandaFile *jsPandaFile method->SetConstantPool(thread, newConstpool); return method; } + +const JSTaggedValue Method::GetRecordName() const +{ + JSTaggedValue module = GetModule(); + if (module.IsSourceTextModule()) { + JSTaggedValue recordName = SourceTextModule::Cast(module.GetTaggedObject())->GetEcmaModuleRecordName(); + if (!recordName.IsString()) { + LOG_INTERPRETER(DEBUG) << "module record name is undefined"; + return JSTaggedValue::Hole(); + } + return recordName; + } + if (module.IsString()) { + return module; + } + LOG_INTERPRETER(DEBUG) << "record name is undefined"; + return JSTaggedValue::Hole(); +} } // namespace panda::ecmascript diff --git a/ecmascript/method.h b/ecmascript/method.h index 2cce607b98fabfd06b10d3082f07cc68d0d25efd..deb72828fe9ebb0fa294376264f1556430f9ec9f 100644 --- a/ecmascript/method.h +++ b/ecmascript/method.h @@ -27,6 +27,7 @@ namespace panda::ecmascript { class JSPandaFile; +struct Reference; using EntityId = panda_file::File::EntityId; class Method : public TaggedObject { public: @@ -412,10 +413,12 @@ public: const char *PUBLIC_API GetMethodName() const; const char *PUBLIC_API GetMethodName(const JSPandaFile *file) const; std::string PUBLIC_API ParseFunctionName() const; - const CString GetRecordName() const; + const CString GetRecordNameStr() const; uint32_t FindCatchBlock(uint32_t pc) const; + const JSTaggedValue GetRecordName() const; + /* callfield */ static constexpr size_t VREGS_ARGS_NUM_BITS = 28; // 28: maximum 268,435,455 static constexpr uint64_t AOT_FASTCALL_BITS = 0x5; // 0x5LU: aot and fastcall bit field @@ -443,7 +446,8 @@ public: static constexpr size_t CONSTANT_POOL_OFFSET = TaggedObjectSize(); ACCESSORS(ConstantPool, CONSTANT_POOL_OFFSET, PROFILE_TYPE_INFO_OFFSET) - ACCESSORS(ProfileTypeInfo, PROFILE_TYPE_INFO_OFFSET, CALL_FIELD_OFFSET) + ACCESSORS(ProfileTypeInfo, PROFILE_TYPE_INFO_OFFSET, ECMA_MODULE_OFFSET) + ACCESSORS(Module, ECMA_MODULE_OFFSET, CALL_FIELD_OFFSET) ACCESSORS_PRIMITIVE_FIELD(CallField, uint64_t, CALL_FIELD_OFFSET, NATIVE_POINTER_OR_BYTECODE_ARRAY_OFFSET) // Native method decides this filed is NativePointer or BytecodeArray pointer. ACCESSORS_NATIVE_FIELD( diff --git a/ecmascript/module/js_dynamic_import.cpp b/ecmascript/module/js_dynamic_import.cpp index 0a8032dab54669650ec1783d7ef45b5e6ab61d88..cb56fbe879b942ed03ad687069ac1a93cdb87609 100644 --- a/ecmascript/module/js_dynamic_import.cpp +++ b/ecmascript/module/js_dynamic_import.cpp @@ -17,6 +17,7 @@ #include "ecmascript/base/path_helper.h" #include "ecmascript/builtins/builtins_promise_job.h" #include "ecmascript/js_function.h" +#include "ecmascript/module/js_module_deregister.h" #include "ecmascript/module/js_module_manager.h" namespace panda::ecmascript { @@ -27,23 +28,40 @@ JSTaggedValue DynamicImport::ExecuteNativeModule(JSThread *thread, JSHandle resolve, JSHandle reject) { ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager(); - CString requestPath = ConvertToString(specifierString.GetTaggedValue()); - CString entryPoint = PathHelper::GetStrippedModuleName(requestPath); - JSHandle nativeModule = moduleManager->ResolveNativeModule(requestPath, moduleType); - JSHandle requiredModule = JSHandle::Cast(nativeModule); - if (!SourceTextModule::LoadNativeModule(thread, requiredModule, - JSHandle(specifierString), moduleType)) { - LOG_FULL(WARN) << "LoadNativeModule " << requestPath << " failed"; + JSMutableHandle requiredModule(thread, thread->GlobalConstants()->GetUndefined()); + if (moduleManager->IsImportedModuleLoaded(specifierString.GetTaggedValue())) { + ModuleDeregister::ReviseLoadedModuleCount(thread, specifierString.GetTaggedValue()); + JSHandle moduleRecord = + moduleManager->HostGetImportedModule(specifierString.GetTaggedValue()); + requiredModule.Update(moduleRecord); + } else { + CString requestPath = ConvertToString(specifierString.GetTaggedValue()); + CString entryPoint = PathHelper::GetStrippedModuleName(requestPath); + JSHandle nativeModuleHld = moduleManager->ResolveNativeModule(requestPath, moduleType); + JSHandle nativeModule = JSHandle::Cast(nativeModuleHld); + + if (!SourceTextModule::LoadNativeModule(thread, nativeModule, JSHandle(specifierString), + moduleType)) { + LOG_FULL(ERROR) << " dynamically loading native module" << requestPath << " failed"; + } + + // initialize native module + nativeModule->SetStatus(ModuleStatus::EVALUATED); + nativeModule->SetLoadingTypes(LoadingTypes::DYNAMITC_MODULE); + nativeModule->SetRegisterCounts(1); + thread->GetEcmaVM()->PushToDeregisterModuleList(requestPath); + requiredModule.Update(nativeModule); } - JSHandle nativeExports(thread, requiredModule->GetModuleValue(thread, 0, false)); - requiredModule->SetStatus(ModuleStatus::EVALUATED); - JSHandle moduleNamespace = SourceTextModule::GetModuleNamespace(thread, requiredModule); + + JSHandle moduleNamespace = SourceTextModule::GetModuleNamespace(thread, + JSHandle(requiredModule)); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, BuiltinsPromiseJob::CatchException(thread, reject)); JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, JSHandle(resolve), undefined, undefined, 1); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, BuiltinsPromiseJob::CatchException(thread, reject)); info->SetCallArg(moduleNamespace.GetTaggedValue()); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return JSFunction::Call(info); diff --git a/ecmascript/module/js_module_deregister.cpp b/ecmascript/module/js_module_deregister.cpp new file mode 100644 index 0000000000000000000000000000000000000000..74b4516cdcef3c6de3cd2de383459cef2a056ef7 --- /dev/null +++ b/ecmascript/module/js_module_deregister.cpp @@ -0,0 +1,206 @@ +/* + * 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/module/js_module_deregister.h" + +#include "ecmascript/base/path_helper.h" +#include "ecmascript/global_env.h" +#include "ecmascript/jspandafile/js_pandafile.h" +#include "ecmascript/jspandafile/js_pandafile_executor.h" +#include "ecmascript/jspandafile/js_pandafile_manager.h" +#include "ecmascript/linked_hash_table.h" +#include "ecmascript/module/js_module_manager.h" +#include "ecmascript/module/js_module_source_text.h" +#include "ecmascript/module/module_data_extractor.h" +#include "ecmascript/platform/file.h" +#include "ecmascript/require/js_cjs_module.h" +#include "ecmascript/tagged_dictionary.h" + +namespace panda::ecmascript { +using PathHelper = base::PathHelper; + +void ModuleDeregister::FreeModuleRecord(void *pointer, void *hint) +{ + if (pointer == nullptr) { + LOG_FULL(FATAL) << "Lacking deregister module's name."; + return; + } + auto thread = reinterpret_cast(hint); + + // pointer is module's name, which will be deregistered. + JSHandle module = + thread->GetCurrentEcmaContext()->GetModuleManager()->HostGetImportedModule(pointer); + + LoadingTypes type = module->GetLoadingTypes(); + JSTaggedValue moduleRecordName = SourceTextModule::GetModuleName(module.GetTaggedValue()); + CString recordNameStr = ConvertToString(moduleRecordName); + if (type != LoadingTypes::DYNAMITC_MODULE) { + LOG_FULL(INFO) << "free stable module's ModuleNameSpace" << recordNameStr; + } + NativeAreaAllocator* allocator = thread->GetEcmaVM()->GetNativeAreaAllocator(); + allocator->FreeBuffer(pointer); + if (type == LoadingTypes::DYNAMITC_MODULE) { + std::set decreaseModule = {recordNameStr}; + DecreaseRegisterCounts(thread, module, decreaseModule); + uint16_t counts = module->GetRegisterCounts(); + if (counts == 0) { + thread->GetEcmaVM()->RemoveFromDeregisterModuleList(recordNameStr); + } + LOG_FULL(INFO) << "try to remove module " << recordNameStr << ", register counts is " << counts; + } +} + +void ModuleDeregister::ReviseLoadedModuleCount(JSThread *thread, JSTaggedValue moduleName) +{ + EcmaVM *vm = thread->GetEcmaVM(); + ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager(); + JSHandle module = moduleManager->HostGetImportedModule(moduleName); + + LoadingTypes type = module->GetLoadingTypes(); + // do not change stable module's RegisterCounts. + if (type == LoadingTypes::STABLE_MODULE) { + return; + } + CString recordNameStr = ConvertToString(moduleName); + if (!vm->ContainInDeregisterModuleList(recordNameStr)) { + std::set increaseModule = {recordNameStr}; + IncreaseRegisterCounts(thread, module, increaseModule); + } +} + +void ModuleDeregister::RemoveModule(JSThread *thread, JSHandle module) +{ + JSTaggedValue moduleRecordName = SourceTextModule::GetModuleName(module.GetTaggedValue()); + ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager(); + if (module->GetTypes() == ModuleTypes::APP_MODULE || module->GetTypes() == ModuleTypes::OHOS_MODULE) { + if (TryToRemoveSO(thread, module)) { + LOG_FULL(INFO) << "Remove native module " << ConvertToString(moduleRecordName).c_str() << " successfully."; + } else { + LOG_FULL(INFO) << "Remove native module " << ConvertToString(moduleRecordName).c_str() << " failed."; + } + } + JSHandle dict(thread, moduleManager->resolvedModules_.GetTaggedObject()); + int entry = dict->FindEntry(moduleRecordName); + LOG_ECMA_IF(entry == -1, FATAL) << "Can not get module: " << ConvertToString(moduleRecordName) << + ", when try to remove the module"; + + moduleManager->resolvedModules_ = NameDictionary::Remove(thread, dict, entry).GetTaggedValue(); +} + +void ModuleDeregister::IncreaseRegisterCounts(JSThread *thread, JSHandle module, + std::set &increaseModule) +{ + if (!module->GetRequestedModules().IsUndefined()) { + JSHandle requestedModules(thread, module->GetRequestedModules()); + size_t requestedModulesLen = requestedModules->GetLength(); + JSMutableHandle required(thread, thread->GlobalConstants()->GetUndefined()); + for (size_t idx = 0; idx < requestedModulesLen; idx++) { + required.Update(requestedModules->Get(idx)); + JSMutableHandle requiredModule(thread, thread->GlobalConstants()->GetUndefined()); + JSTaggedValue moduleRecordName = module->GetEcmaModuleRecordName(); + CString moduleName; + if (moduleRecordName.IsUndefined()) { + JSHandle requiredVal = + SourceTextModule::HostResolveImportedModule(thread, module, required); + RETURN_IF_ABRUPT_COMPLETION(thread); + requiredModule.Update(JSHandle::Cast(requiredVal)); + moduleName = ConvertToString(requiredModule->GetEcmaModuleFilename()); + } else { + ASSERT(moduleRecordName.IsString()); + JSHandle requiredVal = + SourceTextModule::HostResolveImportedModuleWithMerge(thread, module, required); + RETURN_IF_ABRUPT_COMPLETION(thread); + requiredModule.Update(JSHandle::Cast(requiredVal)); + moduleName = ConvertToString(requiredModule->GetEcmaModuleRecordName()); + } + if (increaseModule.find(moduleName) != increaseModule.end()) { + LOG_FULL(DEBUG) << "Find module cyclical loading, stop increasing."; + requiredModule->SetLoadingTypes(LoadingTypes::STABLE_MODULE); + return; + } + increaseModule.emplace(moduleName); + LoadingTypes type = requiredModule->GetLoadingTypes(); + if (type == LoadingTypes::DYNAMITC_MODULE) { + IncreaseRegisterCounts(thread, requiredModule, increaseModule); + } + } + } + + if (module->GetLoadingTypes() == LoadingTypes::STABLE_MODULE) { + return; + } + uint16_t registerNum = module->GetRegisterCounts(); + if (registerNum == UINT16_MAX) { + module->SetLoadingTypes(LoadingTypes::STABLE_MODULE); + return; + } + module->SetRegisterCounts(registerNum + 1); +} + +void ModuleDeregister::DecreaseRegisterCounts(JSThread *thread, JSHandle module, + std::set &decreaseModule) +{ + if (!module->GetRequestedModules().IsUndefined()) { + JSHandle requestedModules(thread, module->GetRequestedModules()); + size_t requestedModulesLen = requestedModules->GetLength(); + JSMutableHandle required(thread, thread->GlobalConstants()->GetUndefined()); + for (size_t idx = 0; idx < requestedModulesLen; idx++) { + required.Update(requestedModules->Get(idx)); + JSMutableHandle requiredModule(thread, thread->GlobalConstants()->GetUndefined()); + JSTaggedValue moduleRecordName = module->GetEcmaModuleRecordName(); + CString moduleName; + if (moduleRecordName.IsUndefined()) { + JSHandle requiredVal = + SourceTextModule::HostResolveImportedModule(thread, module, required); + RETURN_IF_ABRUPT_COMPLETION(thread); + requiredModule.Update(JSHandle::Cast(requiredVal)); + moduleName = ConvertToString(requiredModule->GetEcmaModuleFilename()); + } else { + ASSERT(moduleRecordName.IsString()); + JSHandle requiredVal = + SourceTextModule::HostResolveImportedModuleWithMerge(thread, module, required); + RETURN_IF_ABRUPT_COMPLETION(thread); + requiredModule.Update(JSHandle::Cast(requiredVal)); + moduleName = ConvertToString(requiredModule->GetEcmaModuleRecordName()); + } + if (decreaseModule.find(moduleName) != decreaseModule.end()) { + LOG_FULL(DEBUG) << "Find module cyclical loading, stop increasing."; + requiredModule->SetLoadingTypes(LoadingTypes::STABLE_MODULE); + return; + } + decreaseModule.emplace(moduleName); + LoadingTypes type = requiredModule->GetLoadingTypes(); + if (type == LoadingTypes::DYNAMITC_MODULE) { + DecreaseRegisterCounts(thread, requiredModule, decreaseModule); + } + } + } + + if (module->GetLoadingTypes() != LoadingTypes::DYNAMITC_MODULE) { + return; + } + uint16_t num = module->GetRegisterCounts(); + if (num == 0) { + LOG_FULL(FATAL) << "moduleNameSpace can not be uninstalled"; + } + + uint16_t registerNum = num - 1; + if (registerNum == 0) { + LOG_FULL(INFO) << "try to remove module " << + ConvertToString(SourceTextModule::GetModuleName(module.GetTaggedValue())).c_str(); + RemoveModule(thread, module); + } + module->SetRegisterCounts(registerNum); +} +} // namespace panda::ecmascript diff --git a/ecmascript/module/js_module_deregister.h b/ecmascript/module/js_module_deregister.h new file mode 100644 index 0000000000000000000000000000000000000000..bd583d9db760f89b834d68def71fe471ff1e27ba --- /dev/null +++ b/ecmascript/module/js_module_deregister.h @@ -0,0 +1,61 @@ +/* + * 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_MODULE_JS_MODULE_DEREGISTER_H +#define ECMASCRIPT_MODULE_JS_MODULE_DEREGISTER_H + +#include "ecmascript/js_object.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/module/js_module_source_text.h" + +namespace panda::ecmascript { +class ModuleDeregister { +public: + static inline void InitForDeregisterModule(JSThread *thread, + JSHandle moduleRecord, bool excuteFromJob) + { + if (!excuteFromJob) { + return; + } + LoadingTypes moduleLoadingType = LoadingTypes::DYNAMITC_MODULE; + JSHandle module = JSHandle::Cast(moduleRecord); + module->SetLoadingTypes(moduleLoadingType); + module->SetRegisterCounts(1); + thread->GetEcmaVM()->PushToDeregisterModuleList( + ConvertToString(SourceTextModule::GetModuleName(moduleRecord.GetTaggedValue()))); + } + + static inline void ProcessModuleReference(JSThread *thread, const JSHandle &nameSpVal) + { + JSHandle nameSp = JSHandle::Cast(nameSpVal); + JSHandle moduleRecord(thread, nameSp->GetModule()); + JSTaggedValue weakNameSp = JSTaggedValue(nameSpVal->CreateAndGetWeakRef()); + moduleRecord->SetNamespace(thread, weakNameSp); + } + + static void FreeModuleRecord(void *pointer, void *hint); + + static void RemoveModule(JSThread *thread, JSHandle module); + + static void ReviseLoadedModuleCount(JSThread *thread, JSTaggedValue moduleName); + + static void IncreaseRegisterCounts(JSThread *thread, JSHandle module, + std::set &increaseModule); + + static void DecreaseRegisterCounts(JSThread *thread, JSHandle module, + std::set &decreaseModule); +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_MODULE_JS_MODULE_DEREGISTER_H diff --git a/ecmascript/module/js_module_manager.cpp b/ecmascript/module/js_module_manager.cpp index bfbb220f5a3be13d0950cf22a87ad7adc50674ca..39f963afccb4b1b89d20d9e0602852bf99e8fb8d 100644 --- a/ecmascript/module/js_module_manager.cpp +++ b/ecmascript/module/js_module_manager.cpp @@ -14,7 +14,6 @@ */ #include "ecmascript/module/js_module_manager.h" -#include "ecmascript/base/path_helper.h" #include "ecmascript/compiler/aot_file/aot_file_manager.h" #include "ecmascript/global_env.h" #include "ecmascript/interpreter/fast_runtime_stub-inl.h" @@ -24,8 +23,10 @@ #include "ecmascript/jspandafile/js_pandafile_executor.h" #include "ecmascript/jspandafile/js_pandafile_manager.h" #include "ecmascript/linked_hash_table.h" +#include "ecmascript/module/js_module_deregister.h" #include "ecmascript/module/js_module_source_text.h" #include "ecmascript/module/module_data_extractor.h" +#include "ecmascript/module/module_path_helper.h" #include "ecmascript/require/js_cjs_module.h" #include "ecmascript/tagged_dictionary.h" #ifdef PANDA_TARGET_WINDOWS @@ -33,8 +34,10 @@ #endif namespace panda::ecmascript { -using PathHelper = base::PathHelper; using StringHelper = base::StringHelper; +using JSPandaFile = ecmascript::JSPandaFile; +using JSRecordInfo = ecmascript::JSPandaFile::JSRecordInfo; + ModuleManager::ModuleManager(EcmaVM *vm) : vm_(vm) { resolvedModules_ = NameDictionary::Create(vm_->GetJSThread(), DEAULT_DICTIONART_CAPACITY).GetTaggedValue(); @@ -43,8 +46,8 @@ ModuleManager::ModuleManager(EcmaVM *vm) : vm_(vm) JSTaggedValue ModuleManager::GetCurrentModule() { FrameHandler frameHandler(vm_->GetJSThread()); - JSTaggedValue currentFunc = frameHandler.GetFunction(); - return JSFunction::Cast(currentFunc.GetTaggedObject())->GetModule(); + Method *currentMethod = frameHandler.GetMethod(); + return currentMethod->GetModule(); } JSTaggedValue ModuleManager::GetModuleValueInner(int32_t index) @@ -65,21 +68,18 @@ JSTaggedValue ModuleManager::GetModuleValueInner(int32_t index, JSTaggedValue js return SourceTextModule::Cast(currentModule.GetTaggedObject())->GetModuleValue(vm_->GetJSThread(), index, false); } -JSTaggedValue ModuleManager::GetModuleValueOutter(int32_t index) +JSTaggedValue ModuleManager::GetModuleValueInner(int32_t index, JSHandle currentModule) { - JSTaggedValue currentModule = GetCurrentModule(); - return GetModuleValueOutterInternal(index, currentModule); + if (currentModule->IsUndefined()) { + LOG_FULL(FATAL) << "GetModuleValueInner currentModule failed"; + } + return SourceTextModule::Cast(currentModule->GetTaggedObject())->GetModuleValue(vm_->GetJSThread(), index, false); } -JSTaggedValue ModuleManager::GetModuleName(JSTaggedValue currentModule) +JSTaggedValue ModuleManager::GetModuleValueOutter(int32_t index) { - SourceTextModule *module = SourceTextModule::Cast(currentModule.GetTaggedObject()); - JSTaggedValue recordName = module->GetEcmaModuleRecordName(); - if (recordName.IsUndefined()) { - return module->GetEcmaModuleFilename(); - } - - return recordName; + JSTaggedValue currentModule = GetCurrentModule(); + return GetModuleValueOutterInternal(index, currentModule); } JSTaggedValue ModuleManager::GetModuleValueOutter(int32_t index, JSTaggedValue jsFunc) @@ -88,6 +88,11 @@ JSTaggedValue ModuleManager::GetModuleValueOutter(int32_t index, JSTaggedValue j return GetModuleValueOutterInternal(index, currentModule); } +JSTaggedValue ModuleManager::GetModuleValueOutter(int32_t index, JSHandle currentModule) +{ + return GetModuleValueOutterInternal(index, currentModule.GetTaggedValue()); +} + JSTaggedValue ModuleManager::GetModuleValueOutterInternal(int32_t index, JSTaggedValue currentModule) { JSThread *thread = vm_->GetJSThread(); @@ -121,7 +126,7 @@ JSTaggedValue ModuleManager::GetModuleValueOutterInternal(int32_t index, JSTagge JSTaggedValue resolvedModule = binding->GetModule(); SourceTextModule *module = SourceTextModule::Cast(resolvedModule.GetTaggedObject()); if (module->GetTypes() == ModuleTypes::CJS_MODULE) { - JSHandle cjsModuleName(thread, GetModuleName(JSTaggedValue(module))); + JSHandle cjsModuleName(thread, SourceTextModule::GetModuleName(JSTaggedValue(module))); return CjsModule::SearchFromModuleCache(thread, cjsModuleName).GetTaggedValue(); } LOG_ECMA(FATAL) << "Get module value failed, mistaken ResolvedBinding"; @@ -131,7 +136,7 @@ JSTaggedValue ModuleManager::GetModuleValueOutterInternal(int32_t index, JSTagge JSTaggedValue ModuleManager::GetNativeModuleValue(JSThread *thread, JSTaggedValue currentModule, JSTaggedValue resolvedModule, ResolvedIndexBinding *binding) { - JSHandle nativeModuleName(thread, GetModuleName(resolvedModule)); + JSHandle nativeModuleName(thread, SourceTextModule::GetModuleName(resolvedModule)); JSHandle nativeExports = JSHandle(thread, SourceTextModule::Cast(resolvedModule.GetTaggedObject())->GetModuleValue(thread, 0, false)); if (!nativeExports->IsJSObject()) { @@ -147,7 +152,7 @@ JSTaggedValue ModuleManager::GetNativeModuleValue(JSThread *thread, JSTaggedValu JSTaggedValue ModuleManager::GetCJSModuleValue(JSThread *thread, JSTaggedValue currentModule, JSTaggedValue resolvedModule, ResolvedIndexBinding *binding) { - JSHandle cjsModuleName(thread, GetModuleName(resolvedModule)); + JSHandle cjsModuleName(thread, SourceTextModule::GetModuleName(resolvedModule)); JSHandle cjsExports = CjsModule::SearchFromModuleCache(thread, cjsModuleName); // if cjsModule is not JSObject, means cjs uses default exports. if (!cjsExports->IsJSObject()) { @@ -171,11 +176,18 @@ JSTaggedValue ModuleManager::GetValueFromExportObject(JSHandle &e if (index == SourceTextModule::UNDEFINED_INDEX) { return exportObject.GetTaggedValue(); } + JSTaggedValue value = JSTaggedValue::Hole(); JSObject *obj = JSObject::Cast(exportObject.GetTaggedValue()); - JSHClass *jsHclass = obj->GetJSHClass(); - LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject()); - PropertyAttributes attr = layoutInfo->GetAttr(index); - JSTaggedValue value = obj->GetProperty(jsHclass, attr); + TaggedArray *properties = TaggedArray::Cast(obj->GetProperties().GetTaggedObject()); + if (!properties->IsDictionaryMode()) { + JSHClass *jsHclass = obj->GetJSHClass(); + LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject()); + PropertyAttributes attr = layoutInfo->GetAttr(index); + value = obj->GetProperty(jsHclass, attr); + } else { + NameDictionary *dict = NameDictionary::Cast(properties); + value = dict->GetValue(index); + } if (UNLIKELY(value.IsAccessor())) { return FastRuntimeStub::CallGetter(vm_->GetJSThread(), JSTaggedValue(obj), JSTaggedValue(obj), value); } @@ -263,7 +275,7 @@ JSTaggedValue ModuleManager::GetModuleValueOutterInternal(JSTaggedValue key, JST ASSERT(resolvedModule.IsSourceTextModule()); SourceTextModule *module = SourceTextModule::Cast(resolvedModule.GetTaggedObject()); if (module->GetTypes() == ModuleTypes::CJS_MODULE) { - JSHandle cjsModuleName(thread, GetModuleName(JSTaggedValue(module))); + JSHandle cjsModuleName(thread, SourceTextModule::GetModuleName(JSTaggedValue(module))); return CjsModule::SearchFromModuleCache(thread, cjsModuleName).GetTaggedValue(); } return module->GetModuleValue(thread, binding->GetBindingName(), false); @@ -313,10 +325,28 @@ JSHandle ModuleManager::HostGetImportedModule(JSTaggedValue re return JSHandle(vm_->GetJSThread(), result); } +JSHandle ModuleManager::HostGetImportedModule(void *src) +{ + const char *str = reinterpret_cast(src); + const uint8_t *strData = reinterpret_cast(src); + LOG_FULL(INFO) << "current str during module deregister process : " << str; + NameDictionary *dict = NameDictionary::Cast(resolvedModules_.GetTaggedObject()); + int entry = dict->FindEntry(strData, strlen(str)); + LOG_ECMA_IF(entry == -1, FATAL) << "Can not get deregister module: " << str; + JSTaggedValue result = dict->GetValue(entry); + return JSHandle(vm_->GetJSThread(), result); +} + bool ModuleManager::IsImportedModuleLoaded(JSTaggedValue referencing) { - int entry = NameDictionary::Cast(resolvedModules_.GetTaggedObject())->FindEntry(referencing); - return (entry != -1); + NameDictionary *dict = NameDictionary::Cast(resolvedModules_.GetTaggedObject()); + int entry = dict->FindEntry(referencing); + if (entry == -1) { + return false; + } + JSTaggedValue result = dict->GetValue(entry).GetWeakRawValue(); + dict->UpdateValue(vm_->GetJSThread(), entry, result); + return true; } bool ModuleManager::SkipDefaultBundleFile(const CString &moduleFileName) const @@ -325,12 +355,12 @@ bool ModuleManager::SkipDefaultBundleFile(const CString &moduleFileName) const const char relativeFilePath[] = ".."; // just to skip misunderstanding error log in LoadJSPandaFile when we ignore Module Resolving Failure. return !vm_->EnableReportModuleResolvingFailure() && - (base::StringHelper::StringStartWith(moduleFileName, PathHelper::BUNDLE_INSTALL_PATH) || + (base::StringHelper::StringStartWith(moduleFileName, ModulePathHelper::BUNDLE_INSTALL_PATH) || base::StringHelper::StringStartWith(moduleFileName, relativeFilePath)); } JSHandle ModuleManager::ResolveModuleInMergedABC(JSThread *thread, const JSPandaFile *jsPandaFile, - const CString &recordName) + const CString &recordName, bool excuteFromJob) { // In static parse Phase, due to lack of some parameters, we will create a empty SourceTextModule which will // be marked as INSTANTIATED to skip Dfs traversal of this import branch. @@ -338,12 +368,12 @@ JSHandle ModuleManager::ResolveModuleInMergedABC(JSThread *thread (jsPandaFile != nullptr && !jsPandaFile->HasRecord(recordName)))) { return CreateEmptyModule(); } else { - return ResolveModuleWithMerge(thread, jsPandaFile, recordName); + return ResolveModuleWithMerge(thread, jsPandaFile, recordName, excuteFromJob); } } JSHandle ModuleManager::HostResolveImportedModuleWithMerge(const CString &moduleFileName, - const CString &recordName) + const CString &recordName, bool excuteFromJob) { JSThread *thread = vm_->GetJSThread(); ObjectFactory *factory = vm_->GetFactory(); @@ -366,7 +396,8 @@ JSHandle ModuleManager::HostResolveImportedModuleWithMerge(const } } - JSHandle moduleRecord = ResolveModuleInMergedABC(thread, jsPandaFile.get(), recordName); + JSHandle moduleRecord = ResolveModuleInMergedABC(thread, + jsPandaFile.get(), recordName, excuteFromJob); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); JSHandle handleDict(thread, resolvedModules_); resolvedModules_ = NameDictionary::Put(thread, handleDict, JSHandle(recordNameHandle), @@ -389,7 +420,7 @@ JSHandle ModuleManager::CreateEmptyModule() return JSHandle::Cast(tmpModuleRecord); } -JSHandle ModuleManager::HostResolveImportedModule(const CString &referencingModule) +JSHandle ModuleManager::HostResolveImportedModule(const CString &referencingModule, bool excuteFromJob) { JSThread *thread = vm_->GetJSThread(); ObjectFactory *factory = vm_->GetFactory(); @@ -418,7 +449,7 @@ JSHandle ModuleManager::HostResolveImportedModule(const CString & THROW_NEW_ERROR_AND_RETURN_HANDLE(thread, ErrorType::REFERENCE_ERROR, JSTaggedValue, msg.c_str()); } - return ResolveModule(thread, jsPandaFile.get()); + return ResolveModule(thread, jsPandaFile.get(), excuteFromJob); } // The security interface needs to be modified accordingly. @@ -446,20 +477,22 @@ JSHandle ModuleManager::HostResolveImportedModule(const void *buf return ResolveModule(thread, jsPandaFile.get()); } -JSHandle ModuleManager::ResolveModule(JSThread *thread, const JSPandaFile *jsPandaFile) +JSHandle ModuleManager::ResolveModule(JSThread *thread, const JSPandaFile *jsPandaFile, + bool excuteFromJob) { ObjectFactory *factory = vm_->GetFactory(); CString moduleFileName = jsPandaFile->GetJSPandaFileDesc(); JSHandle moduleRecord = thread->GlobalConstants()->GetHandledUndefined(); - if (jsPandaFile->IsModule(thread)) { + JSRecordInfo recordInfo = const_cast(jsPandaFile)->FindRecordInfo(JSPandaFile::ENTRY_FUNCTION_NAME); + if (jsPandaFile->IsModule(recordInfo)) { moduleRecord = ModuleDataExtractor::ParseModule(thread, jsPandaFile, moduleFileName, moduleFileName); - } else if (jsPandaFile->IsJson(thread)) { + } else if (jsPandaFile->IsJson(recordInfo)) { moduleRecord = ModuleDataExtractor::ParseJsonModule(thread, jsPandaFile, moduleFileName); } else { - ASSERT(jsPandaFile->IsCjs(thread)); + ASSERT(jsPandaFile->IsCjs(recordInfo)); moduleRecord = ModuleDataExtractor::ParseCjsModule(thread, jsPandaFile); } - + ModuleDeregister::InitForDeregisterModule(thread, moduleRecord, excuteFromJob); JSHandle dict(thread, resolvedModules_); JSHandle referencingHandle = JSHandle::Cast(factory->NewFromUtf8(moduleFileName)); resolvedModules_ = @@ -483,24 +516,33 @@ JSHandle ModuleManager::ResolveNativeModule(const CString &module } JSHandle ModuleManager::ResolveModuleWithMerge( - JSThread *thread, const JSPandaFile *jsPandaFile, const CString &recordName) + JSThread *thread, const JSPandaFile *jsPandaFile, const CString &recordName, bool excuteFromJob) { ObjectFactory *factory = vm_->GetFactory(); CString moduleFileName = jsPandaFile->GetJSPandaFileDesc(); JSHandle moduleRecord = thread->GlobalConstants()->GetHandledUndefined(); - if (jsPandaFile->IsModule(thread, recordName)) { + JSRecordInfo recordInfo; + bool hasRecord = jsPandaFile->CheckAndGetRecordInfo(recordName, recordInfo); + if (!hasRecord) { + CString msg = "cannot find record '" + recordName + "', please check the request path.'" + + moduleFileName + "'."; + LOG_FULL(ERROR) << msg; + THROW_NEW_ERROR_AND_RETURN_HANDLE(thread, ErrorType::REFERENCE_ERROR, JSTaggedValue, msg.c_str()); + } + if (jsPandaFile->IsModule(recordInfo)) { RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); moduleRecord = ModuleDataExtractor::ParseModule(thread, jsPandaFile, recordName, moduleFileName); - } else if (jsPandaFile->IsJson(thread, recordName)) { + } else if (jsPandaFile->IsJson(recordInfo)) { moduleRecord = ModuleDataExtractor::ParseJsonModule(thread, jsPandaFile, moduleFileName, recordName); } else { - ASSERT(jsPandaFile->IsCjs(thread, recordName)); + ASSERT(jsPandaFile->IsCjs(recordInfo)); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); moduleRecord = ModuleDataExtractor::ParseCjsModule(thread, jsPandaFile); } JSHandle recordNameHandle = JSHandle::Cast(factory->NewFromUtf8(recordName)); JSHandle::Cast(moduleRecord)->SetEcmaModuleRecordName(thread, recordNameHandle); + ModuleDeregister::InitForDeregisterModule(thread, moduleRecord, excuteFromJob); return moduleRecord; } @@ -564,7 +606,8 @@ JSTaggedValue ModuleManager::GetModuleNamespaceInternal(int32_t index, JSTaggedV } // if requiredModuleST is CommonJS if (moduleType == ModuleTypes::CJS_MODULE) { - JSHandle cjsModuleName(thread, GetModuleName(requiredModuleST.GetTaggedValue())); + JSHandle cjsModuleName(thread, + SourceTextModule::GetModuleName(requiredModuleST.GetTaggedValue())); return CjsModule::SearchFromModuleCache(thread, cjsModuleName).GetTaggedValue(); } // if requiredModuleST is ESM diff --git a/ecmascript/module/js_module_manager.h b/ecmascript/module/js_module_manager.h index 38901a44ce2b3ff28938448c98effe457a35cbdd..b37af1887ba19285bf72c7c33a8756cfef3d2b37 100644 --- a/ecmascript/module/js_module_manager.h +++ b/ecmascript/module/js_module_manager.h @@ -20,6 +20,7 @@ #include "ecmascript/jspandafile/js_pandafile.h" #include "ecmascript/module/js_module_source_text.h" #include "ecmascript/napi/jsnapi_helper.h" +#include "ecmascript/tagged_dictionary.h" namespace panda::ecmascript { class ModuleManager { @@ -29,8 +30,10 @@ public: JSTaggedValue GetModuleValueInner(int32_t index); JSTaggedValue GetModuleValueInner(int32_t index, JSTaggedValue jsFunc); + JSTaggedValue GetModuleValueInner(int32_t index, JSHandle currentModule); JSTaggedValue GetModuleValueOutter(int32_t index); JSTaggedValue GetModuleValueOutter(int32_t index, JSTaggedValue jsFunc); + JSTaggedValue GetModuleValueOutter(int32_t index, JSHandle currentModule); void StoreModuleValue(int32_t index, JSTaggedValue value); void StoreModuleValue(int32_t index, JSTaggedValue value, JSTaggedValue jsFunc); JSTaggedValue GetModuleNamespace(int32_t index); @@ -42,7 +45,6 @@ public: JSTaggedValue GetModuleValueInner(JSTaggedValue key, JSTaggedValue jsFunc); JSTaggedValue GetModuleValueOutter(JSTaggedValue key); JSTaggedValue GetModuleValueOutter(JSTaggedValue key, JSTaggedValue jsFunc); - JSTaggedValue GetModuleName(JSTaggedValue currentModule); void StoreModuleValue(JSTaggedValue key, JSTaggedValue value); void StoreModuleValue(JSTaggedValue key, JSTaggedValue value, JSTaggedValue jsFunc); JSTaggedValue GetModuleNamespace(JSTaggedValue localName); @@ -52,22 +54,27 @@ public: JSHandle HostGetImportedModule(const CString &referencingModule); JSHandle HostGetImportedModule(JSTaggedValue referencing); + JSHandle HostGetImportedModule(void *src); bool IsImportedModuleLoaded(JSTaggedValue referencing); JSHandle ResolveNativeModule(const CString &moduleRequestName, ModuleTypes moduleType); JSHandle HostResolveImportedModule(const void *buffer, size_t size, const CString &filename); - JSHandle HostResolveImportedModule(const CString &referencingModule); + JSHandle HostResolveImportedModule(const CString &referencingModule, + bool excuteFromJob = false); JSHandle PUBLIC_API HostResolveImportedModuleWithMerge(const CString &referencingModule, - const CString &recordName); + const CString &recordName, bool excuteFromJob = false); JSHandle HostResolveImportedModule(const JSPandaFile *jsPandaFile, const CString &filename); JSTaggedValue GetCurrentModule(); - + JSTaggedValue GetNativeModuleValue(JSThread *thread, JSTaggedValue currentModule, + JSTaggedValue resolvedModule, ResolvedIndexBinding *binding); + JSTaggedValue GetCJSModuleValue(JSThread *thread, JSTaggedValue currentModule, + JSTaggedValue resolvedModule, ResolvedIndexBinding *binding); void AddResolveImportedModule(const JSPandaFile *jsPandaFile, const CString &referencingModule); void AddResolveImportedModule(const CString &referencingModule, JSHandle moduleRecord); void Iterate(const RootVisitor &v); - bool GetCurrentMode() const + bool GetExecuteMode() const { return isExecuteBuffer_; } @@ -85,16 +92,12 @@ private: bool SkipDefaultBundleFile(const CString &moduleFileName) const; JSHandle ResolveModuleInMergedABC(JSThread *thread, const JSPandaFile *jsPandaFile, - const CString &recordName); + const CString &recordName, bool excuteFromJob = false); JSHandle CreateEmptyModule(); JSTaggedValue GetModuleValueOutterInternal(int32_t index, JSTaggedValue currentModule); void StoreModuleValueInternal(JSHandle ¤tModule, int32_t index, JSTaggedValue value); - JSTaggedValue GetNativeModuleValue(JSThread *thread, JSTaggedValue currentModule, - JSTaggedValue resolvedModule, ResolvedIndexBinding *binding); - JSTaggedValue GetCJSModuleValue(JSThread *thread, JSTaggedValue currentModule, - JSTaggedValue resolvedModule, ResolvedIndexBinding *binding); JSTaggedValue GetValueFromExportObject(JSHandle &exportObject, int32_t index); // deprecated begin @@ -103,9 +106,10 @@ private: JSTaggedValue key, JSTaggedValue value); // deprecated end - JSHandle ResolveModule(JSThread *thread, const JSPandaFile *jsPandaFile); + JSHandle ResolveModule(JSThread *thread, const JSPandaFile *jsPandaFile, + bool excuteFromJob = false); JSHandle ResolveModuleWithMerge(JSThread *thread, const JSPandaFile *jsPandaFile, - const CString &recordName); + const CString &recordName, bool excuteFromJob = false); static constexpr uint32_t DEAULT_DICTIONART_CAPACITY = 4; @@ -116,6 +120,7 @@ private: friend class EcmaVM; friend class PatchLoader; + friend class ModuleDeregister; }; } // namespace panda::ecmascript #endif // ECMASCRIPT_MODULE_JS_MODULE_MANAGER_H diff --git a/ecmascript/module/js_module_namespace.cpp b/ecmascript/module/js_module_namespace.cpp index 7a18df538d498f0bf3b74d574e85c89457c43ce8..c1d14b9b70e79c6448063a37c4216ac248dce30a 100644 --- a/ecmascript/module/js_module_namespace.cpp +++ b/ecmascript/module/js_module_namespace.cpp @@ -17,6 +17,8 @@ #include "ecmascript/global_env.h" #include "ecmascript/js_array.h" +#include "ecmascript/object_factory-inl.h" +#include "ecmascript/module/js_module_deregister.h" #include "ecmascript/module/js_module_record.h" #include "ecmascript/module/js_module_source_text.h" #include "ecmascript/base/string_helper.h" @@ -43,7 +45,7 @@ JSHandle ModuleNamespace::ModuleNamespaceCreate(JSThread *threa // are ordered as if an Array of the same values had been sorted using // Array.prototype.sort using undefined as comparefn. JSHandle exportsArray = JSArray::CreateArrayFromList(thread, exports); - JSHandle sortedExports = JSHandle::Cast(exportsArray); + JSHandle sortedExports = JSHandle::Cast(exportsArray); JSHandle fn = globalConst->GetHandledUndefined(); JSArray::Sort(thread, sortedExports, fn); // 8. Set M.[[Exports]] to sortedExports. @@ -56,6 +58,7 @@ JSHandle ModuleNamespace::ModuleNamespaceCreate(JSThread *threa JSHandle mNpObj = JSHandle::Cast(mNp); JSObject::DefineOwnProperty(thread, mNpObj, toStringTag, des); // 10. Set module.[[Namespace]] to M. + SetModuleDeregisterProcession(thread, mNp, ModuleDeregister::FreeModuleRecord); ModuleRecord::SetNamespace(thread, moduleRecord.GetTaggedValue(), mNp.GetTaggedValue()); mNp->GetJSHClass()->SetExtensible(false); return mNp; @@ -314,4 +317,26 @@ bool ModuleNamespace::ValidateKeysAvailable(JSThread *thread, const JSHandle &nameSpace, + const DeleteEntryPoint &callback) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + + JSHandle module(thread, nameSpace->GetModule()); + JSHandle moduleName(thread, SourceTextModule::GetModuleName(module.GetTaggedValue())); + CString moduleStr = ConvertToString(moduleName.GetTaggedValue()); + int srcLength = strlen(moduleStr.c_str()) + 1; + auto moduleNameData = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(srcLength); + if (memcpy_s(moduleNameData, srcLength, moduleStr.c_str(), srcLength) != EOK) { + LOG_ECMA(FATAL) << "Failed to copy module name's data."; + UNREACHABLE(); + } + char *tmpData = reinterpret_cast(moduleNameData); + tmpData[srcLength - 1] = '\0'; + ASSERT(nameSpace->GetDeregisterProcession().IsUndefined()); + JSHandle registerPointer = factory->NewJSNativePointer( + reinterpret_cast(moduleNameData), callback, reinterpret_cast(thread), false, srcLength); + nameSpace->SetDeregisterProcession(thread, registerPointer.GetTaggedValue()); +} } // namespace panda::ecmascript diff --git a/ecmascript/module/js_module_namespace.h b/ecmascript/module/js_module_namespace.h index 3154484de55c68b804db8077b4513f7fb71e2c98..18215055b490d6ea81d34bd2f0a72cbd322b4058 100644 --- a/ecmascript/module/js_module_namespace.h +++ b/ecmascript/module/js_module_namespace.h @@ -24,6 +24,9 @@ class ModuleNamespace final : public JSObject { public: CAST_CHECK(ModuleNamespace, IsModuleNamespace); + static void SetModuleDeregisterProcession(JSThread *thread, const JSHandle &nameSpace, + const DeleteEntryPoint &callback); + // 9.4.6.11ModuleNamespaceCreate ( module, exports ) static JSHandle ModuleNamespaceCreate(JSThread *thread, const JSHandle &module, const JSHandle &exports); @@ -57,7 +60,8 @@ public: static constexpr size_t MODULE_OFFSET = JSObject::SIZE; ACCESSORS(Module, MODULE_OFFSET, EXPORTS_OFFSET) - ACCESSORS(Exports, EXPORTS_OFFSET, SIZE) + ACCESSORS(Exports, EXPORTS_OFFSET, DEREGISTER_PROCESSION_OFFSET) + ACCESSORS(DeregisterProcession, DEREGISTER_PROCESSION_OFFSET, SIZE) DECL_DUMP() DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, MODULE_OFFSET, SIZE) diff --git a/ecmascript/module/js_module_record.cpp b/ecmascript/module/js_module_record.cpp index e707eef706f277f5f62159a3349c2e95f7f27d1b..4d7a742cca66d621f399bf99900f6060c15b91f4 100644 --- a/ecmascript/module/js_module_record.cpp +++ b/ecmascript/module/js_module_record.cpp @@ -15,6 +15,7 @@ #include "ecmascript/module/js_module_record.h" #include "ecmascript/module/js_module_source_text.h" +#include "ecmascript/object_factory-inl.h" namespace panda::ecmascript { int32_t ModuleRecord::Instantiate(JSThread *thread, const JSHandle &module) diff --git a/ecmascript/module/js_module_source_text.cpp b/ecmascript/module/js_module_source_text.cpp index e670738bb1dbf93a3bdc285372cfae4392390266..a597d5cb9b61f4a0daf65c73850911da57b5bef6 100644 --- a/ecmascript/module/js_module_source_text.cpp +++ b/ecmascript/module/js_module_source_text.cpp @@ -21,9 +21,11 @@ #include "ecmascript/jspandafile/js_pandafile_executor.h" #include "ecmascript/jspandafile/js_pandafile_manager.h" #include "ecmascript/linked_hash_table.h" +#include "ecmascript/module/js_module_deregister.h" #include "ecmascript/module/js_module_manager.h" #include "ecmascript/module/js_module_namespace.h" #include "ecmascript/module/module_data_extractor.h" +#include "ecmascript/module/module_path_helper.h" #include "ecmascript/platform/file.h" #include "ecmascript/tagged_dictionary.h" @@ -85,7 +87,13 @@ JSHandle SourceTextModule::HostResolveImportedModuleWithMerge( return JSHandle(moduleManager->HostGetImportedModule(moduleRequest.GetTaggedValue())); } - CString moduleRequestName = ConvertToString(EcmaString::Cast(moduleRequest->GetTaggedObject())); + CString moduleRequestName = ConvertToString(moduleRequest.GetTaggedValue()); + auto vm = thread->GetEcmaVM(); + // check if module need to be mock + if (vm->IsMockModule(moduleRequestName)) { + moduleRequestName = vm->GetMockModule(moduleRequestName); + } + auto [isNative, moduleType] = SourceTextModule::CheckNativeModule(moduleRequestName); if (isNative) { return moduleManager->ResolveNativeModule(moduleRequestName, moduleType); @@ -103,12 +111,12 @@ JSHandle SourceTextModule::HostResolveImportedModuleWithMerge( } CString outFileName = baseFilename; - CString entryPoint = PathHelper::ConcatFileNameWithMerge( + CString entryPoint = ModulePathHelper::ConcatFileNameWithMerge( thread, jsPandaFile.get(), outFileName, moduleRecordName, moduleRequestName); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); #if defined(PANDA_TARGET_WINDOWS) || defined(PANDA_TARGET_MACOS) - if (entryPoint == PathHelper::PREVIEW_OF_ACROSS_HAP_FLAG && + if (entryPoint == ModulePathHelper::PREVIEW_OF_ACROSS_HAP_FLAG && thread->GetEcmaVM()->EnableReportModuleResolvingFailure()) { THROW_SYNTAX_ERROR_AND_RETURN(thread, "", thread->GlobalConstants()->GetHandledUndefined()); } @@ -127,7 +135,7 @@ JSHandle SourceTextModule::HostResolveImportedModule(JSThread *th } JSHandle dirname = base::PathHelper::ResolveDirPath(thread, - JSHandle(thread, module->GetEcmaModuleFilename())); + ConvertToString(module->GetEcmaModuleFilename())); JSHandle moduleFilename = ResolveFilenameFromNative(thread, dirname.GetTaggedValue(), moduleRequest.GetTaggedValue()); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); @@ -153,28 +161,40 @@ bool SourceTextModule::CheckCircularImport(const JSHandle &mod } JSHandle SourceTextModule::ResolveExportObject(JSThread *thread, - const JSHandle &module, const JSHandle &exportObject, - const JSHandle &exportName) + const JSHandle &module, + const JSHandle &exports, + const JSHandle &exportName) { // Let module be this Source Text Module Record. auto globalConstants = thread->GlobalConstants(); - // For CJS, if exportObject is not JSObject, means the CJS module use default output + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // For CJS, if exports is not JSObject, means the CJS module use default output JSHandle defaultString = globalConstants->GetHandledDefaultString(); if (JSTaggedValue::SameValue(exportName, defaultString)) { // bind with a number - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); return JSHandle::Cast(factory->NewResolvedIndexBindingRecord(module, -1)); } - if (exportObject->IsJSObject()) { - JSHandle jsHclass(thread, JSObject::Cast(exportObject.GetTaggedValue())->GetJSHClass()); - // Get layoutInfo and compare the input and output names of files - JSHandle layoutInfo(thread, jsHclass->GetLayout()); - if (layoutInfo->NumberOfElements() != 0) { - JSHandle resolution = ResolveElementOfObject(thread, jsHclass, exportName, module); - if (!resolution->IsUndefined()) { - return resolution; + if (exports->IsJSObject()) { + JSHandle resolution(thread, JSTaggedValue::Hole()); + JSObject *exportObject = JSObject::Cast(exports.GetTaggedValue().GetTaggedObject()); + TaggedArray *properties = TaggedArray::Cast(exportObject->GetProperties().GetTaggedObject()); + if (!properties->IsDictionaryMode()) { + JSHandle jsHclass(thread, exportObject->GetJSHClass()); + // Get layoutInfo and compare the input and output names of files + LayoutInfo *layoutInfo = LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject()); + if (layoutInfo->NumberOfElements() != 0) { + resolution = ResolveElementOfObject(thread, jsHclass, exportName, module); + } + } else { + NameDictionary *dict = NameDictionary::Cast(properties); + int entry = dict->FindEntry(exportName.GetTaggedValue()); + if (entry != -1) { + resolution = JSHandle::Cast(factory->NewResolvedIndexBindingRecord(module, entry)); } } + if (!resolution->IsUndefined()) { + return resolution; + } } return globalConstants->GetHandledNull(); } @@ -270,19 +290,19 @@ void SourceTextModule::InstantiateCJS(JSThread *thread, const JSHandle SourceTextModule::CheckNativeModule(const CString &moduleRequestName) { if (moduleRequestName[0] != '@' || - StringHelper::StringStartWith(moduleRequestName, PathHelper::PREFIX_BUNDLE) || - StringHelper::StringStartWith(moduleRequestName, PathHelper::PREFIX_PACKAGE)|| + StringHelper::StringStartWith(moduleRequestName, ModulePathHelper::PREFIX_BUNDLE) || + StringHelper::StringStartWith(moduleRequestName, ModulePathHelper::PREFIX_PACKAGE)|| moduleRequestName.find(':') == CString::npos) { return {false, ModuleTypes::UNKNOWN}; } - if (StringHelper::StringStartWith(moduleRequestName, PathHelper::REQUIRE_NAPI_OHOS_PREFIX)) { + if (StringHelper::StringStartWith(moduleRequestName, ModulePathHelper::REQUIRE_NAPI_OHOS_PREFIX)) { return {true, ModuleTypes::OHOS_MODULE}; } - if (StringHelper::StringStartWith(moduleRequestName, PathHelper::REQUIRE_NAPI_APP_PREFIX)) { + if (StringHelper::StringStartWith(moduleRequestName, ModulePathHelper::REQUIRE_NAPI_APP_PREFIX)) { return {true, ModuleTypes::APP_MODULE}; } - if (StringHelper::StringStartWith(moduleRequestName, PathHelper::REQUIRE_NAITVE_MODULE_PREFIX)) { + if (StringHelper::StringStartWith(moduleRequestName, ModulePathHelper::REQUIRE_NAITVE_MODULE_PREFIX)) { return {true, ModuleTypes::NATIVE_MODULE}; } return {true, ModuleTypes::INTERNAL_MODULE}; @@ -419,7 +439,7 @@ void SourceTextModule::InitializeEnvironment(JSThread *thread, const JSHandleGetEcmaModuleRecordName()) + "'"; THROW_ERROR(thread, ErrorType::SYNTAX_ERROR, msg.c_str()); } // iii. Call envRec.CreateImportBinding( @@ -429,7 +449,7 @@ void SourceTextModule::InitializeEnvironment(JSThread *thread, const JSHandle SourceTextModule::GetModuleFromBinding(JSThread *thread, - const JSTaggedValue &resolvedBinding) + const JSTaggedValue &resolvedBinding) { if (resolvedBinding.IsResolvedIndexBinding()) { ResolvedIndexBinding *binding = ResolvedIndexBinding::Cast(resolvedBinding.GetTaggedObject()); @@ -439,8 +459,10 @@ JSHandle SourceTextModule::GetModuleFromBinding(JSThread *thre return JSHandle(thread, binding->GetModule()); } -int SourceTextModule::Instantiate(JSThread *thread, const JSHandle &moduleHdl) +int SourceTextModule::Instantiate(JSThread *thread, const JSHandle &moduleHdl, + bool excuteFromJob) { + ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "SourceTextModule::Instantiate"); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, SourceTextModule::UNDEFINED_INDEX); JSHandle module = JSHandle::Cast(moduleHdl); // 1. Let module be this Source Text Module Record. @@ -450,7 +472,7 @@ int SourceTextModule::Instantiate(JSThread *thread, const JSHandle> stack; // 4. Let result be InnerModuleInstantiation(module, stack, 0). JSHandle moduleRecord = JSHandle::Cast(module); - int result = SourceTextModule::InnerModuleInstantiation(thread, moduleRecord, stack, 0); + int result = SourceTextModule::InnerModuleInstantiation(thread, moduleRecord, stack, 0, excuteFromJob); // 5. If result is an abrupt completion, then if (thread->HasPendingException()) { // a. For each module m in stack, do @@ -479,7 +501,7 @@ int SourceTextModule::Instantiate(JSThread *thread, const JSHandle &moduleRecord, - CVector> &stack, int index) + CVector> &stack, int index, bool excuteFromJob) { // 1. If module is not a Source Text Module Record, then if (!moduleRecord.GetTaggedValue().IsSourceTextModule()) { @@ -523,20 +545,21 @@ int SourceTextModule::InnerModuleInstantiation(JSThread *thread, const JSHandle< JSHandle requiredVal = SourceTextModule::HostResolveImportedModule(thread, module, required); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, SourceTextModule::UNDEFINED_INDEX); + ModuleDeregister::InitForDeregisterModule(thread, requiredVal, excuteFromJob); requiredModule.Update(JSHandle::Cast(requiredVal)); - requestedModules->Set(thread, idx, requiredModule->GetEcmaModuleFilename()); } else { ASSERT(moduleRecordName.IsString()); JSHandle requiredVal = SourceTextModule::HostResolveImportedModuleWithMerge(thread, module, required); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, SourceTextModule::UNDEFINED_INDEX); + ModuleDeregister::InitForDeregisterModule(thread, requiredVal, excuteFromJob); requiredModule.Update(JSHandle::Cast(requiredVal)); - requestedModules->Set(thread, idx, requiredModule->GetEcmaModuleRecordName()); } // b. Set index to ? InnerModuleInstantiation(requiredModule, stack, index). JSHandle requiredModuleRecord = JSHandle::Cast(requiredModule); - index = SourceTextModule::InnerModuleInstantiation(thread, requiredModuleRecord, stack, index); + index = SourceTextModule::InnerModuleInstantiation(thread, + requiredModuleRecord, stack, index, excuteFromJob); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, index); // c. Assert: requiredModule.[[Status]] is either "instantiating", "instantiated", or "evaluated". ModuleStatus requiredModuleStatus = requiredModule->GetStatus(); @@ -555,7 +578,6 @@ int SourceTextModule::InnerModuleInstantiation(JSThread *thread, const JSHandle< } } } - // Adapter new opcode // 10. Perform ? ModuleDeclarationEnvironmentSetup(module). if (module->GetIsNewBcVersion()) { @@ -772,7 +794,7 @@ JSHandle SourceTextModule::GetModuleNamespace(JSThread *thread, ASSERT(module->GetEvaluationError() == SourceTextModule::UNDEFINED_INDEX); } // 4. Let namespace be module.[[Namespace]]. - JSMutableHandle moduleNamespace(thread, module->GetNamespace()); + JSMutableHandle moduleNamespace(thread, module->GetNamespace().GetWeakRawValue()); // If namespace is undefined, then if (moduleNamespace->IsUndefined()) { // a. Let exportedNames be ? module.GetExportedNames(« »). @@ -806,6 +828,7 @@ JSHandle SourceTextModule::GetModuleNamespace(JSThread *thread, int SourceTextModule::Evaluate(JSThread *thread, const JSHandle &module, const void *buffer, size_t size, bool excuteFromJob) { + ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "SourceTextModule::Evaluate"); // 1. Let module be this Source Text Module Record. // 2. Assert: module.[[Status]] is "instantiated" or "evaluated". [[maybe_unused]] ModuleStatus status = module->GetStatus(); @@ -1021,6 +1044,7 @@ int SourceTextModule::ModuleEvaluation(JSThread *thread, const JSHandleGetTypes(); if (SourceTextModule::IsNativeModule(moduleType)) { @@ -1518,11 +1542,29 @@ void SourceTextModule::CheckResolvedIndexBinding(JSThread *thread, const JSHandl CString msg = "the requested module '" + ConvertToString(ee->GetModuleRequest()) + "' does not provide an export named '" + - ConvertToString(exportName.GetTaggedValue()) + - "' which exported by '" + - ConvertToString(module->GetEcmaModuleRecordName()) + "'"; + ConvertToString(exportName.GetTaggedValue()); + if (!module->GetEcmaModuleRecordName().IsUndefined()) { + msg += "' which exported by '" + ConvertToString(module->GetEcmaModuleRecordName()) + "'"; + } else { + msg += "' which exported by '" + ConvertToString(module->GetEcmaModuleFilename()) + "'"; + } THROW_ERROR(thread, ErrorType::SYNTAX_ERROR, msg.c_str()); } } } + +JSTaggedValue SourceTextModule::GetModuleName(JSTaggedValue currentModule) +{ + SourceTextModule *module = SourceTextModule::Cast(currentModule.GetTaggedObject()); + JSTaggedValue recordName = module->GetEcmaModuleRecordName(); + if (recordName.IsUndefined()) { + return module->GetEcmaModuleFilename(); + } + return recordName; +} + +bool SourceTextModule::IsDynamicModule(LoadingTypes types) +{ + return types == LoadingTypes::DYNAMITC_MODULE; +} } // namespace panda::ecmascript diff --git a/ecmascript/module/js_module_source_text.h b/ecmascript/module/js_module_source_text.h index d8c462514188c61b89650ba985d72e4f701d6c64..d558661eeaf11bdc66f8a8176ce22af142c4929f 100644 --- a/ecmascript/module/js_module_source_text.h +++ b/ecmascript/module/js_module_source_text.h @@ -36,18 +36,28 @@ enum class ModuleTypes : uint8_t { UNKNOWN }; +enum class LoadingTypes : uint8_t { + STABLE_MODULE = 0x01, + DYNAMITC_MODULE, + OTHERS +}; + class SourceTextModule final : public ModuleRecord { public: static constexpr int UNDEFINED_INDEX = -1; + static constexpr size_t DEFAULT_DICTIONART_CAPACITY = 2; + static constexpr size_t DEFAULT_ARRAY_CAPACITY = 2; + static constexpr uint8_t DEREGISTER_MODULE_TAG = 1; CAST_CHECK(SourceTextModule, IsSourceTextModule); // 15.2.1.17 Runtime Semantics: HostResolveImportedModule ( referencingModule, specifier ) static JSHandle HostResolveImportedModule(JSThread *thread, - const JSHandle &module, - const JSHandle &moduleRequest); - static JSHandle HostResolveImportedModuleWithMerge( - JSThread *thread, const JSHandle &module, const JSHandle &moduleRequest); + const JSHandle &module, + const JSHandle &moduleRequest); + static JSHandle HostResolveImportedModuleWithMerge(JSThread *thread, + const JSHandle &module, + const JSHandle &moduleRequest); // 15.2.1.16.2 GetExportedNames(exportStarSet) static CVector GetExportedNames(JSThread *thread, const JSHandle &module, @@ -58,11 +68,12 @@ public: const JSHandle &exportName, CVector, JSHandle>> &resolveVector); static JSHandle ResolveExportObject(JSThread *thread, const JSHandle &module, - const JSHandle &exportObject, - const JSHandle &exportName); + const JSHandle &exportObject, + const JSHandle &exportName); // 15.2.1.16.4.1 InnerModuleInstantiation ( module, stack, index ) - static int InnerModuleInstantiation(JSThread *thread, const JSHandle &moduleRecord, - CVector> &stack, int index); + static int InnerModuleInstantiation(JSThread *thread, + const JSHandle &moduleRecord, CVector> &stack, + int index, bool excuteFromJob = false); // 15.2.1.16.4.2 ModuleDeclarationEnvironmentSetup ( module ) static void ModuleDeclarationEnvironmentSetup(JSThread *thread, const JSHandle &module); @@ -96,7 +107,7 @@ public: static void MakeInternalArgs(const EcmaVM *vm, std::vector> &arguments, const CString &moduleRequestName); static bool LoadNativeModule(JSThread *thread, JSHandle &requiredModule, - const JSHandle &moduleRequest, ModuleTypes moduleType); + const JSHandle &moduleRequest, ModuleTypes moduleType); inline static bool IsNativeModule(ModuleTypes moduleType) { return moduleType == ModuleTypes::OHOS_MODULE || @@ -127,9 +138,14 @@ public: static constexpr size_t STATUS_BITS = 3; static constexpr size_t MODULE_TYPE_BITS = 4; static constexpr size_t IS_NEW_BC_VERSION_BITS = 1; + static constexpr size_t LOADING_TYPE_BITS = 3; + static constexpr uint16_t REGISTER_COUNTS = 16; + FIRST_BIT_FIELD(BitField, Status, ModuleStatus, STATUS_BITS) NEXT_BIT_FIELD(BitField, Types, ModuleTypes, MODULE_TYPE_BITS, Status) NEXT_BIT_FIELD(BitField, IsNewBcVersion, bool, IS_NEW_BC_VERSION_BITS, Types) + NEXT_BIT_FIELD(BitField, LoadingTypes, LoadingTypes, LOADING_TYPE_BITS, IsNewBcVersion) + NEXT_BIT_FIELD(BitField, RegisterCounts, uint16_t, REGISTER_COUNTS, LoadingTypes) DECL_DUMP() DECL_VISIT_OBJECT(SOURCE_TEXT_MODULE_OFFSET, EVALUATION_ERROR_OFFSET) @@ -140,12 +156,14 @@ public: static int EvaluateForConcurrent(JSThread *thread, const JSHandle &module); // 15.2.1.16.4 Instantiate() - static int Instantiate(JSThread *thread, const JSHandle &moduleHdl); + static int Instantiate(JSThread *thread, const JSHandle &moduleHdl, + bool excuteFromJob = false); static void InstantiateCJS(JSThread *thread, const JSHandle ¤tModule, const JSHandle &requiredModule); static void InstantiateNativeModule(JSThread *thread, JSHandle ¤tModule, - JSHandle &requiredModule, const JSHandle &moduleRequest, - ModuleTypes moduleType); + JSHandle &requiredModule, + const JSHandle &moduleRequest, + ModuleTypes moduleType); JSTaggedValue GetModuleValue(JSThread *thread, int32_t index, bool isThrow); void StoreModuleValue(JSThread *thread, int32_t index, const JSHandle &value); @@ -157,8 +175,9 @@ public: const JSHandle &module, CVector, JSHandle>> &resolveVector); - static constexpr size_t DEFAULT_DICTIONART_CAPACITY = 2; - static constexpr size_t DEFAULT_ARRAY_CAPACITY = 2; + static JSTaggedValue GetModuleName(JSTaggedValue currentModule); + + static bool IsDynamicModule(LoadingTypes types); private: static void SetExportName(JSThread *thread, const JSHandle &moduleRequest, const JSHandle &module, diff --git a/ecmascript/module/module_data_extractor.cpp b/ecmascript/module/module_data_extractor.cpp index bfe7ffaecc91d6d12e003d550d77c84518eb0da8..a4d4d23da74136b58b8c48318c8766733fa21f43 100644 --- a/ecmascript/module/module_data_extractor.cpp +++ b/ecmascript/module/module_data_extractor.cpp @@ -28,6 +28,7 @@ namespace panda::ecmascript { using StringData = panda_file::StringData; using BuiltinsJson = builtins::BuiltinsJson; +using JSRecordInfo = ecmascript::JSPandaFile::JSRecordInfo; JSHandle ModuleDataExtractor::ParseModule(JSThread *thread, const JSPandaFile *jsPandaFile, const CString &descriptor, const CString &moduleFilename) @@ -126,8 +127,8 @@ JSHandle ModuleDataExtractor::ParseJsonModule(JSThread *thread, c return JSHandle::Cast(moduleRecord); } -JSHandle ModuleDataExtractor::ParseNativeModule(JSThread *thread, - const CString &moduleRequestName, ModuleTypes moduleType) +JSHandle ModuleDataExtractor::ParseNativeModule(JSThread *thread, const CString &moduleRequestName, + ModuleTypes moduleType) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle moduleRecord = factory->NewSourceTextModule(); @@ -153,7 +154,15 @@ JSTaggedValue ModuleDataExtractor::JsonParse(JSThread *thread, const JSPandaFile EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo( thread, undefined, undefined, undefined, 1); // 1 : argument numbers - CString value = jsPandaFile->GetJsonStringId(thread, entryPoint); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSRecordInfo recordInfo; + bool hasRecord = jsPandaFile->CheckAndGetRecordInfo(entryPoint, recordInfo); + if (!hasRecord) { + CString msg = "cannot find record '" + entryPoint + "', please check the request path."; + LOG_FULL(ERROR) << msg; + THROW_REFERENCE_ERROR_AND_RETURN(thread, msg.c_str(), JSTaggedValue::Exception()); + } + CString value = jsPandaFile->GetJsonStringId(recordInfo); JSHandle arg0(thread->GetEcmaVM()->GetFactory()->NewFromASCII(value)); info->SetCallArg(arg0.GetTaggedValue()); return BuiltinsJson::Parse(info); diff --git a/ecmascript/module/module_data_extractor.h b/ecmascript/module/module_data_extractor.h index 52855266b3e59bab628be71273279595a941b517..3d61af506146a7693f536aa59a113d2dfe069eef 100644 --- a/ecmascript/module/module_data_extractor.h +++ b/ecmascript/module/module_data_extractor.h @@ -38,9 +38,9 @@ public: const CString &descriptor, const CString &moduleFilename); static JSHandle ParseCjsModule(JSThread *thread, const JSPandaFile *jsPandaFile); static JSHandle ParseJsonModule(JSThread *thread, const JSPandaFile *jsPandaFile, - const CString &moduleFilename, const CString &recordName = nullptr); - static JSHandle ParseNativeModule(JSThread *thread, - const CString &moduleRequestName, ModuleTypes moduleType); + const CString &moduleFilename, const CString &recordName = nullptr); + static JSHandle ParseNativeModule(JSThread *thread, const CString &moduleRequestName, + ModuleTypes moduleType); static JSTaggedValue JsonParse(JSThread *thread, const JSPandaFile *jsPandaFile, CString entryPoint); }; } // namespace panda::ecmascript diff --git a/ecmascript/module/module_path_helper.cpp b/ecmascript/module/module_path_helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9f7f7a586fe7cf53badf9719e390a307ba081a15 --- /dev/null +++ b/ecmascript/module/module_path_helper.cpp @@ -0,0 +1,496 @@ +/* + * 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/module/module_path_helper.h" + +namespace panda::ecmascript { +/* + * This function use recordName, requestName to get baseFileName and entryPoint. + */ +CString ModulePathHelper::ConcatFileNameWithMerge(JSThread *thread, const JSPandaFile *jsPandaFile, + CString &baseFileName, CString recordName, CString requestName) +{ + CString entryPoint; + if (StringHelper::StringStartWith(requestName, PREFIX_BUNDLE)) { + // requestName: @bundle:bundleName/moduleName@namespace/ets/xxx/xxx + entryPoint = ParsePrefixBundle(thread, jsPandaFile, baseFileName, requestName, recordName); + } else if (StringHelper::StringStartWith(requestName, PREFIX_PACKAGE)) { + // requestName: @package:pkg_modules@namespace/xxx/Index + entryPoint = requestName.substr(PREFIX_PACKAGE_LEN); + } else if (IsImportFile(requestName)) { + // this branch save for require/dynamic import/old version sdk + // load a relative pathName. + // requestName: ./ || ./xxx/xxx.js || ../xxx/xxx.js || ./xxx/xxx + entryPoint = MakeNewRecord(jsPandaFile, baseFileName, recordName, requestName); + } else { + // this branch save for require/dynamic import/old version sdk + // requestName: requestPkgName + entryPoint = ParseThirdPartyPackage(jsPandaFile, recordName, requestName); + } + if (entryPoint.empty() && thread->GetEcmaVM()->EnableReportModuleResolvingFailure()) { + LOG_ECMA(ERROR) << "Failed to resolve the requested entryPoint. baseFileName : '" << baseFileName << + "'. RecordName : '" << recordName << "'. RequestName : '" << requestName << "'."; + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + CString msg = "failed to load module'" + requestName + "' which imported by '" + + recordName + "'. Please check the target path."; + JSTaggedValue error = factory->GetJSError(ErrorType::REFERENCE_ERROR, msg.c_str()).GetTaggedValue(); + THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, entryPoint); + } + LOG_ECMA(DEBUG) << "Concated baseFileName : '" << baseFileName << + "'. EntryPoint : '" << entryPoint << "'."; + return entryPoint; +} + +/* + * Before: inputFileName: 1. /data/storage/el1/bundle/moduleName/ets/xxx/xxx.abc + 2. @bundle:bundleName/moduleName/ets/xxx/xxx.abc + 3. moduleName/ets/xxx/xxx.abc + 4. .test/xxx/xxx.abc + 5. xxx/xxx.abc + * After: outBaseFileName: /data/storage/el1/bundle/moduleName/ets/modules.abc + * outEntryPoint: bundleName/moduleName/ets/xxx/xxx + */ +void ModulePathHelper::ParseOhmUrl(EcmaVM *vm, const CString &inputFileName, + CString &outBaseFileName, CString &outEntryPoint) +{ + CString bundleInstallName(BUNDLE_INSTALL_PATH); + size_t startStrLen = bundleInstallName.length(); + size_t pos = CString::npos; + + if (inputFileName.length() > startStrLen && inputFileName.compare(0, startStrLen, bundleInstallName) == 0) { + pos = startStrLen; + } + if (pos != CString::npos) { + // inputFileName: /data/storage/el1/bundle/moduleName/ets/xxx/xxx.abc + pos = inputFileName.find(PathHelper::SLASH_TAG, startStrLen); + if (pos == CString::npos) { + LOG_FULL(FATAL) << "Invalid Ohm url, please check."; + } + CString moduleName = inputFileName.substr(startStrLen, pos - startStrLen); + outBaseFileName = BUNDLE_INSTALL_PATH + moduleName + MERGE_ABC_ETS_MODULES; + outEntryPoint = vm->GetBundleName() + PathHelper::SLASH_TAG + inputFileName.substr(startStrLen); + } else { + // Temporarily handle the relative path sent by arkui + // inputFileName: @bundle:bundleName/moduleName/ets/xxx/xxx.abc + if (StringHelper::StringStartWith(inputFileName, PREFIX_BUNDLE)) { + outEntryPoint = inputFileName.substr(PREFIX_BUNDLE_LEN); + outBaseFileName = ParseUrl(vm, outEntryPoint); + } else { +#if !defined(PANDA_TARGET_WINDOWS) && !defined(PANDA_TARGET_MACOS) + // inputFileName: moduleName/ets/xxx/xxx.abc + outEntryPoint = vm->GetBundleName() + PathHelper::SLASH_TAG + inputFileName; +#else + // if the inputFileName starts with '.test', the preview test page is started. + // in this case, the path ets does not need to be combined. + // inputFileName: .test/xxx/xxx.abc + if (StringHelper::StringStartWith(inputFileName, PREVIER_TEST_DIR)) { + outEntryPoint = vm->GetBundleName() + PathHelper::SLASH_TAG + vm->GetModuleName() + + PathHelper::SLASH_TAG + inputFileName; + } else { + // inputFileName: xxx/xxx.abc + outEntryPoint = vm->GetBundleName() + PathHelper::SLASH_TAG + vm->GetModuleName() + + MODULE_DEFAULE_ETS + inputFileName; + } +#endif + } + } + if (StringHelper::StringEndWith(outEntryPoint, EXT_NAME_ABC)) { + outEntryPoint.erase(outEntryPoint.length() - EXT_NAME_ABC_LEN, EXT_NAME_ABC_LEN); + } +} + +/* + * Before: recordName: bundleName/moduleName@namespace/moduleName/xxx/xxx.abc + * After: Intra-application cross hap: /data/storage/el1/bundle/bundleName/ets/modules.abc + * Cross-application: /data/storage/el1/bundle/bundleName/moduleName/moduleName/ets/modules.abc + */ +CString ModulePathHelper::ParseUrl(EcmaVM *vm, const CString &recordName) +{ + CVector vec; + StringHelper::SplitString(recordName, vec, 0, SEGMENTS_LIMIT_TWO); + if (vec.size() < SEGMENTS_LIMIT_TWO) { + LOG_ECMA(DEBUG) << "ParseUrl SplitString filed, please check Url" << recordName; + return CString(); + } + CString bundleName = vec[0]; + CString moduleName = vec[1]; + PathHelper::DeleteNamespace(moduleName); + + CString baseFileName; + if (bundleName != vm->GetBundleName()) { + // Cross-application + baseFileName = BUNDLE_INSTALL_PATH + bundleName + PathHelper::SLASH_TAG + moduleName + + PathHelper::SLASH_TAG + moduleName + MERGE_ABC_ETS_MODULES; + } else { + // Intra-application cross hap + baseFileName = BUNDLE_INSTALL_PATH + moduleName + MERGE_ABC_ETS_MODULES; + } + return baseFileName; +} + +/* + * Before: moduleRequestName: @bundle:bundleName/moduleName@namespace/ets/xxx + * After: baseFileName: 1./data/storage/el1/bundle/bundleName/ets/modules.abc + * 2./data/storage/el1/bundle/bundleName/moduleName/moduleName/ets/modules.abc + * entryPoint: bundleName/moduleName@namespace/ets/xxx + */ +CString ModulePathHelper::ParsePrefixBundle(JSThread *thread, const JSPandaFile *jsPandaFile, + [[maybe_unused]] CString &baseFileName, CString moduleRequestName, [[maybe_unused]] CString recordName) +{ + EcmaVM *vm = thread->GetEcmaVM(); + moduleRequestName = moduleRequestName.substr(PREFIX_BUNDLE_LEN); + CString entryPoint = moduleRequestName; + if (jsPandaFile->IsRecordWithBundleName()) { + CVector vec; + StringHelper::SplitString(moduleRequestName, vec, 0, SEGMENTS_LIMIT_TWO); + if (vec.size() < SEGMENTS_LIMIT_TWO) { + LOG_ECMA(INFO) << "SplitString filed, please check moduleRequestName"; + return CString(); + } + CString bundleName = vec[0]; + CString moduleName = vec[1]; + PathHelper::DeleteNamespace(moduleName); + +#if !defined(PANDA_TARGET_WINDOWS) && !defined(PANDA_TARGET_MACOS) + if (bundleName != vm->GetBundleName()) { + baseFileName = BUNDLE_INSTALL_PATH + bundleName + PathHelper::SLASH_TAG + moduleName + + PathHelper::SLASH_TAG + moduleName + MERGE_ABC_ETS_MODULES; + } else if (moduleName != vm->GetModuleName()) { + baseFileName = BUNDLE_INSTALL_PATH + moduleName + MERGE_ABC_ETS_MODULES; + } else { + // Support multi-module card service + baseFileName = vm->GetAssetPath(); + } +#else + CVector currentVec; + StringHelper::SplitString(moduleRequestName, currentVec, 0, SEGMENTS_LIMIT_TWO); + if (currentVec.size() < SEGMENTS_LIMIT_TWO) { + LOG_ECMA(INFO) << "SplitString filed, please check recordName"; + return CString(); + } + CString currentModuleName = currentVec[1]; + PathHelper::DeleteNamespace(currentModuleName); + if (bundleName != vm->GetBundleName()) { + entryPoint = PREVIEW_OF_ACROSS_HAP_FLAG; + if (vm->EnableReportModuleResolvingFailure()) { + CString msg = "[ArkRuntime Log] Cannot preview this HSP module as" \ + "it is imported from outside the current application."; + LOG_NO_TAG(ERROR) << msg; + } + } else if (currentModuleName != vm->GetModuleName()) { + baseFileName = BUNDLE_INSTALL_PATH + moduleName + MERGE_ABC_ETS_MODULES; + } +#endif + } else { + PathHelper::AdaptOldIsaRecord(entryPoint); + } + return entryPoint; +} + +/* + * Before: requestName: ../xxx1/xxx2 || ./b + * recordName: pkg_modules/.ohpm/pkgName/pkg_modules/pkgName/xxx1/xxx3 || a + * After: entryPoint: pkg_modules/.ohpm/pkgName/pkg_modules/pkgName/xxx1/xxx2 || b + * baseFileName: /data/storage/el1/bundle/moduleName/ets/modules.abc || /home/user/src/a + */ +CString ModulePathHelper::MakeNewRecord(const JSPandaFile *jsPandaFile, CString &baseFileName, + const CString &recordName, const CString &requestName) +{ + CString entryPoint; + CString moduleRequestName = RemoveSuffix(requestName); + size_t pos = moduleRequestName.find(PathHelper::CURRENT_DIREATORY_TAG); + if (pos == 0) { + moduleRequestName = moduleRequestName.substr(2); // 2 means jump "./" + } + pos = recordName.rfind(PathHelper::SLASH_TAG); + if (pos != CString::npos) { + // entryPoint: pkg_modules/.ohpm/pkgName/pkg_modules/pkgName/xxx1/../xxx1/xxx2 + entryPoint = recordName.substr(0, pos + 1) + moduleRequestName; + } else { + entryPoint = moduleRequestName; + } + // entryPoint: pkg_modules/.ohpm/pkgName/pkg_modules/pkgName/xxx1/xxx2 + entryPoint = PathHelper::NormalizePath(entryPoint); + entryPoint = ConfirmLoadingIndexOrNot(jsPandaFile, entryPoint); + if (!entryPoint.empty()) { + return entryPoint; + } + // the package name may have a '.js' suffix, try to parseThirdPartyPackage + entryPoint = ParseThirdPartyPackage(jsPandaFile, recordName, requestName); + if (!entryPoint.empty()) { + return entryPoint; + } + // Execute abc locally + pos = baseFileName.rfind(PathHelper::SLASH_TAG); + if (pos != CString::npos) { + baseFileName = baseFileName.substr(0, pos + 1) + moduleRequestName + EXT_NAME_ABC; + } else { + baseFileName = moduleRequestName + EXT_NAME_ABC; + } + pos = moduleRequestName.rfind(PathHelper::SLASH_TAG); + if (pos != CString::npos) { + entryPoint = moduleRequestName.substr(pos + 1); + } else { + entryPoint = moduleRequestName; + } + return entryPoint; +} + +/* + * Before: ohpmPath: pkg_modules/.ohpm/pkgName/pkg_modules + * requestName: requestPkgName + * After: entryPoint: pkg_modules/.ohpm/requestPkgName/pkg_modules/requestPkgName/xxx + */ +CString ModulePathHelper::FindOhpmEntryPoint(const JSPandaFile *jsPandaFile, + const CString& ohpmPath, const CString& requestName) +{ + CVector vec; + StringHelper::SplitString(requestName, vec, 0); + size_t maxIndex = vec.size() - 1; + CString ohpmKey; + size_t index = 0; + // first we find the ohpmKey by splicing the requestName + while (index <= maxIndex) { + // maybeKey: pkg_modules/.ohpm/pkgName/pkg_modules/requestPkgName + CString maybeKey = ohpmPath + PathHelper::SLASH_TAG + StringHelper::JoinString(vec, 0, index); + // ohpmKey: pkg_modules/.ohpm/requestPkgName/pkg_modules/requestPkgName + ohpmKey = jsPandaFile->GetNpmEntries(maybeKey); + if (!ohpmKey.empty()) { + break; + } + ++index; + } + if (ohpmKey.empty()) { + return CString(); + } + // second If the ohpmKey is not empty, we will use it to obtain the real entrypoint + CString entryPoint; + if (index == maxIndex) { + // requestName is a packageName + // entryPoint: pkg_modules/.ohpm/requestPkgName/pkg_modules/requestPkgName/xxx + entryPoint = jsPandaFile->GetEntryPoint(ohpmKey); + } else { + // import a specific file or directory + ohpmKey = ohpmKey + PathHelper::SLASH_TAG + StringHelper::JoinString(vec, index + 1, maxIndex); + entryPoint = ConfirmLoadingIndexOrNot(jsPandaFile, ohpmKey); + } + return entryPoint; +} + +CString ModulePathHelper::FindPackageInTopLevelWithNamespace(const JSPandaFile *jsPandaFile, + const CString& requestName, const CString &recordName) +{ + // find in current module @[moduleName|namespace]/ + CString entryPoint; + CString ohpmPath; + if (StringHelper::StringStartWith(recordName, PACKAGE_PATH_SEGMENT)) { + size_t pos = recordName.find(PathHelper::SLASH_TAG); + if (pos == CString::npos) { + LOG_ECMA(DEBUG) << "wrong recordname : " << recordName; + return CString(); + } + ohpmPath = recordName.substr(0, pos); + entryPoint = FindOhpmEntryPoint(jsPandaFile, recordName.substr(0, pos), requestName); + } else { + CVector vec; + StringHelper::SplitString(recordName, vec, 0, SEGMENTS_LIMIT_TWO); + if (vec.size() < SEGMENTS_LIMIT_TWO) { + LOG_ECMA(DEBUG) << "SplitString filed, please check moduleRequestName"; + return CString(); + } + CString moduleName = vec[1]; + // If namespace exists, use namespace as moduleName + size_t pos = moduleName.find(PathHelper::NAME_SPACE_TAG); + if (pos != CString::npos) { + moduleName = moduleName.substr(pos + 1); + } + ohpmPath = CString(PACKAGE_PATH_SEGMENT) + PathHelper::NAME_SPACE_TAG + moduleName; + entryPoint = FindOhpmEntryPoint(jsPandaFile, ohpmPath, requestName); + } + if (!entryPoint.empty()) { + return entryPoint; + } + // find in project directory / + return FindOhpmEntryPoint(jsPandaFile, PACKAGE_PATH_SEGMENT, requestName); +} + +/* + * Before: requestName: requestPkgName + * recordName: pkg_modules/.ohpm/pkgName/pkg_modules/pkgName/xxx + * After: entryPoint: pkg_modules/.ohpm/requestPkgName/pkg_modules/requestPkgName/xxx + */ +CString ModulePathHelper::ParseOhpmPackage(const JSPandaFile *jsPandaFile, + const CString &recordName, const CString &requestName) +{ + CString entryPoint; + if (StringHelper::StringStartWith(recordName, PACKAGE_PATH_SEGMENT)) { + // this way is thirdPartyPackage import ThirdPartyPackage + auto info = const_cast(jsPandaFile)->FindRecordInfo(recordName); + // packageName: pkg_modules/.ohpm/pkgName/pkg_modules/pkgName + CString packageName = info.npmPackageName; + size_t pos = packageName.rfind(PACKAGE_PATH_SEGMENT); + if (pos != CString::npos) { + packageName.erase(pos, packageName.size() - pos); + // ohpmPath: pkg_modules/.ohpm/pkgName/pkg_modules + CString ohpmPath = packageName + PACKAGE_PATH_SEGMENT; + // entryPoint: pkg_modules/.ohpm/requestPkgName/pkg_modules/requestPkgName/xxx + entryPoint = FindOhpmEntryPoint(jsPandaFile, ohpmPath, requestName); + if (!entryPoint.empty()) { + return entryPoint; + } + } + } + // Import packages under the current module or project directory + return FindPackageInTopLevelWithNamespace(jsPandaFile, requestName, recordName); +} + +/* + * Before: requestName: requestPkgName + * recordName: pkg_modules/.ohpm/pkgName/pkg_modules/pkgName/xxx + * packagePath: pkg_modules || node_modules + * After: entryPoint: pkg_modules/.ohpm/requestPkgName/pkg_modules/requestPkgName/xxx + */ +CString ModulePathHelper::ParseThirdPartyPackage(const JSPandaFile *jsPandaFile, + const CString &recordName, const CString &requestName, const CString &packagePath) +{ + CString entryPoint; + if (StringHelper::StringStartWith(recordName, packagePath)) { + auto info = const_cast(jsPandaFile)->FindRecordInfo(recordName); + CString packageName = info.npmPackageName; // pkg_modules/.ohpm/pkgName/pkg_modules/pkgName + size_t pos = 0; + while (true) { + CString key = packageName + PathHelper::SLASH_TAG + packagePath + PathHelper::SLASH_TAG + requestName; + entryPoint = FindNpmEntryPoint(jsPandaFile, key); + if (!entryPoint.empty()) { + return entryPoint; + } + pos = packageName.rfind(packagePath) - 1; + if (pos == CString::npos || pos < 0) { + break; + } + packageName.erase(pos, packageName.size() - pos); + } + } + return FindPackageInTopLevel(jsPandaFile, requestName, packagePath); +} + +/* + * Before: requestName: requestPkgName + * recordName: pkg_modules/.ohpm/pkgName/pkg_modules/pkgName/xxx + * After: entryPoint: pkg_modules/.ohpm/requestPkgName/pkg_modules/requestPkgName/xxx + */ +CString ModulePathHelper::ParseThirdPartyPackage(const JSPandaFile *jsPandaFile, + const CString &recordName, const CString &requestName) +{ + // We need to deal with scenarios like this 'json5/' -> 'json5' + CString normalizeRequestName = PathHelper::NormalizePath(requestName); + CString entryPoint = ParseOhpmPackage(jsPandaFile, recordName, normalizeRequestName); + if (!entryPoint.empty()) { + return entryPoint; + } + + static CVector packagePaths = {CString(PACKAGE_PATH_SEGMENT), CString(NPM_PATH_SEGMENT)}; + // Package compatible with old soft link format + for (size_t i = 0; i < packagePaths.size(); ++i) { + entryPoint = ParseThirdPartyPackage(jsPandaFile, recordName, normalizeRequestName, packagePaths[i]); + if (!entryPoint.empty()) { + return entryPoint; + } + } + return CString(); +} + +/* + * Before: dirPath: Undefined + * fileName: Undefined + * After: dirPath: pkg_modules/.ohpm/pkgName/pkg_modules/pkgName/xxx/ + * fileName: pkg_modules/.ohpm/pkgName/pkg_modules/pkgName/xxx/xxx.abc + */ +void ModulePathHelper::ResolveCurrentPath(JSThread *thread, JSMutableHandle &dirPath, + JSMutableHandle &fileName, const JSPandaFile *jsPandaFile) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + CString fullName = jsPandaFile->GetJSPandaFileDesc(); + JSHandle dirPathName = PathHelper::ResolveDirPath(thread, fullName); + dirPath.Update(dirPathName.GetTaggedValue()); + + // Get filename from JSPandaFile + JSHandle cbFileName = factory->NewFromUtf8(fullName); + fileName.Update(cbFileName.GetTaggedValue()); +} + +CString ModulePathHelper::FindNpmEntryPoint(const JSPandaFile *jsPandaFile, const CString &packageEntryPoint) +{ + // if we are currently importing a specific file or directory, we will get the entryPoint here + CString entryPoint = ConfirmLoadingIndexOrNot(jsPandaFile, packageEntryPoint); + if (!entryPoint.empty()) { + return entryPoint; + } + // When you come here, must import a packageName + return jsPandaFile->GetEntryPoint(packageEntryPoint); +} + +/* + * Before: requestName: requestPkgName + * packagePath: pkg_modules || node_modules + * After: entryPoint: pkg_modules/.ohpm/requestPkgName/pkg_modules/requestPkgName/xxx + */ +CString ModulePathHelper::FindPackageInTopLevel(const JSPandaFile *jsPandaFile, + const CString& requestName, const CString &packagePath) +{ + // we find packagePath/0/xxx or packagePath/1/xxx + CString entryPoint; + for (size_t level = 0; level <= MAX_PACKAGE_LEVEL; ++level) { + CString levelStr = std::to_string(level).c_str(); + CString key = packagePath + PathHelper::SLASH_TAG + levelStr + PathHelper::SLASH_TAG + requestName; + entryPoint = FindNpmEntryPoint(jsPandaFile, key); + if (!entryPoint.empty()) { + return entryPoint; + } + } + return CString(); +} + +bool ModulePathHelper::IsImportFile(const CString &moduleRequestName) +{ + if (moduleRequestName[0] == PathHelper::POINT_TAG) { + return true; + } + size_t pos = moduleRequestName.rfind(PathHelper::POINT_TAG); + if (pos != CString::npos) { + CString suffix = moduleRequestName.substr(pos); + if (suffix == EXT_NAME_JS || suffix == EXT_NAME_TS || suffix == EXT_NAME_ETS || suffix == EXT_NAME_JSON) { + return true; + } + } + return false; +} + +/* + * Before: xxx/xxx.js || xxx/xxx.ts || xxx/xxx.ets ||xxx/xxx.json + * After: xxx/xxx + */ +CString ModulePathHelper::RemoveSuffix(const CString &requestName) +{ + CString res = requestName; + size_t pos = res.rfind(PathHelper::POINT_TAG); + if (pos != CString::npos) { + CString suffix = res.substr(pos); + if (suffix == EXT_NAME_JS || suffix == EXT_NAME_TS || suffix == EXT_NAME_ETS || suffix == EXT_NAME_JSON) { + res.erase(pos, suffix.length()); + } + } + return res; +} +} // namespace panda::ecmascript::base \ No newline at end of file diff --git a/ecmascript/module/module_path_helper.h b/ecmascript/module/module_path_helper.h new file mode 100644 index 0000000000000000000000000000000000000000..a10f92efa48f9ab039463ed89af32a862221449a --- /dev/null +++ b/ecmascript/module/module_path_helper.h @@ -0,0 +1,174 @@ +/* + * 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_MODULE_MODULE_PATH_HELPER_H +#define ECMASCRIPT_MODULE_MODULE_PATH_HELPER_H + +#include "ecmascript/base/path_helper.h" + +#include "ecmascript/compiler/aot_file/aot_file_manager.h" +#include "ecmascript/base/string_helper.h" +#include "ecmascript/ecma_macros.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/global_env.h" +#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/jspandafile/js_pandafile.h" +/* + * Intra-application cross hap: + * baseFileName = 'data/storage/el1/bundle/moduleName/ets/modules.abc'; + * cross-application: + * baseFileName = 'data/storage/el1/bundle/bundleName/moduleName/moduleName/ets/modules.abc'; + * recordName = bundleName/moduleName/xxx(entry)/xxx(ets)/xxx(pages)/xxx specific abc file + * + * ohmUrl: It's an index information that can uniquely identify module files. + * Current ohmUrl has the following five different prefixs: + * 1. @bundle:... Identify OpenHarmony modules. + * {project_path}\entry\src\main\ets\pages\Index --> @bundle:bundleName/moduleName/ets/pages/Index + * @namespace: needs to add when import local har or ohosTest import entry file. + * {project_path}\namespace\src\main\ets\pages\Index --> @bundle:bundleName/moduleName@namespace/ets/pages/Index + * + * 2. @package:... Identify open source third party modules. + * {project_path}\node_modules.ohpm\pkgName\oh_modules\pkgName\xxx\xxx + * --> @package:pkg_modules/.ohpm/pkgName/pkg_modules/pkgName/xxx/xxx + * + * 3. @app:... Identify c++ modules in application. + * libxxx.so --> @app:bundleName/moduleName/xxx + * + * 4. @native:... Identify system builtin modules. + * system.app --> @native:system.app + * + * 5. @ohos:... Identify ohos builtin modules. + * @ohos:hilog --> @ohos:hilog + */ + +namespace panda::ecmascript { +using PathHelper = base::PathHelper; +using StringHelper = base::StringHelper; + +class ModulePathHelper { +public: + static constexpr char EXT_NAME_ABC[] = ".abc"; + static constexpr char EXT_NAME_ETS[] = ".ets"; + static constexpr char EXT_NAME_TS[] = ".ts"; + static constexpr char EXT_NAME_JS[] = ".js"; + static constexpr char EXT_NAME_JSON[] = ".json"; + static constexpr char PREFIX_BUNDLE[] = "@bundle:"; + static constexpr char PREFIX_MODULE[] = "@module:"; + static constexpr char PREFIX_PACKAGE[] = "@package:"; + static constexpr char REQUIRE_NAITVE_MODULE_PREFIX[] = "@native:"; + static constexpr char REQUIRE_NAPI_OHOS_PREFIX[] = "@ohos:"; + static constexpr char REQUIRE_NAPI_APP_PREFIX[] = "@app:"; + static constexpr char NPM_PATH_SEGMENT[] = "node_modules"; + static constexpr char PACKAGE_PATH_SEGMENT[] = "pkg_modules"; + static constexpr char PACKAGE_ENTRY_FILE[] = "/index"; + static constexpr char BUNDLE_INSTALL_PATH[] = "/data/storage/el1/bundle/"; + static constexpr char MERGE_ABC_ETS_MODULES[] = "/ets/modules.abc"; + static constexpr char MODULE_DEFAULE_ETS[] = "/ets/"; + static constexpr char BUNDLE_SUB_INSTALL_PATH[] = "/data/storage/el1/"; + static constexpr char PREVIEW_OF_ACROSS_HAP_FLAG[] = "[preview]"; + static constexpr char PREVIER_TEST_DIR[] = ".test"; + + static constexpr size_t MAX_PACKAGE_LEVEL = 1; + static constexpr size_t SEGMENTS_LIMIT_TWO = 2; + static constexpr size_t EXT_NAME_ABC_LEN = 4; + static constexpr size_t EXT_NAME_ETS_LEN = 4; + static constexpr size_t EXT_NAME_TS_LEN = 3; + static constexpr size_t EXT_NAME_JS_LEN = 3; + static constexpr size_t EXT_NAME_JSON_LEN = 5; + static constexpr size_t PREFIX_BUNDLE_LEN = 8; + static constexpr size_t PREFIX_MODULE_LEN = 8; + static constexpr size_t PREFIX_PACKAGE_LEN = 9; + static constexpr size_t NATIVE_PREFIX_SIZE = 8; + static constexpr size_t OHOS_PREFIX_SIZE = 6; + static constexpr size_t APP_PREFIX_SIZE = 5; + + static CString ConcatFileNameWithMerge(JSThread *thread, const JSPandaFile *jsPandaFile, + CString &baseFileName, CString recordName, CString requestName); + static void ParseOhmUrl(EcmaVM *vm, const CString &inputFileName, + CString &outBaseFileName, CString &outEntryPoint); + static CString ParseUrl(EcmaVM *vm, const CString &recordName); + static CString ParsePrefixBundle(JSThread *thread, const JSPandaFile *jsPandaFile, + [[maybe_unused]] CString &baseFileName, CString moduleRequestName, [[maybe_unused]] CString recordName); + static CString MakeNewRecord(const JSPandaFile *jsPandaFile, CString &baseFileName, + const CString &recordName, const CString &requestName); + static CString FindOhpmEntryPoint(const JSPandaFile *jsPandaFile, const CString &ohpmPath, + const CString &requestName); + static CString FindPackageInTopLevelWithNamespace(const JSPandaFile *jsPandaFile, const CString &requestName, + const CString &recordName); + static CString ParseOhpmPackage(const JSPandaFile *jsPandaFile, const CString &recordName, + const CString &requestName); + static CString ParseThirdPartyPackage(const JSPandaFile *jsPandaFile, const CString &recordName, + const CString &requestName, const CString &packagePath); + static CString ParseThirdPartyPackage(const JSPandaFile *jsPandaFile, const CString &recordName, + const CString &requestName); + static void ResolveCurrentPath(JSThread *thread, JSMutableHandle &dirPath, + JSMutableHandle &fileName, const JSPandaFile *jsPandaFile); + static CString FindNpmEntryPoint(const JSPandaFile *jsPandaFile, const CString &packageEntryPoint); + static CString FindPackageInTopLevel(const JSPandaFile *jsPandaFile, const CString &requestName, + const CString &packagePath); + static bool IsImportFile(const CString &moduleRequestName); + static CString RemoveSuffix(const CString &requestName); + + /* + * Before: data/storage/el1/bundle/moduleName/ets/modules.abc + * After: bundle/moduleName + */ + inline static std::string ParseHapPath(const CString &baseFileName) + { + CString bundleSubInstallName(BUNDLE_SUB_INSTALL_PATH); + size_t startStrLen = bundleSubInstallName.length(); + if (baseFileName.length() > startStrLen && baseFileName.compare(0, startStrLen, bundleSubInstallName) == 0) { + CString hapPath = baseFileName.substr(startStrLen); + size_t pos = hapPath.find(MERGE_ABC_ETS_MODULES); + if (pos != CString::npos) { + return hapPath.substr(0, pos).c_str(); + } + } + return std::string(); + } + + /* + * Before: xxx + * After: xxx || xxx/index + */ + inline static CString ConfirmLoadingIndexOrNot(const JSPandaFile *jsPandaFile, const CString &packageEntryPoint) + { + CString entryPoint = packageEntryPoint; + if (jsPandaFile->HasRecord(entryPoint)) { + return entryPoint; + } + // Possible import directory + entryPoint += PACKAGE_ENTRY_FILE; + if (jsPandaFile->HasRecord(entryPoint)) { + return entryPoint; + } + return CString(); + } + + inline static bool IsNativeModuleRequest(const CString &requestName) + { + if (requestName[0] != PathHelper::NAME_SPACE_TAG) { + return false; + } + if (StringHelper::StringStartWith(requestName, ModulePathHelper::REQUIRE_NAPI_OHOS_PREFIX) || + StringHelper::StringStartWith(requestName, ModulePathHelper::REQUIRE_NAPI_APP_PREFIX) || + StringHelper::StringStartWith(requestName, ModulePathHelper::REQUIRE_NAITVE_MODULE_PREFIX)) { + return true; + } + return false; + } +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_MODULE_MODULE_PATH_HELPER_H \ No newline at end of file diff --git a/ecmascript/module/tests/ecma_module_test.cpp b/ecmascript/module/tests/ecma_module_test.cpp index c538170b8cc5a344df2c3cf42d2ca58ffa1e1acb..fcac7620ac8801dcd252154b43641bdac5384fe7 100644 --- a/ecmascript/module/tests/ecma_module_test.cpp +++ b/ecmascript/module/tests/ecma_module_test.cpp @@ -25,6 +25,7 @@ #include "ecmascript/module/js_module_manager.h" #include "ecmascript/module/js_module_source_text.h" #include "ecmascript/module/module_data_extractor.h" +#include "ecmascript/module/module_path_helper.h" #include "ecmascript/tests/test_helper.h" #include "ecmascript/linked_hash_table.h" @@ -32,7 +33,7 @@ using namespace panda::ecmascript; using namespace panda::panda_file; using namespace panda::pandasm; -using PathHelper = panda::ecmascript::base::PathHelper; + namespace panda::test { class EcmaModuleTest : public testing::Test { public: @@ -255,6 +256,11 @@ HWTEST_F_L0(EcmaModuleTest, PreventExtensions_IsExtensible) JSHandle localExportEntry2 = objectFactory->NewLocalExportEntry(); SourceTextModule::AddLocalExportEntry(thread, module, localExportEntry2, 1, 2); JSHandle localExportEntries(thread, module->GetLocalExportEntries()); + CString baseFileName = "a.abc"; + JSHandle moduleFilename = objectFactory->NewFromUtf8(baseFileName); + module->SetEcmaModuleFilename(thread, moduleFilename); + ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager(); + moduleManager->AddResolveImportedModule(baseFileName, JSHandle::Cast(module)); JSHandle np = ModuleNamespace::ModuleNamespaceCreate(thread, JSHandle::Cast(module), localExportEntries); EXPECT_FALSE(np->IsExtensible()); @@ -299,14 +305,14 @@ HWTEST_F_L0(EcmaModuleTest, ConcatFileNameWithMerge1) CString moduleRecordName = "moduleTest1"; CString moduleRequestName = "@bundle:com.bundleName.test/moduleName/requestModuleName1"; CString result = "com.bundleName.test/moduleName/requestModuleName1"; - CString entryPoint = PathHelper::ConcatFileNameWithMerge(thread, pf.get(), baseFilename, moduleRecordName, + CString entryPoint = ModulePathHelper::ConcatFileNameWithMerge(thread, pf.get(), baseFilename, moduleRecordName, moduleRequestName); EXPECT_EQ(result, entryPoint); // Test cross application moduleRecordName = "@bundle:com.bundleName1.test/moduleName/requestModuleName1"; CString newBaseFileName = "/data/storage/el1/bundle/com.bundleName.test/moduleName/moduleName/ets/modules.abc"; - PathHelper::ConcatFileNameWithMerge(thread, pf.get(), baseFilename, moduleRecordName, moduleRequestName); + ModulePathHelper::ConcatFileNameWithMerge(thread, pf.get(), baseFilename, moduleRecordName, moduleRequestName); EXPECT_EQ(baseFilename, newBaseFileName); } @@ -331,7 +337,7 @@ HWTEST_F_L0(EcmaModuleTest, ConcatFileNameWithMerge2) CString moduleRequestName = "./requestModule.js"; CString result = "requestModule"; pf->InsertJSRecordInfo(result); - CString entryPoint = PathHelper::ConcatFileNameWithMerge(thread, pf.get(), baseFilename, moduleRecordName, + CString entryPoint = ModulePathHelper::ConcatFileNameWithMerge(thread, pf.get(), baseFilename, moduleRecordName, moduleRequestName); EXPECT_EQ(result, entryPoint); @@ -340,7 +346,7 @@ HWTEST_F_L0(EcmaModuleTest, ConcatFileNameWithMerge2) moduleRequestName = "./requestModule.js"; result = "moduleName/requestModule"; pf->InsertJSRecordInfo(result); - entryPoint = PathHelper::ConcatFileNameWithMerge( + entryPoint = ModulePathHelper::ConcatFileNameWithMerge( thread, pf.get(), baseFilename, moduleRecordName, moduleRequestName); EXPECT_EQ(result, entryPoint); } @@ -367,7 +373,7 @@ HWTEST_F_L0(EcmaModuleTest, ConcatFileNameWithMerge3) CString result = "secord"; CString requestFileName = "secord.abc"; CString entryPoint = - PathHelper::ConcatFileNameWithMerge(thread, pf.get(), baseFilename, moduleRecordName, moduleRequestName); + ModulePathHelper::ConcatFileNameWithMerge(thread, pf.get(), baseFilename, moduleRecordName, moduleRequestName); EXPECT_EQ(baseFilename, requestFileName); EXPECT_EQ(result, entryPoint); @@ -380,7 +386,7 @@ HWTEST_F_L0(EcmaModuleTest, ConcatFileNameWithMerge3) moduleRequestName = "./test/secord.js"; result = "secord"; requestFileName = "test/test/secord.abc"; - entryPoint = PathHelper::ConcatFileNameWithMerge(thread, pf2.get(), baseFilename, moduleRecordName, + entryPoint = ModulePathHelper::ConcatFileNameWithMerge(thread, pf2.get(), baseFilename, moduleRecordName, moduleRequestName); EXPECT_EQ(baseFilename, requestFileName); EXPECT_EQ(result, entryPoint); @@ -410,7 +416,7 @@ HWTEST_F_L0(EcmaModuleTest, ConcatFileNameWithMerge4) info.npmPackageName = "node_modules/0/moduleTest4"; const_cast &>(recordInfo).insert({moduleRecordName, info}); const_cast &>(recordInfo).insert({result, info}); - CString entryPoint = PathHelper::ConcatFileNameWithMerge(thread, pf.get(), baseFilename, moduleRecordName, + CString entryPoint = ModulePathHelper::ConcatFileNameWithMerge(thread, pf.get(), baseFilename, moduleRecordName, moduleRequestName); EXPECT_EQ(result, entryPoint); } @@ -453,13 +459,14 @@ HWTEST_F_L0(EcmaModuleTest, ParseOhmUrl) CString inputFileName = "moduleName/ets/pages/index.abc"; CString outFileName = ""; CString res1 = "com.bundleName.test/moduleName/ets/pages/index"; - CString entryPoint = PathHelper::ParseOhmUrl(instance, inputFileName, outFileName); + CString entryPoint; + ModulePathHelper::ParseOhmUrl(instance, inputFileName, outFileName, entryPoint); EXPECT_EQ(entryPoint, res1); EXPECT_EQ(outFileName, ""); // new pages url inputFileName = "@bundle:com.bundleName.test/moduleName/ets/pages/index.abc"; - entryPoint = PathHelper::ParseOhmUrl(instance, inputFileName, outFileName); + ModulePathHelper::ParseOhmUrl(instance, inputFileName, outFileName, entryPoint); EXPECT_EQ(entryPoint, res1); EXPECT_EQ(outFileName, "/data/storage/el1/bundle/moduleName/ets/modules.abc"); @@ -467,7 +474,7 @@ HWTEST_F_L0(EcmaModuleTest, ParseOhmUrl) inputFileName = "@bundle:com.bundleName.test/moduleName1/ets/pages/index.abc"; CString outRes = "/data/storage/el1/bundle/moduleName1/ets/modules.abc"; CString res2 = "com.bundleName.test/moduleName1/ets/pages/index"; - entryPoint = PathHelper::ParseOhmUrl(instance, inputFileName, outFileName); + ModulePathHelper::ParseOhmUrl(instance, inputFileName, outFileName, entryPoint); EXPECT_EQ(entryPoint, res2); EXPECT_EQ(outFileName, outRes); @@ -475,7 +482,7 @@ HWTEST_F_L0(EcmaModuleTest, ParseOhmUrl) inputFileName = "@bundle:com.bundleName.test1/moduleName1/ets/pages/index.abc"; CString outRes1 = "/data/storage/el1/bundle/com.bundleName.test1/moduleName1/moduleName1/ets/modules.abc"; CString res3 = "com.bundleName.test1/moduleName1/ets/pages/index"; - entryPoint = PathHelper::ParseOhmUrl(instance, inputFileName, outFileName); + ModulePathHelper::ParseOhmUrl(instance, inputFileName, outFileName, entryPoint); EXPECT_EQ(entryPoint, res3); EXPECT_EQ(outFileName, outRes1); @@ -483,7 +490,7 @@ HWTEST_F_L0(EcmaModuleTest, ParseOhmUrl) inputFileName = "/data/storage/el1/bundle/entry/ets/mainAbility.abc"; CString outRes2 = "/data/storage/el1/bundle/entry/ets/modules.abc"; CString res4 = "com.bundleName.test/entry/ets/mainAbility"; - entryPoint = PathHelper::ParseOhmUrl(instance, inputFileName, outFileName); + ModulePathHelper::ParseOhmUrl(instance, inputFileName, outFileName, entryPoint); EXPECT_EQ(entryPoint, res4); EXPECT_EQ(outFileName, outRes2); @@ -491,7 +498,7 @@ HWTEST_F_L0(EcmaModuleTest, ParseOhmUrl) outFileName = ""; inputFileName = "/data/storage/el1/bundle/moduleName/ets/mainAbility.abc"; CString res5 = "com.bundleName.test/moduleName/ets/mainAbility"; - entryPoint = PathHelper::ParseOhmUrl(instance, inputFileName, outFileName); + ModulePathHelper::ParseOhmUrl(instance, inputFileName, outFileName, entryPoint); EXPECT_EQ(entryPoint, res5); EXPECT_EQ(outFileName, "/data/storage/el1/bundle/moduleName/ets/modules.abc"); } @@ -539,4 +546,123 @@ HWTEST_F_L0(EcmaModuleTest, CheckNativeModule) EXPECT_EQ(res7.first, false); EXPECT_EQ(res7.second, ModuleTypes::UNKNOWN); } + +HWTEST_F_L0(EcmaModuleTest, ResolveDirPath) +{ + ObjectFactory *objectFactory = thread->GetEcmaVM()->GetFactory(); + + CString inputFileName = "moduleName/ets/pages/index.abc"; + CString resName1 = "moduleName/ets/pages/"; + JSHandle res1 = objectFactory->NewFromUtf8(resName1); + JSHandle outFileName = PathHelper::ResolveDirPath(thread, inputFileName); + EXPECT_EQ(outFileName, res1); + + inputFileName = "moduleName\\ets\\pages\\index.abc"; + CString resName2 = "moduleName\\ets\\pages\\"; + JSHandle res2 = objectFactory->NewFromUtf8(resName2); + outFileName = PathHelper::ResolveDirPath(thread, inputFileName); + EXPECT_EQ(outFileName, res2); + + inputFileName = "cjs"; + CString resName3 = ""; + JSHandle res3 = objectFactory->NewFromUtf8(resName3); + outFileName = PathHelper::ResolveDirPath(thread, inputFileName); + EXPECT_EQ(outFileName, res3); +} + +HWTEST_F_L0(EcmaModuleTest, DeleteNamespace) +{ + CString inputFileName = "moduleName@nameSpace"; + CString res1 = "moduleName"; + PathHelper::DeleteNamespace(inputFileName); + EXPECT_EQ(inputFileName, res1); + + inputFileName = "moduleName"; + CString res2 = "moduleName"; + PathHelper::DeleteNamespace(inputFileName); + EXPECT_EQ(inputFileName, res2); +} + +HWTEST_F_L0(EcmaModuleTest, AdaptOldIsaRecord) +{ + CString inputFileName = "bundleName/moduleName@namespace/moduleName"; + CString res1 = "moduleName"; + PathHelper::AdaptOldIsaRecord(inputFileName); + EXPECT_EQ(inputFileName, res1); +} + +HWTEST_F_L0(EcmaModuleTest, GetStrippedModuleName) +{ + CString inputFileName = "@ohos:hilog"; + CString res1 = "hilog"; + CString outFileName = PathHelper::GetStrippedModuleName(inputFileName); + EXPECT_EQ(outFileName, res1); +} + +HWTEST_F_L0(EcmaModuleTest, GetInternalModulePrefix) +{ + CString inputFileName = "@ohos:hilog"; + CString res1 = "ohos"; + CString outFileName = PathHelper::GetInternalModulePrefix(inputFileName); + EXPECT_EQ(outFileName, res1); +} + +HWTEST_F_L0(EcmaModuleTest, IsNativeModuleRequest) +{ + CString inputFileName = "json5"; + bool res1 = ModulePathHelper::IsNativeModuleRequest(inputFileName); + EXPECT_TRUE(!res1); + + inputFileName = "@ohos:hilog"; + bool res2 = ModulePathHelper::IsNativeModuleRequest(inputFileName); + EXPECT_TRUE(res2); + + inputFileName = "@app:xxxx"; + bool res3 = ModulePathHelper::IsNativeModuleRequest(inputFileName); + EXPECT_TRUE(res3); + + inputFileName = "@native:xxxx"; + bool res4 = ModulePathHelper::IsNativeModuleRequest(inputFileName); + EXPECT_TRUE(res4); +} + +HWTEST_F_L0(EcmaModuleTest, IsImportFile) +{ + CString inputFileName = "./test"; + bool res1 = ModulePathHelper::IsImportFile(inputFileName); + EXPECT_TRUE(res1); + CString outFileName = ModulePathHelper::RemoveSuffix(inputFileName); + EXPECT_EQ(outFileName, inputFileName); + + inputFileName = "test"; + bool res2 = ModulePathHelper::IsImportFile(inputFileName); + EXPECT_TRUE(!res2); + outFileName = ModulePathHelper::RemoveSuffix(inputFileName); + EXPECT_EQ(outFileName, inputFileName); + + CString result = "test"; + inputFileName = "test.js"; + bool res3 = ModulePathHelper::IsImportFile(inputFileName); + EXPECT_TRUE(res3); + outFileName = ModulePathHelper::RemoveSuffix(inputFileName); + EXPECT_EQ(outFileName, result); + + inputFileName = "test.ts"; + bool res4 = ModulePathHelper::IsImportFile(inputFileName); + EXPECT_TRUE(res4); + outFileName = ModulePathHelper::RemoveSuffix(inputFileName); + EXPECT_EQ(outFileName, result); + + inputFileName = "test.ets"; + bool res5 = ModulePathHelper::IsImportFile(inputFileName); + EXPECT_TRUE(res5); + outFileName = ModulePathHelper::RemoveSuffix(inputFileName); + EXPECT_EQ(outFileName, result); + + inputFileName = "test.json"; + bool res6 = ModulePathHelper::IsImportFile(inputFileName); + EXPECT_TRUE(res6); + outFileName = ModulePathHelper::RemoveSuffix(inputFileName); + EXPECT_EQ(outFileName, result); +} } // namespace panda::test diff --git a/ecmascript/napi/dfx_jsnapi.cpp b/ecmascript/napi/dfx_jsnapi.cpp index 77ff91498c7c0f22c8d28ded8c9b49acb925029a..2f9f407537d6cc340d05ce544df612491bb1d4e7 100644 --- a/ecmascript/napi/dfx_jsnapi.cpp +++ b/ecmascript/napi/dfx_jsnapi.cpp @@ -25,6 +25,7 @@ #include "ecmascript/mem/gc_stats.h" #include "ecmascript/napi/include/jsnapi.h" #include "ecmascript/dfx/hprof/file_stream.h" +#include "ecmascript/dfx/vm_thread_control.h" #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER) #include "ecmascript/dfx/cpu_profiler/cpu_profiler.h" @@ -47,16 +48,15 @@ template using JSHandle = ecmascript::JSHandle; using ecmascript::FileStream; using ecmascript::FileDescriptorStream; -using ecmascript::Stream; using ecmascript::CMap; void DFXJSNApi::DumpHeapSnapshot([[maybe_unused]] const EcmaVM *vm, [[maybe_unused]] int dumpFormat, [[maybe_unused]] const std::string &path, [[maybe_unused]] bool isVmMode, - [[maybe_unused]] bool isPrivate) + [[maybe_unused]] bool isPrivate, [[maybe_unused]] bool captureNumericValue) { #if defined(ECMASCRIPT_SUPPORT_SNAPSHOT) FileStream stream(path); - DumpHeapSnapshot(vm, dumpFormat, &stream, nullptr, isVmMode, isPrivate); + DumpHeapSnapshot(vm, dumpFormat, &stream, nullptr, isVmMode, isPrivate, captureNumericValue); #else LOG_ECMA(ERROR) << "Not support arkcompiler heap snapshot"; #endif @@ -64,14 +64,15 @@ void DFXJSNApi::DumpHeapSnapshot([[maybe_unused]] const EcmaVM *vm, [[maybe_unus void DFXJSNApi::DumpHeapSnapshot([[maybe_unused]] const EcmaVM *vm, [[maybe_unused]] int dumpFormat, [[maybe_unused]] Stream *stream, [[maybe_unused]] Progress *progress, - [[maybe_unused]] bool isVmMode, [[maybe_unused]] bool isPrivate) + [[maybe_unused]] bool isVmMode, [[maybe_unused]] bool isPrivate, + [[maybe_unused]] bool captureNumericValue) { #if defined(ECMASCRIPT_SUPPORT_SNAPSHOT) ecmascript::base::BlockHookScope blockScope; ecmascript::HeapProfilerInterface *heapProfile = ecmascript::HeapProfilerInterface::GetInstance( const_cast(vm)); - heapProfile->DumpHeapSnapshot(ecmascript::DumpFormat(dumpFormat), stream, progress, isVmMode, isPrivate); - ecmascript::HeapProfilerInterface::Destroy(const_cast(vm)); + heapProfile->DumpHeapSnapshot(ecmascript::DumpFormat(dumpFormat), stream, progress, + isVmMode, isPrivate, captureNumericValue); #else LOG_ECMA(ERROR) << "Not support arkcompiler heap snapshot"; #endif @@ -80,7 +81,8 @@ void DFXJSNApi::DumpHeapSnapshot([[maybe_unused]] const EcmaVM *vm, [[maybe_unus [[maybe_unused]] static uint8_t killCount = 0; void DFXJSNApi::DumpHeapSnapshot([[maybe_unused]] const EcmaVM *vm, [[maybe_unused]] int dumpFormat, - [[maybe_unused]] bool isVmMode, [[maybe_unused]] bool isPrivate) + [[maybe_unused]] bool isVmMode, [[maybe_unused]] bool isPrivate, + [[maybe_unused]] bool captureNumericValue) { #if defined(ECMASCRIPT_SUPPORT_SNAPSHOT) #if defined(ENABLE_DUMP_IN_FAULTLOG) @@ -113,13 +115,22 @@ void DFXJSNApi::DumpHeapSnapshot([[maybe_unused]] const EcmaVM *vm, [[maybe_unus return; } FileDescriptorStream stream(fd); - DumpHeapSnapshot(vm, dumpFormat, &stream, nullptr, isVmMode, isPrivate); + DumpHeapSnapshot(vm, dumpFormat, &stream, nullptr, isVmMode, isPrivate, captureNumericValue); #endif // ENABLE_DUMP_IN_FAULTLOG #else LOG_ECMA(ERROR) << "Not support arkcompiler heap snapshot"; #endif // ECMASCRIPT_SUPPORT_SNAPSHOT } +void DFXJSNApi::DestroyHeapProfiler([[maybe_unused]] const EcmaVM *vm) +{ +#if defined(ECMASCRIPT_SUPPORT_SNAPSHOT) + ecmascript::HeapProfilerInterface::Destroy(const_cast(vm)); +#else + LOG_ECMA(ERROR) << "Not support arkcompiler heap snapshot"; +#endif +} + bool DFXJSNApi::BuildNativeAndJsStackTrace(const EcmaVM *vm, std::string &stackTraceStr) { LOG_ECMA(INFO) <<"BuildJsStackInfoList start"; @@ -259,6 +270,20 @@ void DFXJSNApi::NotifyMemoryPressure(EcmaVM *vm, bool inHighMemoryPressure) const_cast(vm->GetHeap())->NotifyMemoryPressure(inHighMemoryPressure); } +void DFXJSNApi::NotifyFinishColdStart(EcmaVM *vm, bool isConvinced) +{ + if (isConvinced) { + const_cast(vm->GetHeap())->NotifyFinishColdStart(); + } else { + const_cast(vm->GetHeap())->NotifyFinishColdStartSoon(); + } +} + +void DFXJSNApi::NotifyHighSensitive(EcmaVM *vm, bool isStart) +{ + const_cast(vm->GetHeap())->NotifyHighSensitive(isStart); +} + bool DFXJSNApi::StopCpuProfilerForColdStart([[maybe_unused]] const EcmaVM *vm) { #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER) @@ -332,9 +357,11 @@ bool DFXJSNApi::CpuProfilerSamplingAnyTime([[maybe_unused]] const EcmaVM *vm) } else { LOG_ECMA(INFO) << "Stop CpuProfiler Any Time Worker Thread, killCount = " << killCount; const_cast(vm)->EnumerateWorkerVm([&](const EcmaVM *workerVm) -> void { - if (workerVm->GetJSThread()->GetIsProfiling()) { + auto *thread = workerVm->GetAssociatedJSThread(); + if (thread->GetIsProfiling()) { DFXJSNApi::StopCpuProfilerForFile(workerVm); } + thread->SetNeedProfiling(false); }); } } @@ -351,6 +378,10 @@ void DFXJSNApi::StartCpuProfilerForFile([[maybe_unused]] const EcmaVM *vm, [[maybe_unused]] int interval) { #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER) + if (interval < 0) { + LOG_ECMA(ERROR) << "Sampling interval is illegal"; + return; + } if (vm == nullptr) { return; } @@ -387,6 +418,10 @@ void DFXJSNApi::StopCpuProfilerForFile([[maybe_unused]] const EcmaVM *vm) void DFXJSNApi::StartCpuProfilerForInfo([[maybe_unused]] const EcmaVM *vm, [[maybe_unused]] int interval) { #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER) + if (interval < 0) { + LOG_ECMA(ERROR) << "Sampling interval is illegal"; + return; + } if (vm == nullptr) { return; } @@ -428,6 +463,10 @@ std::unique_ptr DFXJSNApi::StopCpuProfilerForInfo([[maybe_unused]] void DFXJSNApi::SetCpuSamplingInterval([[maybe_unused]] const EcmaVM *vm, [[maybe_unused]] int interval) { #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER) + if (interval < 0) { + LOG_ECMA(ERROR) << "Sampling interval is illegal"; + return; + } LOG_ECMA(INFO) << "SetCpuProfilerSamplingInterval, Sampling interval is: " << interval; if (vm == nullptr) { return; @@ -493,7 +532,7 @@ bool DFXJSNApi::BuildJsStackInfoList(const EcmaVM *hostVm, uint32_t tid, std::ve if (hostVm->GetAssociatedJSThread()->GetThreadId() == tid) { vm = const_cast(hostVm); } else { - vm = hostVm->GetWorkerVm(tid); + vm = const_cast(hostVm)->GetWorkerVm(tid); if (vm == nullptr) { return false; } @@ -559,4 +598,27 @@ bool DFXJSNApi::StartProfiler(EcmaVM *vm, const ProfilerOption &option, int32_t return JSNApi::StartDebugger(vm, debugOption, instanceId, debuggerPostTask); } } + +void DFXJSNApi::ResumeVMById(EcmaVM *hostVm, uint32_t tid) +{ + if (hostVm->GetAssociatedJSThread()->GetThreadId() == tid) { + ResumeVM(hostVm); + } else { + hostVm->ResumeWorkerVm(tid); + } +} + +bool DFXJSNApi::SuspendVMById(EcmaVM *hostVm, uint32_t tid) +{ + bool success = false; + if (hostVm->GetAssociatedJSThread()->GetThreadId() == tid) { + success = SuspendVM(hostVm); + LOG_ECMA(INFO) << "The main thread, SuspendVMById succeeded: " << success; + return success; + } else { + success = hostVm->SuspendWorkerVm(tid); + LOG_ECMA(INFO) << "The worker thread, SuspendVMById succeeded: " << success; + return success; + } +} } // namespace panda diff --git a/ecmascript/napi/include/dfx_jsnapi.h b/ecmascript/napi/include/dfx_jsnapi.h index 73af5b77f618d61455f53b94c6b89eb7a016a681..c2db828c80c180c4521a4bfb15fa3b68de130874 100644 --- a/ecmascript/napi/include/dfx_jsnapi.h +++ b/ecmascript/napi/include/dfx_jsnapi.h @@ -51,10 +51,12 @@ public: // progress pointer is used to report the object number for IDE. // isVmMode means the internal class in vm is visible. isPrivate means the number and string is not visible. static void DumpHeapSnapshot(const EcmaVM *vm, int dumpFormat, const std::string &path, bool isVmMode = true, - bool isPrivate = false); + bool isPrivate = false, bool captureNumericValue = false); static void DumpHeapSnapshot(const EcmaVM *vm, int dumpFormat, Stream *stream, Progress *progress = nullptr, - bool isVmMode = true, bool isPrivate = false); - static void DumpHeapSnapshot(const EcmaVM *vm, int dumpFormat, bool isVmMode = true, bool isPrivate = false); + bool isVmMode = true, bool isPrivate = false, bool captureNumericValue = false); + static void DumpHeapSnapshot(const EcmaVM *vm, int dumpFormat, bool isVmMode = true, bool isPrivate = false, + bool captureNumericValue = false); + static void DestroyHeapProfiler(const EcmaVM *vm); static bool BuildNativeAndJsStackTrace(const EcmaVM *vm, std::string &stackTraceStr); static bool BuildJsStackTrace(const EcmaVM *vm, std::string &stackTraceStr); @@ -73,6 +75,8 @@ public: static void NotifyIdleStatusControl(const EcmaVM *vm, std::function callback); static void NotifyIdleTime(const EcmaVM *vm, int idleMicroSec); static void NotifyMemoryPressure(EcmaVM *vm, bool inHighMemoryPressure); + static void NotifyFinishColdStart(EcmaVM *vm, bool isConvinced); + static void NotifyHighSensitive(EcmaVM *vm, bool isStart); static bool BuildJsStackInfoList(const EcmaVM *hostVm, uint32_t tid, std::vector& jsFrames); // cpuprofiler @@ -103,6 +107,8 @@ public: static bool SuspendVM(const EcmaVM *vm); static bool IsSuspended(const EcmaVM *vm); static bool CheckSafepoint(const EcmaVM *vm); + static void ResumeVMById(EcmaVM *vm, uint32_t tid); + static bool SuspendVMById(EcmaVM *vm, uint32_t tid); }; } #endif \ No newline at end of file diff --git a/ecmascript/napi/include/jsnapi.h b/ecmascript/napi/include/jsnapi.h index 252b683a9aac10996f01420694687df96f89427a..23956147c6dfacbce203e9e387f15108e3311be4 100644 --- a/ecmascript/napi/include/jsnapi.h +++ b/ecmascript/napi/include/jsnapi.h @@ -22,6 +22,7 @@ #include #include #include +#include #include "ecmascript/base/config.h" #include "ecmascript/common.h" @@ -57,6 +58,7 @@ class BufferRef; namespace ecmascript { class EcmaVM; class JSTaggedValue; +class EcmaContext; class JSRuntimeOptions; class JSThread; struct EcmaRuntimeCallInfo; @@ -66,9 +68,11 @@ static constexpr uint32_t DEFAULT_GC_POOL_SIZE = 256_MB; using Deleter = void (*)(void *nativePointer, void *data); using WeakRefClearCallBack = void (*)(void *); using EcmaVM = ecmascript::EcmaVM; +using EcmaContext = ecmascript::EcmaContext; using JSThread = ecmascript::JSThread; using JSTaggedType = uint64_t; using ConcurrentCallback = void (*)(Local result, bool success, void *taskInfo, void *data); +using SourceMapTranslateCallback = std::function; static constexpr size_t DEFAULT_GC_THREAD_NUM = 7; static constexpr size_t DEFAULT_LONG_PAUSE_TIME = 40; @@ -217,6 +221,8 @@ public: return GetAddress() == nullptr; } + void SetWeakCallback(void *ref, WeakRefClearCallBack freeGlobalCallBack, + WeakRefClearCallBack nativeFinalizeCallback); void SetWeak(); void ClearWeak(); @@ -413,6 +419,7 @@ public: bool IsSymbol(); bool IsObject(); bool IsArray(const EcmaVM *vm); + bool IsJSArray(const EcmaVM *vm); bool IsConstructor(); bool IsFunction(); bool IsProxy(); @@ -475,6 +482,22 @@ public: Local Typeof(const EcmaVM *vm); bool InstanceOf(const EcmaVM *vm, Local value); + bool IsArrayList(); + bool IsDeque(); + bool IsHashMap(); + bool IsHashSet(); + bool IsLightWeightMap(); + bool IsLightWeightSet(); + bool IsLinkedList(); + bool IsLinkedListIterator(); + bool IsList(); + bool IsPlainArray(); + bool IsQueue(); + bool IsStack(); + bool IsTreeMap(); + bool IsTreeSet(); + bool IsVector(); + private: JSTaggedType value_; friend JSNApi; @@ -533,7 +556,7 @@ public: static Local NewFromUtf8(const EcmaVM *vm, const char *utf8, int length = -1); static Local NewFromUtf16(const EcmaVM *vm, const char16_t *utf16, int length = -1); std::string ToString(); - int32_t Length(); + uint32_t Length(); int32_t Utf8Length(const EcmaVM *vm); int WriteUtf8(char *buffer, int length, bool isWriteBuffer = false); int WriteUtf16(char16_t *buffer, int length); @@ -725,7 +748,7 @@ public: void *data, bool callNapi = false, size_t nativeBindingsize = 0); Local Call(const EcmaVM *vm, Local thisObj, const Local argv[], - int32_t length, bool isNapi = false); + int32_t length); Local Constructor(const EcmaVM *vm, const Local argv[], int32_t length); Local GetFunctionPrototype(const EcmaVM *vm); @@ -741,7 +764,7 @@ public: class PUBLIC_API ArrayRef : public ObjectRef { public: static Local New(const EcmaVM *vm, uint32_t length = 0); - int32_t Length(const EcmaVM *vm); + uint32_t Length(const EcmaVM *vm); static bool SetValueAt(const EcmaVM *vm, Local obj, uint32_t index, Local value); static Local GetValueAt(const EcmaVM *vm, Local obj, uint32_t index); }; @@ -884,6 +907,13 @@ public: double GetTime(); }; +class PUBLIC_API ProxyRef : public ObjectRef { +public: + Local GetHandler(const EcmaVM *vm); + Local GetTarget(const EcmaVM *vm); + bool IsRevoked(); +}; + class PUBLIC_API MapRef : public ObjectRef { public: int32_t GetSize(); @@ -895,6 +925,14 @@ public: void Set(const EcmaVM *vm, Local key, Local value); }; +class PUBLIC_API WeakMapRef : public ObjectRef { +public: + int32_t GetSize(); + int32_t GetTotalElements(); + Local GetKey(const EcmaVM *vm, int entry); + Local GetValue(const EcmaVM *vm, int entry); +}; + class PUBLIC_API SetRef : public ObjectRef { public: int32_t GetSize(); @@ -902,6 +940,13 @@ public: Local GetValue(const EcmaVM *vm, int entry); }; +class PUBLIC_API WeakSetRef : public ObjectRef { +public: + int32_t GetSize(); + int32_t GetTotalElements(); + Local GetValue(const EcmaVM *vm, int entry); +}; + class PUBLIC_API MapIteratorRef : public ObjectRef { public: int32_t GetIndex(); @@ -1226,6 +1271,36 @@ private: void* data_ {nullptr}; }; +/** + * An external exception handler. + */ +class PUBLIC_API TryCatch { +public: + explicit TryCatch(const EcmaVM *ecmaVm) : ecmaVm_(ecmaVm) {}; + + /** + * Consumes the exception by default if not rethrow explicitly. + */ + ~TryCatch(); + + bool HasCaught() const; + void Rethrow(); + Local GetAndClearException(); + + NO_COPY_SEMANTIC(TryCatch); + NO_MOVE_SEMANTIC(TryCatch); + +private: + // Disable dynamic allocation + void* operator new(size_t size) = delete; + void operator delete(void*, size_t) = delete; + void* operator new[](size_t size) = delete; + void operator delete[](void*, size_t) = delete; + + const EcmaVM *ecmaVm_ {nullptr}; + bool rethrow_ {false}; +}; + class PUBLIC_API JSNApi { public: struct DebugOption { @@ -1255,10 +1330,15 @@ public: // aot load static void LoadAotFile(EcmaVM *vm, const std::string &moduleName); + // context + static EcmaContext *CreateJSContext(EcmaVM *vm); + static void SwitchCurrentContext(EcmaVM *vm, EcmaContext *context); + static void DestroyJSContext(EcmaVM *vm, EcmaContext *context); - // JS code + // context execute static bool ExecuteInContext(EcmaVM *vm, const std::string &fileName, const std::string &entry, bool needUpdate = false); + // JS code static bool Execute(EcmaVM *vm, const std::string &fileName, const std::string &entry, bool needUpdate = false); static bool Execute(EcmaVM *vm, const uint8_t *data, int32_t size, const std::string &entry, const std::string &filename = "", bool needUpdate = false); @@ -1269,6 +1349,9 @@ public: static Local GetExportObject(EcmaVM *vm, const std::string &file, const std::string &key); static Local GetExportObjectFromBuffer(EcmaVM *vm, const std::string &file, const std::string &key); + // secure memory check + static bool CheckSecureMem(uintptr_t mem); + /* * Execute panda file from secure mem. secure memory lifecycle managed externally. * The data parameter needs to be created externally by an external caller and managed externally @@ -1300,6 +1383,7 @@ public: static Local GetAndClearUncaughtException(const EcmaVM *vm); static Local GetUncaughtException(const EcmaVM *vm); static bool HasPendingException(const EcmaVM *vm); + static bool HasPendingJob(const EcmaVM *vm); static void EnableUserUncaughtErrorHandler(EcmaVM *vm); static bool StartDebugger(EcmaVM *vm, const DebugOption &option, int32_t instanceId = 0, const DebuggerPostTask &debuggerPostTask = {}); @@ -1311,8 +1395,11 @@ public: static Local DeserializeValue(const EcmaVM *vm, void *recoder, void *hint); static void DeleteSerializationData(void *data); static void SetHostPromiseRejectionTracker(EcmaVM *vm, void *cb, void* data); - static void SetHostResolveBufferTracker(EcmaVM *vm, std::function(std::string dirPath)> cb); + static void SetHostResolveBufferTracker(EcmaVM *vm, + std::function cb); + static void SetUnloadNativeModuleCallback(EcmaVM *vm, const std::function &cb); static void SetNativePtrGetter(EcmaVM *vm, void* cb); + static void SetSourceMapTranslateCallback(EcmaVM *vm, SourceMapTranslateCallback cb); static void SetHostEnqueueJob(const EcmaVM* vm, Local cb); static void InitializeIcuData(const ecmascript::JSRuntimeOptions &options); static void InitializeMemMapAllocator(); @@ -1341,6 +1428,7 @@ public: static bool IsBundle(EcmaVM *vm); static void SetBundle(EcmaVM *vm, bool value); static void SetAssetPath(EcmaVM *vm, const std::string &assetPath); + static void SetMockModuleList(EcmaVM *vm, const std::map &list); static void SetLoop(EcmaVM *vm, void *loop); static std::string GetAssetPath(EcmaVM *vm); @@ -1353,6 +1441,11 @@ public: static std::string GetModuleName(EcmaVM *vm); static void AllowCrossThreadExecution(EcmaVM *vm); static void SynchronizVMInfo(EcmaVM *vm, const EcmaVM *hostVM); + static bool IsProfiling(EcmaVM *vm); + static void SetProfilerState(const EcmaVM *vm, bool value); + static void SetRequestAotCallback(EcmaVM *vm, const std::function &cb); private: static int vmCount_; @@ -1447,6 +1540,15 @@ private: friend class FunctionRef; }; +class PUBLIC_API FunctionCallScope { +public: + FunctionCallScope(EcmaVM *vm); + ~FunctionCallScope(); + +private: + EcmaVM *vm_; +}; + template template Global::Global(const EcmaVM *vm, const Local ¤t) : vm_(vm) @@ -1524,6 +1626,13 @@ inline void CopyableGlobal::Free() } } +template +void CopyableGlobal::SetWeakCallback(void *ref, WeakRefClearCallBack freeGlobalCallBack, + WeakRefClearCallBack nativeFinalizeCallback) +{ + address_ = JSNApi::SetWeakCallback(vm_, address_, ref, freeGlobalCallBack, nativeFinalizeCallback); +} + template void CopyableGlobal::SetWeak() { diff --git a/ecmascript/napi/jsnapi.cpp b/ecmascript/napi/jsnapi.cpp index 16af70b936064536d528ea1ddd69aa9c0cb99a8f..e3f9b393ddc1153a6ea448e7914ef9c74f6b314e 100644 --- a/ecmascript/napi/jsnapi.cpp +++ b/ecmascript/napi/jsnapi.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * Copyright (c) 2021-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 @@ -75,12 +75,14 @@ #include "ecmascript/mem/region.h" #include "ecmascript/module/js_module_manager.h" #include "ecmascript/module/js_module_source_text.h" +#include "ecmascript/module/module_path_helper.h" #include "ecmascript/object_factory.h" #include "ecmascript/patch/quick_fix_manager.h" #include "ecmascript/pgo_profiler/pgo_profiler_manager.h" #include "ecmascript/platform/file.h" #include "ecmascript/regexp/regexp_parser.h" #include "ecmascript/tagged_array.h" +#include "ecmascript/js_weak_container.h" #ifdef ARK_SUPPORT_INTL #include "ecmascript/js_bigint.h" #include "ecmascript/js_collator.h" @@ -90,7 +92,7 @@ #include "ohos/init_data.h" -#include "libpandabase/os/mutex.h" +#include "ecmascript/platform/mutex.h" #if defined(ECMASCRIPT_SUPPORT_DEBUGGER) && defined(PANDA_TARGET_IOS) namespace OHOS::ArkCompiler::Toolchain { @@ -148,7 +150,8 @@ using ecmascript::TaggedArray; using ecmascript::JSTypedArray; using ecmascript::base::BuiltinsBase; using ecmascript::builtins::BuiltinsObject; -using ecmascript::base::JsonParser; +using ecmascript::base::Utf8JsonParser; +using ecmascript::base::Utf16JsonParser; using ecmascript::base::JsonStringifier; using ecmascript::base::StringHelper; using ecmascript::base::TypedArrayHelper; @@ -157,6 +160,8 @@ using ecmascript::job::QueueType; using ecmascript::JSRuntimeOptions; using ecmascript::BigInt; using ecmascript::MemMapAllocator; +using ecmascript::Mutex; +using ecmascript::LockHolder; using ecmascript::JSMapIterator; using ecmascript::JSSetIterator; using ecmascript::IterationKind; @@ -165,6 +170,7 @@ using ecmascript::JSIterator; using ecmascript::JSGeneratorFunction; using ecmascript::JSGeneratorObject; using ecmascript::GeneratorContext; +using ecmascript::JSProxy; #ifdef ARK_SUPPORT_INTL using ecmascript::JSCollator; using ecmascript::JSDateTimeFormat; @@ -176,6 +182,8 @@ using ecmascript::PatchErrorCode; using ecmascript::base::NumberHelper; using ecmascript::Log; using ecmascript::EcmaContext; +using ecmascript::JSWeakMap; +using ecmascript::JSWeakSet; template using JSHandle = ecmascript::JSHandle; @@ -183,18 +191,20 @@ template using JSMutableHandle = ecmascript::JSMutableHandle; using PathHelper = ecmascript::base::PathHelper; +using ModulePathHelper = ecmascript::ModulePathHelper; + namespace { // NOLINTNEXTLINE(fuchsia-statically-constructed-objects) constexpr std::string_view ENTRY_POINTER = "_GLOBAL::func_main_0"; } int JSNApi::vmCount_ = 0; bool JSNApi::initialize_ = false; -static os::memory::Mutex *mutex = new panda::os::memory::Mutex(); +static Mutex *mutex = new panda::Mutex(); #define XPM_PROC_PREFIX "/proc/" #define XPM_PROC_SUFFIX "/xpm_region" #define XPM_PROC_LENGTH 50 -static bool CheckSecureMem(uintptr_t mem) +bool JSNApi::CheckSecureMem(uintptr_t mem) { static bool hasOpen = false; static uintptr_t secureMemStart = 0; @@ -303,10 +313,28 @@ EcmaVM *JSNApi::CreateJSVM(const RuntimeOption &option) return CreateEcmaVM(runtimeOptions); } +EcmaContext *JSNApi::CreateJSContext(EcmaVM *vm) +{ + JSThread *thread = vm->GetJSThread(); + return EcmaContext::CreateAndInitialize(thread); +} + +void JSNApi::SwitchCurrentContext(EcmaVM *vm, EcmaContext *context) +{ + JSThread *thread = vm->GetJSThread(); + thread->SwitchCurrentContext(context); +} + +void JSNApi::DestroyJSContext(EcmaVM *vm, EcmaContext *context) +{ + JSThread *thread = vm->GetJSThread(); + EcmaContext::CheckAndDestroy(thread, context); +} + EcmaVM *JSNApi::CreateEcmaVM(const JSRuntimeOptions &options) { { - os::memory::LockHolder lock(*mutex); + LockHolder lock(*mutex); vmCount_++; if (!initialize_) { ecmascript::Log::Initialize(options); @@ -325,7 +353,7 @@ EcmaVM *JSNApi::CreateEcmaVM(const JSRuntimeOptions &options) void JSNApi::DestroyJSVM(EcmaVM *ecmaVm) { - os::memory::LockHolder lock(*mutex); + LockHolder lock(*mutex); if (!initialize_) { return; } @@ -347,7 +375,7 @@ void JSNApi::TriggerGC(const EcmaVM *vm, TRIGGER_GC_TYPE gcType) CHECK_HAS_PENDING_EXCEPTION_WITHOUT_RETURN(vm); switch (gcType) { case TRIGGER_GC_TYPE::SEMI_GC: - vm->CollectGarbage(ecmascript::TriggerGCType::YOUNG_GC, ecmascript::GCReason::EXTERNAL_TRIGGER); + vm->CollectGarbage(vm->GetHeap()->SelectGCType(), ecmascript::GCReason::EXTERNAL_TRIGGER); break; case TRIGGER_GC_TYPE::OLD_GC: vm->CollectGarbage(ecmascript::TriggerGCType::OLD_GC, ecmascript::GCReason::EXTERNAL_TRIGGER); @@ -494,7 +522,6 @@ bool JSNApi::IsMixedDebugEnabled([[maybe_unused]] const EcmaVM *vm) CHECK_HAS_PENDING_EXCEPTION(vm, false); return vm->GetJsDebuggerManager()->IsMixedDebugEnabled(); #else - LOG_ECMA(ERROR) << "Not support arkcompiler debugger"; return false; #endif } @@ -640,14 +667,15 @@ void JSNApi::PostFork(EcmaVM *vm, const RuntimeOption &option) void JSNApi::addWorker(EcmaVM *hostVm, EcmaVM *workerVm) { if (hostVm != nullptr && workerVm != nullptr) { - hostVm->WorkersetInfo(hostVm, workerVm); + hostVm->WorkersetInfo(workerVm); + workerVm->SetBundleName(hostVm->GetBundleName()); } } bool JSNApi::DeleteWorker(EcmaVM *hostVm, EcmaVM *workerVm) { if (hostVm != nullptr && workerVm != nullptr) { - return hostVm->DeleteWorker(hostVm, workerVm); + return hostVm->DeleteWorker(workerVm); } return false; } @@ -667,6 +695,11 @@ bool JSNApi::HasPendingException(const EcmaVM *vm) return vm->GetJSThread()->HasPendingException(); } +bool JSNApi::HasPendingJob(const EcmaVM *vm) +{ + return vm->GetJSThread()->GetCurrentEcmaContext()->HasPendingJob(); +} + void JSNApi::EnableUserUncaughtErrorHandler(EcmaVM *vm) { return vm->GetJSThread()->GetCurrentEcmaContext()->EnableUserUncaughtErrorHandler(); @@ -813,11 +846,24 @@ void JSNApi::SetHostPromiseRejectionTracker(EcmaVM *vm, void *cb, void* data) vm->GetJSThread()->GetCurrentEcmaContext()->SetData(data); } -void JSNApi::SetHostResolveBufferTracker(EcmaVM *vm, std::function(std::string dirPath)> cb) +void JSNApi::SetHostResolveBufferTracker(EcmaVM *vm, + std::function cb) { vm->SetResolveBufferCallback(cb); } +void JSNApi::SetRequestAotCallback(EcmaVM *vm, const std::function &cb) +{ + vm->SetRequestAotCallback(cb); +} + +void JSNApi::SetUnloadNativeModuleCallback(EcmaVM *vm, const std::function &cb) +{ + vm->SetUnloadNativeModuleCallback(cb); +} + void JSNApi::SetNativePtrGetter(EcmaVM *vm, void* cb) { vm->SetNativePtrGetter(reinterpret_cast(cb)); @@ -874,7 +920,7 @@ Local JSNApi::GetExportObject(EcmaVM *vm, const std::string &file, co JSThread *thread = vm->GetJSThread(); ecmascript::CString name = vm->GetAssetPath(); if (!vm->IsBundlePack()) { - entry = PathHelper::ParseOhmUrl(vm, entry, name); + ModulePathHelper::ParseOhmUrl(vm, entry, name, entry); std::shared_ptr jsPandaFile = JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, name, entry.c_str(), false); if (jsPandaFile == nullptr) { @@ -882,7 +928,7 @@ Local JSNApi::GetExportObject(EcmaVM *vm, const std::string &file, co return JSNApiHelper::ToLocal(exportObj); } if (!jsPandaFile->IsRecordWithBundleName()) { - PathHelper::CroppingRecord(entry); + PathHelper::AdaptOldIsaRecord(entry); } } ecmascript::ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager(); @@ -951,13 +997,13 @@ void JSNApi::DestroyMemMapAllocator() void JSNApi::InitializePGOProfiler(const ecmascript::JSRuntimeOptions &options) { - ecmascript::PGOProfilerManager::GetInstance()->Initialize( + ecmascript::pgo::PGOProfilerManager::GetInstance()->Initialize( options.GetPGOProfilerPath(), options.GetPGOHotnessThreshold()); } void JSNApi::DestroyPGOProfiler() { - ecmascript::PGOProfilerManager::GetInstance()->Destroy(); + ecmascript::pgo::PGOProfilerManager::GetInstance()->Destroy(); } void JSNApi::DestroyAnDataManager() @@ -965,6 +1011,20 @@ void JSNApi::DestroyAnDataManager() ecmascript::AnFileDataManager::GetInstance()->SafeDestroyAllData(); } +FunctionCallScope::FunctionCallScope(EcmaVM *vm) : vm_(vm) +{ + vm_->IncreaseCallDepth(); +} + +FunctionCallScope::~FunctionCallScope() +{ + vm_->DecreaseCallDepth(); + if (vm_->IsTopLevelCallDepth()) { + JSThread *thread = vm_->GetJSThread(); + thread->GetCurrentEcmaContext()->ExecutePromisePendingJob(); + } +} + // ----------------------------------- HandleScope ------------------------------------- LocalScope::LocalScope(const EcmaVM *vm) : thread_(vm->GetJSThread()) { @@ -1197,7 +1257,7 @@ std::string StringRef::ToString() return EcmaStringAccessor(JSNApiHelper::ToJSTaggedValue(this)).ToStdString(); } -int32_t StringRef::Length() +uint32_t StringRef::Length() { return EcmaStringAccessor(JSNApiHelper::ToJSTaggedValue(this)).GetLength(); } @@ -1671,10 +1731,11 @@ Local FunctionRef::NewClassFunction(EcmaVM *vm, FunctionCallback na Local FunctionRef::Call(const EcmaVM *vm, Local thisObj, const Local argv[], // NOLINTNEXTLINE(modernize-avoid-c-arrays) - int32_t length, bool isNapi) + int32_t length) { CHECK_HAS_PENDING_EXCEPTION_RETURN_UNDEFINED(vm); EscapeLocalScope scope(vm); + FunctionCallScope callScope(EcmaVM::ConstCast(vm)); JSThread *thread = vm->GetJSThread(); if (!IsFunction()) { return JSValueRef::Undefined(vm); @@ -1695,10 +1756,6 @@ Local FunctionRef::Call(const EcmaVM *vm, Local thisObj, RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm)); JSHandle resultValue(thread, result); - if (!isNapi) { - vm->GetJSThread()->GetCurrentEcmaContext()->ExecutePromisePendingJob(); - } - RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm)); vm->GetHeap()->ClearKeptObjects(); return scope.Escape(JSNApiHelper::ToLocal(resultValue)); @@ -1709,6 +1766,7 @@ Local FunctionRef::Constructor(const EcmaVM *vm, int32_t length) { CHECK_HAS_PENDING_EXCEPTION_RETURN_UNDEFINED(vm); + FunctionCallScope callScope(EcmaVM::ConstCast(vm)); JSThread *thread = vm->GetJSThread(); if (!IsFunction()) { return JSValueRef::Undefined(vm); @@ -1793,8 +1851,7 @@ Local FunctionRef::GetSourceCode(const EcmaVM *vm, int lineNumber) DebugInfoExtractor *debugExtractor = JSPandaFileManager::GetInstance()->GetJSPtExtractor(jsPandaFile); ecmascript::CString entry = JSPandaFile::ENTRY_FUNCTION_NAME; if (!jsPandaFile->IsBundlePack()) { - JSFunction *function = JSFunction::Cast(func.GetTaggedValue().GetTaggedObject()); - JSTaggedValue recordName = function->GetRecordName(); + JSTaggedValue recordName = method->GetRecordName(); ASSERT(!recordName.IsHole()); entry = ConvertToString(recordName); } @@ -1839,7 +1896,7 @@ Local ArrayRef::New(const EcmaVM *vm, uint32_t length) return JSNApiHelper::ToLocal(array); } -int32_t ArrayRef::Length([[maybe_unused]] const EcmaVM *vm) +uint32_t ArrayRef::Length([[maybe_unused]] const EcmaVM *vm) { CHECK_HAS_PENDING_EXCEPTION(vm, 0); return JSArray::Cast(JSNApiHelper::ToJSTaggedValue(this).GetTaggedObject())->GetArrayLength(); @@ -1862,6 +1919,7 @@ bool ArrayRef::SetValueAt(const EcmaVM *vm, Local obj, uint32_t inde JSHandle valueHandle = JSNApiHelper::ToJSHandle(value); return JSArray::FastSetPropertyByValue(thread, objectHandle, index, valueHandle); } + // ---------------------------------- Promise -------------------------------------- Local PromiseCapabilityRef::New(const EcmaVM *vm) { @@ -2252,7 +2310,7 @@ Local TypedArrayRef::GetArrayBuffer(const EcmaVM *vm) JSHandle func = env->Get##Type##Function(); \ JSHandle arrayBuffer(JSNApiHelper::ToJSHandle(buffer)); \ JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); \ - const int32_t argsLength = 3; \ + const uint32_t argsLength = 3; \ EcmaRuntimeCallInfo *info = \ ecmascript::EcmaInterpreter::NewRuntimeCallInfo(thread, func, undefined, func, argsLength); \ RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm)); \ @@ -2298,11 +2356,11 @@ Local JSON::Parse(const EcmaVM *vm, Local string) auto ecmaStr = EcmaString::Cast(JSNApiHelper::ToJSTaggedValue(*string).GetTaggedObject()); JSHandle result; if (EcmaStringAccessor(ecmaStr).IsUtf8()) { - JsonParser parser(thread); - result = parser.ParseUtf8(EcmaString::Cast(JSNApiHelper::ToJSTaggedValue(*string).GetTaggedObject())); + Utf8JsonParser parser(thread); + result = parser.Parse(EcmaString::Cast(JSNApiHelper::ToJSTaggedValue(*string).GetTaggedObject())); } else { - JsonParser parser(thread); - result = parser.ParseUtf16(EcmaString::Cast(JSNApiHelper::ToJSTaggedValue(*string).GetTaggedObject())); + Utf16JsonParser parser(thread); + result = parser.Parse(EcmaString::Cast(JSNApiHelper::ToJSTaggedValue(*string).GetTaggedObject())); } RETURN_VALUE_IF_ABRUPT(thread, JSValueRef::Undefined(vm)); return JSNApiHelper::ToLocal(result); @@ -2467,6 +2525,26 @@ double DateRef::GetTime() return date->GetTime().GetDouble(); } +Local ProxyRef::GetHandler(const EcmaVM *vm) +{ + JSHandle jsProxy(JSNApiHelper::ToJSHandle(this)); + JSThread *thread = vm->GetJSThread(); + return JSNApiHelper::ToLocal(JSHandle(thread, jsProxy->GetHandler())); +} + +Local ProxyRef::GetTarget(const EcmaVM *vm) +{ + JSHandle jsProxy(JSNApiHelper::ToJSHandle(this)); + JSThread *thread = vm->GetJSThread(); + return JSNApiHelper::ToLocal(JSHandle(thread, jsProxy->GetTarget())); +} + +bool ProxyRef::IsRevoked() +{ + JSHandle jsProxy(JSNApiHelper::ToJSHandle(this)); + return jsProxy->GetIsRevoked(); +} + Local MapRef::Get(const EcmaVM *vm, Local key) { CHECK_HAS_PENDING_EXCEPTION_RETURN_UNDEFINED(vm); @@ -2510,7 +2588,8 @@ int32_t MapRef::GetTotalElements() { JSHandle map(JSNApiHelper::ToJSHandle(this)); LOG_IF_SPECIAL(map, FATAL); - return map->GetSize() + LinkedHashMap::Cast(map->GetLinkedMap().GetTaggedObject())->NumberOfDeletedElements(); + return static_cast((map->GetSize())) + + LinkedHashMap::Cast(map->GetLinkedMap().GetTaggedObject())->NumberOfDeletedElements(); } Local MapRef::GetKey(const EcmaVM *vm, int entry) @@ -2531,6 +2610,40 @@ Local MapRef::GetValue(const EcmaVM *vm, int entry) return JSNApiHelper::ToLocal(JSHandle(thread, map->GetValue(entry))); } +int32_t WeakMapRef::GetSize() +{ + JSHandle weakMap(JSNApiHelper::ToJSHandle(this)); + LOG_IF_SPECIAL(weakMap, FATAL); + return weakMap->GetSize(); +} + +int32_t WeakMapRef::GetTotalElements() +{ + JSHandle weakMap(JSNApiHelper::ToJSHandle(this)); + LOG_IF_SPECIAL(weakMap, FATAL); + return weakMap->GetSize() + + LinkedHashMap::Cast(weakMap->GetLinkedMap().GetTaggedObject())->NumberOfDeletedElements(); +} + +Local WeakMapRef::GetKey(const EcmaVM *vm, int entry) +{ + CHECK_HAS_PENDING_EXCEPTION_RETURN_UNDEFINED(vm); + JSHandle weakMap(JSNApiHelper::ToJSHandle(this)); + LOG_IF_SPECIAL(weakMap, FATAL); + JSTaggedValue key = weakMap->GetKey(entry); + JSThread *thread = vm->GetJSThread(); + return JSNApiHelper::ToLocal(JSHandle(thread, key.GetWeakRawValue())); +} + +Local WeakMapRef::GetValue(const EcmaVM *vm, int entry) +{ + CHECK_HAS_PENDING_EXCEPTION_RETURN_UNDEFINED(vm); + JSHandle weakMap(JSNApiHelper::ToJSHandle(this)); + LOG_IF_SPECIAL(weakMap, FATAL); + JSThread *thread = vm->GetJSThread(); + return JSNApiHelper::ToLocal(JSHandle(thread, weakMap->GetValue(entry))); +} + int32_t SetRef::GetSize() { JSHandle set(JSNApiHelper::ToJSHandle(this)); @@ -2542,7 +2655,8 @@ int32_t SetRef::GetTotalElements() { JSHandle set(JSNApiHelper::ToJSHandle(this)); LOG_IF_SPECIAL(set, FATAL); - return set->GetSize() + LinkedHashSet::Cast(set->GetLinkedSet().GetTaggedObject())->NumberOfDeletedElements(); + return static_cast(set->GetSize()) + + LinkedHashSet::Cast(set->GetLinkedSet().GetTaggedObject())->NumberOfDeletedElements(); } Local SetRef::GetValue(const EcmaVM *vm, int entry) @@ -2554,6 +2668,31 @@ Local SetRef::GetValue(const EcmaVM *vm, int entry) return JSNApiHelper::ToLocal(JSHandle(thread, set->GetValue(entry))); } +int32_t WeakSetRef::GetSize() +{ + JSHandle weakSet(JSNApiHelper::ToJSHandle(this)); + LOG_IF_SPECIAL(weakSet, FATAL); + return weakSet->GetSize(); +} + +int32_t WeakSetRef::GetTotalElements() +{ + JSHandle weakSet(JSNApiHelper::ToJSHandle(this)); + LOG_IF_SPECIAL(weakSet, FATAL); + return weakSet->GetSize() + + LinkedHashSet::Cast(weakSet->GetLinkedSet().GetTaggedObject())->NumberOfDeletedElements(); +} + +Local WeakSetRef::GetValue(const EcmaVM *vm, int entry) +{ + CHECK_HAS_PENDING_EXCEPTION_RETURN_UNDEFINED(vm); + JSHandle weakSet(JSNApiHelper::ToJSHandle(this)); + LOG_IF_SPECIAL(weakSet, FATAL); + JSTaggedValue value = weakSet->GetValue(entry); + JSThread *thread = vm->GetJSThread(); + return JSNApiHelper::ToLocal(JSHandle(thread, value.GetWeakRawValue())); +} + int32_t MapIteratorRef::GetIndex() { JSHandle jsMapIter(JSNApiHelper::ToJSHandle(this)); @@ -2652,7 +2791,6 @@ Local GeneratorObjectRef::GetGeneratorReceiver(const EcmaVM *vm) return JSNApiHelper::ToLocal(JSHandle(thread, jsTagValue)); } - Local CollatorRef::GetCompareFunction(const EcmaVM *vm) { CHECK_HAS_PENDING_EXCEPTION_RETURN_UNDEFINED(vm); @@ -2719,14 +2857,15 @@ JSTaggedValue Callback::RegisterCallback(ecmascript::EcmaRuntimeCallInfo *ecmaRu JsiRuntimeCallInfo jsiRuntimeCallInfo(ecmaRuntimeCallInfo, extraInfo->GetData()); #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER) + bool getStackBeforeCallNapiSuccess = false; if (thread->GetIsProfiling() && function->IsCallNapi()) { - thread->GetEcmaVM()->GetProfiler()->GetStackCallNapi(thread, true); + getStackBeforeCallNapiSuccess = thread->GetEcmaVM()->GetProfiler()->GetStackBeforeCallNapi(thread); } #endif Local result = nativeFunc(&jsiRuntimeCallInfo); #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER) - if (thread->GetIsProfiling() && function->IsCallNapi()) { - thread->GetEcmaVM()->GetProfiler()->GetStackCallNapi(thread, false); + if (thread->GetIsProfiling() && function->IsCallNapi() && getStackBeforeCallNapiSuccess) { + thread->GetEcmaVM()->GetProfiler()->GetStackAfterCallNapi(thread); } #endif return JSNApiHelper::ToJSHandle(result).GetTaggedValue(); @@ -2966,6 +3105,12 @@ bool JSValueRef::IsArray(const EcmaVM *vm) return JSNApiHelper::ToJSTaggedValue(this).IsArray(thread); } +bool JSValueRef::IsJSArray(const EcmaVM *vm) +{ + CHECK_HAS_PENDING_EXCEPTION(vm, false); + return JSNApiHelper::ToJSTaggedValue(this).IsJSArray(); +} + bool JSValueRef::IsConstructor() { JSTaggedValue value = JSNApiHelper::ToJSTaggedValue(this); @@ -3270,6 +3415,81 @@ bool JSValueRef::IsAsyncGeneratorFunction() return obj->IsAsyncGeneratorFunction(); } +bool JSValueRef::IsArrayList() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSAPIArrayList(); +} + +bool JSValueRef::IsDeque() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSAPIDeque(); +} + +bool JSValueRef::IsHashMap() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSAPIHashMap(); +} + +bool JSValueRef::IsHashSet() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSAPIHashSet(); +} + +bool JSValueRef::IsLightWeightMap() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSAPILightWeightMap(); +} + +bool JSValueRef::IsLightWeightSet() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSAPILightWeightSet(); +} + +bool JSValueRef::IsLinkedList() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSAPILinkedList(); +} + +bool JSValueRef::IsLinkedListIterator() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSAPILinkedListIterator(); +} + +bool JSValueRef::IsList() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSAPIList(); +} + +bool JSValueRef::IsPlainArray() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSAPIPlainArray(); +} + +bool JSValueRef::IsQueue() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSAPIQueue(); +} + +bool JSValueRef::IsStack() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSAPIStack(); +} + +bool JSValueRef::IsTreeMap() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSAPITreeMap(); +} + +bool JSValueRef::IsTreeSet() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSAPITreeSet(); +} + +bool JSValueRef::IsVector() +{ + return JSNApiHelper::ToJSTaggedValue(this).IsJSAPIVector(); +} + // ------------------------------------ JsiRuntimeCallInfo ----------------------------------------------- JsiRuntimeCallInfo::JsiRuntimeCallInfo(ecmascript::EcmaRuntimeCallInfo* ecmaInfo, void* data) : thread_(ecmaInfo->GetThread()), numArgs_(ecmaInfo->GetArgsNumber()) @@ -3365,6 +3585,11 @@ std::string JSNApi::GetAssetPath(EcmaVM *vm) return vm->GetAssetPath().c_str(); } +void JSNApi::SetMockModuleList(EcmaVM *vm, const std::map &list) +{ + vm->SetMockModuleList(list); +} + bool JSNApi::InitForConcurrentThread(EcmaVM *vm, ConcurrentCallback cb, void *data) { vm->SetConcurrentCallback(cb, data); @@ -3391,14 +3616,20 @@ bool JSNApi::InitForConcurrentFunction(EcmaVM *vm, Local function, v return false; } ecmascript::CString moduleName = jsPandaFile->GetJSPandaFileDesc(); - ecmascript::CString recordName = method->GetRecordName(); + ecmascript::CString recordName = method->GetRecordNameStr(); // for debugger, to notify the script loaded and parsed which the concurrent function is in auto *notificationMgr = vm->GetJsDebuggerManager()->GetNotificationManager(); notificationMgr->LoadModuleEvent(moduleName, recordName); // check ESM or CJS - if (!jsPandaFile->IsModule(thread, recordName)) { + ecmascript::JSRecordInfo recordInfo; + bool hasRecord = jsPandaFile->CheckAndGetRecordInfo(recordName, recordInfo); + if (!hasRecord) { + LOG_ECMA(ERROR) << "cannot find record '" << recordName << "', please check the request path."; + return false; + } + if (!jsPandaFile->IsModule(recordInfo)) { LOG_ECMA(DEBUG) << "Current function is not from ES Module's file."; return true; } @@ -3411,15 +3642,12 @@ bool JSNApi::InitForConcurrentFunction(EcmaVM *vm, Local function, v } else { LOG_ECMA(DEBUG) << "CompileMode is esmodule"; moduleRecord = moduleManager->HostResolveImportedModuleWithMerge(moduleName, recordName); - if (ecmascript::AnFileDataManager::GetInstance()->IsEnable()) { - thread->GetCurrentEcmaContext()->GetAOTFileManager()->LoadAiFile(jsPandaFile); - } } ecmascript::SourceTextModule::Instantiate(thread, moduleRecord); JSHandle module = JSHandle::Cast(moduleRecord); module->SetStatus(ecmascript::ModuleStatus::INSTANTIATED); ecmascript::SourceTextModule::EvaluateForConcurrent(thread, module); - transFunc->SetModule(thread, module); + method->SetModule(thread, module); return true; } @@ -3427,16 +3655,19 @@ void* JSNApi::GetCurrentTaskInfo(const EcmaVM *vm) { CHECK_HAS_PENDING_EXCEPTION(vm, nullptr); auto thread = vm->GetJSThread(); - ecmascript::FrameHandler frameHandler(thread); - for (; frameHandler.HasFrame(); frameHandler.PrevJSFrame()) { - if (!frameHandler.IsJSFrame()) { + JSTaggedType *current = const_cast(thread->GetCurrentFrame()); + ecmascript::FrameIterator it(current, thread); + for (; !it.Done(); it.Advance()) { + if (!it.IsJSFrame()) { continue; } - auto funcObj = frameHandler.GetFunction(); - JSHandle function(thread, funcObj); - if (function->GetFunctionKind() != ecmascript::FunctionKind::CONCURRENT_FUNCTION) { + auto method = it.CheckAndGetMethod(); + if (method == nullptr || method->IsNativeWithCallField() || + method->GetFunctionKind() != ecmascript::FunctionKind::CONCURRENT_FUNCTION) { continue; } + auto functionObj = it.GetFunction(); + JSHandle function(thread, functionObj); JSTaggedValue extraInfoValue = function->GetFunctionExtraInfo(); if (!extraInfoValue.IsJSNativePointer()) { LOG_ECMA(DEBUG) << "Concurrent function donnot have taskInfo"; @@ -3463,6 +3694,7 @@ std::string JSNApi::GetBundleName(EcmaVM *vm) void JSNApi::SetModuleName(EcmaVM *vm, const std::string &moduleName) { ecmascript::CString name = moduleName.c_str(); + ecmascript::pgo::PGOProfilerManager::GetInstance()->SetModuleName(moduleName); vm->SetModuleName(name); } @@ -3483,9 +3715,49 @@ void JSNApi::SynchronizVMInfo(EcmaVM *vm, const EcmaVM *hostVM) vm->SetModuleName(hostVM->GetModuleName()); vm->SetAssetPath(hostVM->GetAssetPath()); vm->SetIsBundlePack(hostVM->IsBundlePack()); - vm->GetAssociatedJSThread()->GetCurrentEcmaContext()->GetModuleManager()->SetExecuteMode( - hostVM->GetAssociatedJSThread()->GetCurrentEcmaContext()->GetModuleManager()->GetCurrentMode()); + + ecmascript::ModuleManager *vmModuleManager = + vm->GetAssociatedJSThread()->GetCurrentEcmaContext()->GetModuleManager(); + ecmascript::ModuleManager *hostVMModuleManager = + hostVM->GetAssociatedJSThread()->GetCurrentEcmaContext()->GetModuleManager(); + vmModuleManager->SetExecuteMode(hostVMModuleManager->GetExecuteMode()); vm->SetResolveBufferCallback(hostVM->GetResolveBufferCallback()); } +bool JSNApi::IsProfiling(EcmaVM *vm) +{ + return vm->GetProfilerState(); +} + +void JSNApi::SetProfilerState(const EcmaVM *vm, bool value) +{ + const_cast(vm)->SetProfilerState(value); +} + +void JSNApi::SetSourceMapTranslateCallback(EcmaVM *vm, SourceMapTranslateCallback callback) +{ + vm->SetSourceMapTranslateCallback(callback); +} + +TryCatch::~TryCatch() +{ + if (!rethrow_) { + ecmaVm_->GetJSThread()->ClearException(); + } +} + +bool TryCatch::HasCaught() const +{ + return ecmaVm_->GetJSThread()->HasPendingException(); +} + +void TryCatch::Rethrow() +{ + rethrow_ = true; +} + +Local TryCatch::GetAndClearException() +{ + return JSNApiHelper::ToLocal(ecmaVm_->GetAndClearEcmaUncaughtException()); +} } // namespace panda diff --git a/ecmascript/napi/jsnapi_helper.h b/ecmascript/napi/jsnapi_helper.h index 448b7fc34aad059266542501918d197e273c77b5..45e1d1a3e5529b4dc8431328884eb942e07b0ba7 100644 --- a/ecmascript/napi/jsnapi_helper.h +++ b/ecmascript/napi/jsnapi_helper.h @@ -33,6 +33,7 @@ #if ECMASCRIPT_ENABLE_NAPI_SPECIAL_CHECK #define LOG_IF_SPECIAL(handleValue, level) \ do { \ + LOG_ECMA(DEBUG) << "Enable napi special check"; \ if ((handleValue).GetTaggedValue().IsSpecial()) { \ LOG_FULL(level) << "Special value " << (handleValue).GetTaggedType() << " checked!"; \ } \ diff --git a/ecmascript/napi/test/dfx_jsnapi_tests.cpp b/ecmascript/napi/test/dfx_jsnapi_tests.cpp index 91b03ab2f93ccd2725b6af296734fa25e0b987e2..1b4ac07949a455c004eb14836bd3709775046b30 100644 --- a/ecmascript/napi/test/dfx_jsnapi_tests.cpp +++ b/ecmascript/napi/test/dfx_jsnapi_tests.cpp @@ -16,8 +16,9 @@ #include "ecmascript/dfx/hprof/heap_profiler_interface.h" #include "ecmascript/dfx/stackinfo/js_stackinfo.h" #include "ecmascript/dfx/vmstat/runtime_stat.h" -#include "ecmascript/mem/heap.h" +#include "ecmascript/mem/heap-inl.h" #include "ecmascript/mem/concurrent_marker.h" +#include "ecmascript/mem/concurrent_sweeper.h" #include "ecmascript/napi/include/dfx_jsnapi.h" #include "ecmascript/tests/test_helper.h" @@ -83,10 +84,11 @@ HWTEST_F_L0(DFXJSNApiTests, DumpHeapSnapshot_001) bool isVmMode = true; bool isPrivate = false; + bool captureNumericValue = false; std::fstream inputFile {}; EXPECT_TRUE(inputFile.good()); - DFXJSNApi::DumpHeapSnapshot(vm_, dumpFormat, filePath, isVmMode, isPrivate); + DFXJSNApi::DumpHeapSnapshot(vm_, dumpFormat, filePath, isVmMode, isPrivate, captureNumericValue); EXPECT_TRUE(MatchJSONLineHeader(inputFile, filePath, 1, "{\"snapshot\":")); EXPECT_TRUE(MatchJSONLineHeader(inputFile, filePath, 2, "{\"meta\":")); EXPECT_TRUE(MatchJSONLineHeader(inputFile, filePath, 3, "{\"node_fields\":")); @@ -114,10 +116,11 @@ HWTEST_F_L0(DFXJSNApiTests, DumpHeapSnapshot_002) ecmascript::Progress *progress = nullptr; bool isVmMode = true; bool isPrivate = false; + bool captureNumericValue = false; std::fstream fStream {}; EXPECT_TRUE(fStream.good()); - DFXJSNApi::DumpHeapSnapshot(vm_, dumpFormat, &stream, progress, isVmMode, isPrivate); + DFXJSNApi::DumpHeapSnapshot(vm_, dumpFormat, &stream, progress, isVmMode, isPrivate, captureNumericValue); EXPECT_TRUE(MatchJSONLineHeader(fStream, filePath, 1, "{\"snapshot\":")); EXPECT_TRUE(MatchJSONLineHeader(fStream, filePath, 2, "{\"meta\":")); EXPECT_TRUE(MatchJSONLineHeader(fStream, filePath, 3, "{\"node_fields\":")); diff --git a/ecmascript/napi/test/jsnapi_tests.cpp b/ecmascript/napi/test/jsnapi_tests.cpp index 1d391fdcd7f53bceadaa23f4cf4ddcc04ad78384..8ab4f9a3ee9e8c29614e9df3312141cd8ca548fc 100644 --- a/ecmascript/napi/test/jsnapi_tests.cpp +++ b/ecmascript/napi/test/jsnapi_tests.cpp @@ -29,6 +29,7 @@ #include "ecmascript/tagged_array.h" #include "ecmascript/tests/test_helper.h" #include "ecmascript/js_generator_object.h" +#include "gtest/gtest.h" using namespace panda; using namespace panda::ecmascript; @@ -1508,4 +1509,31 @@ HWTEST_F_L0(JSNApiTests, PromiseRejectInfo_GetData) void* dataRes = promisereject.GetData(); ASSERT_EQ(dataRes, data); } + +HWTEST_F_L0(JSNApiTests, FunctionCallScope) +{ + { + FunctionCallScope callScope(vm_); + ASSERT_FALSE(vm_->IsTopLevelCallDepth()); + } + ASSERT_TRUE(vm_->IsTopLevelCallDepth()); +} + +HWTEST_F_L0(JSNApiTests, AotTrigger) +{ + std::string bundle; + std::string module; + int32_t trigger = -1; + JSNApi::SetRequestAotCallback( + vm_, [&](const std::string &bundleName, const std::string &moduleName, int32_t triggerMode) -> bool { + bundle = bundleName; + module = moduleName; + trigger = triggerMode; + return 100; + }); + ASSERT_FALSE(vm_->RequestAot("com.test.test", "requestAot", RequestAotMode::RE_COMPILE_ON_IDLE)); + ASSERT_EQ(bundle, "com.test.test"); + ASSERT_EQ(module, "requestAot"); + ASSERT_EQ(trigger, 0); +} } // namespace panda::test diff --git a/ecmascript/object_factory-inl.h b/ecmascript/object_factory-inl.h index 7628652a6d2d4018dec175b644f0c80f1851d7f9..0f1298dd765f2add498fe8ed3e49f3d291390df6 100644 --- a/ecmascript/object_factory-inl.h +++ b/ecmascript/object_factory-inl.h @@ -18,6 +18,7 @@ #include "ecmascript/global_env_constants-inl.h" #include "ecmascript/global_env_constants.h" +#include "ecmascript/js_hclass-inl.h" #include "ecmascript/js_thread.h" #include "ecmascript/lexical_env.h" #include "ecmascript/mem/heap-inl.h" @@ -47,6 +48,13 @@ EcmaString *ObjectFactory::AllocOldSpaceLineStringObject(size_t size) JSHClass::Cast(thread_->GlobalConstants()->GetLineStringClass().GetTaggedObject()), size)); } +EcmaString *ObjectFactory::AllocSlicedStringObject(MemSpaceType type) +{ + NewObjectHook(); + return reinterpret_cast(AllocObjectWithSpaceType(SlicedString::SIZE, + JSHClass::Cast(thread_->GlobalConstants()->GetSlicedStringClass().GetTaggedObject()), type)); +} + EcmaString *ObjectFactory::AllocConstantStringObject(MemSpaceType type) { NewObjectHook(); @@ -73,7 +81,7 @@ JSHandle ObjectFactory::NewJSNativePointer(void *externalPointe if (nonMovable) { header = heap_->AllocateNonMovableOrHugeObject(jsNativePointerClass); } else { - header = heap_->AllocateYoungOrHugeObject(jsNativePointerClass); + header = heap_->AllocateOldOrHugeObject(jsNativePointerClass); } JSHandle obj(thread_, header); obj->SetExternalPointer(externalPointer); @@ -82,7 +90,7 @@ JSHandle ObjectFactory::NewJSNativePointer(void *externalPointe obj->SetBindingSize(nativeBindingsize); if (callBack != nullptr) { - heap_->IncreaseNativeBindingSize(nonMovable, nativeBindingsize); + heap_->IncreaseNativeBindingSize(nativeBindingsize); vm_->PushToNativePointerList(static_cast(header)); } return obj; diff --git a/ecmascript/object_factory.cpp b/ecmascript/object_factory.cpp index 3bd0f36d58096faa88131021690409a447914928..6f3909aeba9c785d628ac1e92fa3d967059a652c 100644 --- a/ecmascript/object_factory.cpp +++ b/ecmascript/object_factory.cpp @@ -13,6 +13,8 @@ * limitations under the License. */ +#include "ecmascript/object_factory-inl.h" + #include "ecmascript/accessor_data.h" #include "ecmascript/base/error_helper.h" #include "ecmascript/builtins/builtins.h" @@ -102,6 +104,7 @@ #include "ecmascript/jspandafile/program_object.h" #include "ecmascript/layout_info-inl.h" #include "ecmascript/linked_hash_table.h" +#include "ecmascript/marker_cell.h" #include "ecmascript/mem/heap-inl.h" #include "ecmascript/mem/region.h" #include "ecmascript/mem/space.h" @@ -251,6 +254,7 @@ void ObjectFactory::NewJSArrayBufferData(const JSHandle &array, i JSHandle pointer = NewJSNativePointer(newData, NativeAreaAllocator::FreeBufferFunc, vm_->GetNativeAreaAllocator(), false, size); array->SetArrayBufferData(thread_, pointer); + array->SetWithNativeAreaAllocator(true); } void ObjectFactory::NewJSSharedArrayBufferData(const JSHandle &array, int32_t length) @@ -268,6 +272,7 @@ void ObjectFactory::NewJSSharedArrayBufferData(const JSHandle &ar JSHandle pointer = NewJSNativePointer(newData, JSSharedMemoryManager::RemoveSharedMemory, JSSharedMemoryManager::GetInstance(), false, size); array->SetArrayBufferData(thread_, pointer); + array->SetWithNativeAreaAllocator(false); } JSHandle ObjectFactory::NewJSArrayBuffer(int32_t length) @@ -286,20 +291,11 @@ JSHandle ObjectFactory::NewJSArrayBuffer(int32_t length) JSHandle pointer = NewJSNativePointer(newData, NativeAreaAllocator::FreeBufferFunc, vm_->GetNativeAreaAllocator(), false, length); arrayBuffer->SetArrayBufferData(thread_, pointer.GetTaggedValue()); + arrayBuffer->SetWithNativeAreaAllocator(true); } return arrayBuffer; } -JSHandle ObjectFactory::NewJSArrayBuffer(int32_t length, const JSHandle &nativePtr) -{ - JSHandle env = vm_->GetGlobalEnv(); - JSHandle constructor(env->GetArrayBufferFunction()); - JSHandle arrayBuffer(NewJSObjectByConstructor(constructor)); - arrayBuffer->SetArrayBufferByteLength(length); - arrayBuffer->SetArrayBufferData(thread_, nativePtr.GetTaggedValue()); - return arrayBuffer; -} - JSHandle ObjectFactory::NewJSArrayBuffer(void *buffer, int32_t length, const DeleteEntryPoint &deleter, void *data, bool share) { @@ -313,6 +309,8 @@ JSHandle ObjectFactory::NewJSArrayBuffer(void *buffer, int32_t le JSHandle pointer = NewJSNativePointer(buffer, deleter, data, false, length); arrayBuffer->SetArrayBufferData(thread_, pointer.GetTaggedValue()); arrayBuffer->SetShared(share); + arrayBuffer->SetWithNativeAreaAllocator(deleter == NativeAreaAllocator::FreeBufferFunc && + data == vm_->GetNativeAreaAllocator()); } return arrayBuffer; } @@ -362,6 +360,7 @@ JSHandle ObjectFactory::NewJSSharedArrayBuffer(void *buffer, int3 JSSharedMemoryManager::GetInstance(), false, length); sharedArrayBuffer->SetArrayBufferData(thread_, pointer); sharedArrayBuffer->SetShared(true); + sharedArrayBuffer->SetWithNativeAreaAllocator(false); } return sharedArrayBuffer; } @@ -447,7 +446,7 @@ JSHandle ObjectFactory::CloneObjectLiteral(JSHandle object) cloneObject->SetProperties(thread_, newProperties.GetTaggedValue()); for (uint32_t i = 0; i < klass->GetInlinedProperties(); i++) { - cloneObject->SetPropertyInlinedProps(thread_, i, object->GetPropertyInlinedProps(i)); + cloneObject->SetPropertyInlinedPropsWithRep(thread_, i, object->GetPropertyInlinedProps(i)); } return cloneObject; } @@ -459,6 +458,7 @@ JSHandle ObjectFactory::CloneArrayLiteral(JSHandle object) JSHandle cloneObject(NewJSObject(klass)); cloneObject->SetArrayLength(thread_, object->GetArrayLength()); + cloneObject->SetTrackInfo(thread_, JSTaggedValue::Undefined()); JSHandle elements(thread_, object->GetElements()); static constexpr uint8_t MAX_READ_ONLY_ARRAY_LENGTH = 10; @@ -508,7 +508,7 @@ JSHandle ObjectFactory::CloneArrayLiteral(JSHandle object) } for (uint32_t i = 0; i < klass->GetInlinedProperties(); i++) { - cloneObject->SetPropertyInlinedProps(thread_, i, object->GetPropertyInlinedProps(i)); + cloneObject->SetPropertyInlinedPropsWithRep(thread_, i, object->GetPropertyInlinedProps(i)); } return cloneObject; } @@ -563,9 +563,10 @@ JSHandle ObjectFactory::CloneObjectLiteral(JSHandle object, cloneObject->SetProperties(thread_, newProperties.GetTaggedValue()); for (uint32_t i = 0; i < klass->GetInlinedProperties(); i++) { + auto layout = LayoutInfo::Cast(klass->GetLayout().GetTaggedObject()); JSTaggedValue value = object->GetPropertyInlinedProps(i); - if (!value.IsJSFunction()) { - cloneObject->SetPropertyInlinedProps(thread_, i, value); + if (!layout->GetAttr(i).IsTaggedRep() || !value.IsJSFunction()) { + cloneObject->SetPropertyInlinedPropsWithRep(thread_, i, value); } else { JSHandle valueHandle(thread_, value); JSHandle newFunc = CloneJSFuction(valueHandle); @@ -585,7 +586,6 @@ JSHandle ObjectFactory::CloneJSFuction(JSHandle func) JSTaggedValue length = func->GetPropertyInlinedProps(JSFunction::LENGTH_INLINE_PROPERTY_INDEX); cloneFunc->SetPropertyInlinedProps(thread_, JSFunction::LENGTH_INLINE_PROPERTY_INDEX, length); - cloneFunc->SetModule(thread_, func->GetModule()); return cloneFunc; } @@ -606,9 +606,10 @@ JSHandle ObjectFactory::CloneClassCtor(JSHandle ctor, co JSHandle cloneCtor = NewJSFunctionByHClass(method, hclass); for (uint32_t i = 0; i < hclass->GetInlinedProperties(); i++) { + auto layout = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject()); JSTaggedValue value = ctor->GetPropertyInlinedProps(i); - if (!value.IsJSFunction()) { - cloneCtor->SetPropertyInlinedProps(thread_, i, value); + if (!layout->GetAttr(i).IsTaggedRep() || !value.IsJSFunction()) { + cloneCtor->SetPropertyInlinedPropsWithRep(thread_, i, value); } else { JSHandle valueHandle(thread_, value); JSHandle newFunc = CloneJSFuction(valueHandle); @@ -677,7 +678,7 @@ JSHandle ObjectFactory::CreateJSRegExpInstanceClass(JSHandleAddKey(thread_, 0, globalConst->GetLastIndexString(), attributes); } @@ -701,7 +702,7 @@ JSHandle ObjectFactory::CreateJSArrayInstanceClass(JSHandleAddKey(thread_, 0, globalConst->GetLengthString(), attributes); } @@ -729,7 +730,7 @@ JSHandle ObjectFactory::CreateJSArguments(const JSHandle &e { PropertyAttributes attributes = PropertyAttributes::Default(true, false, true); attributes.SetIsInlinedProps(true); - attributes.SetRepresentation(Representation::MIXED); + attributes.SetRepresentation(Representation::TAGGED); attributes.SetOffset(fieldOrder++); layoutInfoHandle->AddKey(thread_, JSArguments::LENGTH_INLINE_PROPERTY_INDEX, globalConst->GetLengthString(), attributes); @@ -739,7 +740,7 @@ JSHandle ObjectFactory::CreateJSArguments(const JSHandle &e { PropertyAttributes attributes = PropertyAttributes::Default(true, false, true); attributes.SetIsInlinedProps(true); - attributes.SetRepresentation(Representation::MIXED); + attributes.SetRepresentation(Representation::TAGGED); attributes.SetOffset(fieldOrder++); layoutInfoHandle->AddKey(thread_, JSArguments::ITERATOR_INLINE_PROPERTY_INDEX, env->GetIteratorSymbol().GetTaggedValue(), attributes); @@ -750,7 +751,7 @@ JSHandle ObjectFactory::CreateJSArguments(const JSHandle &e PropertyAttributes attributes = PropertyAttributes::Default(false, false, false); attributes.SetIsInlinedProps(true); attributes.SetIsAccessor(true); - attributes.SetRepresentation(Representation::MIXED); + attributes.SetRepresentation(Representation::TAGGED); attributes.SetOffset(fieldOrder++); layoutInfoHandle->AddKey(thread_, JSArguments::CALLER_INLINE_PROPERTY_INDEX, thread_->GlobalConstants()->GetHandledCallerString().GetTaggedValue(), attributes); @@ -761,7 +762,7 @@ JSHandle ObjectFactory::CreateJSArguments(const JSHandle &e PropertyAttributes attributes = PropertyAttributes::Default(false, false, false); attributes.SetIsInlinedProps(true); attributes.SetIsAccessor(true); - attributes.SetRepresentation(Representation::MIXED); + attributes.SetRepresentation(Representation::TAGGED); attributes.SetOffset(fieldOrder++); layoutInfoHandle->AddKey(thread_, JSArguments::CALLEE_INLINE_PROPERTY_INDEX, thread_->GlobalConstants()->GetHandledCalleeString().GetTaggedValue(), attributes); @@ -852,6 +853,7 @@ JSHandle ObjectFactory::NewJSError(const ErrorType &errorType, const J JSHandle undefined = thread_->GlobalConstants()->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread_, ctor, nativePrototype, undefined, 1, needCheckStack); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSObject, thread_); info->SetCallArg(message.GetTaggedValue()); Method *method = JSHandle::Cast(ctor)->GetCallTarget(); JSTaggedValue obj = reinterpret_cast(const_cast(method->GetNativePointer()))(info); @@ -874,8 +876,7 @@ JSHandle ObjectFactory::NewJSObjectByConstructor(const JSHandleHasFunctionPrototype() || (constructor->GetProtoOrHClass().IsHeapObject() && constructor->GetFunctionPrototype().IsECMAObject())) { - JSHandle jshclass = JSFunction::GetInstanceJSHClass(thread_, constructor, - JSHandle(constructor)); + JSHandle jshclass(thread_, JSFunction::GetOrCreateInitialJSHClass(thread_, constructor)); return NewJSObjectWithInit(jshclass); } JSHandle env = vm_->GetGlobalEnv(); @@ -1038,7 +1039,8 @@ void ObjectFactory::InitializeJSObject(const JSHandle &obj, const JSHa } #endif case JSType::JS_ARRAY: { - JSArray::Cast(*obj)->SetLength(thread_, JSTaggedValue(0)); + JSArray::Cast(*obj)->SetLength(0); + JSArray::Cast(*obj)->SetTrackInfo(thread_, JSTaggedValue::Undefined()); ASSERT(!obj->GetJSHClass()->IsDictionaryMode()); auto accessor = thread_->GlobalConstants()->GetArrayLengthAccessor(); JSArray::Cast(*obj)->SetPropertyInlinedProps(thread_, JSArray::LENGTH_INLINE_PROPERTY_INDEX, accessor); @@ -1335,7 +1337,7 @@ FreeObject *ObjectFactory::FillFreeObject(uintptr_t address, size_t size, Remove uintptr_t hugeObjectHead) { FreeObject *object = nullptr; - const GlobalEnvConstants *globalConst = thread_->GlobalConstants(); + const GlobalEnvConstants *globalConst = thread_->GetFirstGlobalConst(); if (size >= FreeObject::SIZE_OFFSET && size < FreeObject::SIZE) { object = reinterpret_cast(address); object->SetClassWithoutBarrier(JSHClass::Cast(globalConst->GetFreeObjectWithOneFieldClass().GetTaggedObject())); @@ -1457,7 +1459,7 @@ JSHandle ObjectFactory::CreateFunctionClass(FunctionKind kind, uint32_ { PropertyAttributes attributes = PropertyAttributes::Default(false, false, true); attributes.SetIsInlinedProps(true); - attributes.SetRepresentation(Representation::MIXED); + attributes.SetRepresentation(Representation::TAGGED); attributes.SetOffset(fieldOrder); layoutInfoHandle->AddKey(thread_, fieldOrder, globalConst->GetLengthString(), attributes); fieldOrder++; @@ -1468,7 +1470,7 @@ JSHandle ObjectFactory::CreateFunctionClass(FunctionKind kind, uint32_ if (!JSFunction::IsClassConstructor(kind)) { PropertyAttributes attributes = PropertyAttributes::DefaultAccessor(false, false, true); attributes.SetIsInlinedProps(true); - attributes.SetRepresentation(Representation::MIXED); + attributes.SetRepresentation(Representation::TAGGED); attributes.SetOffset(fieldOrder); layoutInfoHandle->AddKey(thread_, fieldOrder, thread_->GlobalConstants()->GetHandledNameString().GetTaggedValue(), attributes); @@ -1479,7 +1481,7 @@ JSHandle ObjectFactory::CreateFunctionClass(FunctionKind kind, uint32_ ASSERT(JSFunction::PROTOTYPE_INLINE_PROPERTY_INDEX == fieldOrder); PropertyAttributes attributes = PropertyAttributes::DefaultAccessor(true, false, false); attributes.SetIsInlinedProps(true); - attributes.SetRepresentation(Representation::MIXED); + attributes.SetRepresentation(Representation::TAGGED); attributes.SetOffset(fieldOrder); layoutInfoHandle->AddKey(thread_, fieldOrder, globalConst->GetPrototypeString(), attributes); fieldOrder++; @@ -1487,7 +1489,7 @@ JSHandle ObjectFactory::CreateFunctionClass(FunctionKind kind, uint32_ ASSERT(JSFunction::CLASS_PROTOTYPE_INLINE_PROPERTY_INDEX == fieldOrder); PropertyAttributes attributes = PropertyAttributes::DefaultAccessor(false, false, false); attributes.SetIsInlinedProps(true); - attributes.SetRepresentation(Representation::MIXED); + attributes.SetRepresentation(Representation::TAGGED); attributes.SetOffset(fieldOrder); layoutInfoHandle->AddKey(thread_, fieldOrder, globalConst->GetPrototypeString(), attributes); fieldOrder++; @@ -1507,7 +1509,7 @@ JSHandle ObjectFactory::CreateDefaultClassPrototypeHClass(JSHClass *hc PropertyAttributes attributes = PropertyAttributes::Default(true, false, true); // non-enumerable attributes.SetIsInlinedProps(true); - attributes.SetRepresentation(Representation::MIXED); + attributes.SetRepresentation(Representation::TAGGED); attributes.SetOffset(ClassInfoExtractor::CONSTRUCTOR_INDEX); layout->AddKey(thread_, ClassInfoExtractor::CONSTRUCTOR_INDEX, thread_->GlobalConstants()->GetConstructorString(), attributes); @@ -1537,7 +1539,7 @@ JSHandle ObjectFactory::CreateDefaultClassConstructorHClass(JSHClass * attributes = PropertyAttributes::Default(false, false, true); } attributes.SetIsInlinedProps(true); - attributes.SetRepresentation(Representation::MIXED); + attributes.SetRepresentation(Representation::TAGGED); attributes.SetOffset(index); layout->AddKey(thread_, index, array->Get(index), attributes); } @@ -1614,11 +1616,13 @@ JSHandle ObjectFactory::NewMethod(const MethodLiteral *methodLiteral, Me method->SetCodeEntryOrLiteral(reinterpret_cast(methodLiteral)); method->SetConstantPool(thread_, JSTaggedValue::Undefined()); method->SetProfileTypeInfo(thread_, JSTaggedValue::Undefined()); + method->SetModule(thread_, JSTaggedValue::Undefined()); return method; } JSHandle ObjectFactory::NewMethod(const JSPandaFile *jsPandaFile, MethodLiteral *methodLiteral, - JSHandle constpool, uint32_t entryIndex, bool needSetAotFlag, bool *canFastCall) + JSHandle constpool, JSHandle module, + uint32_t entryIndex, bool needSetAotFlag, bool *canFastCall) { JSHandle method; if (jsPandaFile->IsNewVersion()) { @@ -1627,6 +1631,7 @@ JSHandle ObjectFactory::NewMethod(const JSPandaFile *jsPandaFile, Method method = NewMethod(methodLiteral); method->SetConstantPool(thread_, constpool); } + method->SetModule(thread_, module); if (needSetAotFlag) { thread_->GetCurrentEcmaContext()->GetAOTFileManager()-> SetAOTFuncEntry(jsPandaFile, *method, entryIndex, canFastCall); @@ -1699,6 +1704,7 @@ JSHandle ObjectFactory::NewJSIntlBoundFunction(MethodIndex JSHandle nameKey = globalConst->GetHandledNameString(); PropertyDescriptor nameDesc(thread_, emptyString, false, false, true); JSTaggedValue::DefinePropertyOrThrow(thread_, JSHandle::Cast(function), nameKey, nameDesc); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSIntlBoundFunction, thread_); return intlBoundFunc; } @@ -1719,6 +1725,7 @@ JSHandle ObjectFactory::NewJSProxyRevocFunction(const JSHa JSHandle nameKey = globalConst->GetHandledNameString(); PropertyDescriptor nameDesc(thread_, emptyString, false, false, true); JSTaggedValue::DefinePropertyOrThrow(thread_, JSHandle::Cast(function), nameKey, nameDesc); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSProxyRevocFunction, thread_); return revocFunction; } @@ -1740,10 +1747,11 @@ JSHandle ObjectFactory::NewJSGeneratorObject(JSHandle proto(thread_, JSHandle::Cast(generatorFunction)->GetProtoOrHClass()); if (!proto->IsECMAObject()) { JSHandle realmHandle = JSObject::GetFunctionRealm(thread_, generatorFunction); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSGeneratorObject, thread_); proto = realmHandle->GetGeneratorPrototype(); } JSHandle hclass = NewEcmaHClass(JSGeneratorObject::SIZE, JSType::JS_GENERATOR_OBJECT, proto); - JSHandle generatorObject = JSHandle::Cast(NewJSObject(hclass)); + JSHandle generatorObject = JSHandle::Cast(NewJSObjectWithInit(hclass)); generatorObject->SetGeneratorContext(thread_, JSTaggedValue::Undefined()); generatorObject->SetResumeResult(thread_, JSTaggedValue::Undefined()); return generatorObject; @@ -1754,6 +1762,7 @@ JSHandle ObjectFactory::NewJSAsyncGeneratorObject(JSHand JSHandle proto(thread_, JSHandle::Cast(generatorFunction)->GetProtoOrHClass()); if (!proto->IsECMAObject()) { JSHandle realmHandle = JSObject::GetFunctionRealm(thread_, generatorFunction); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSAsyncGeneratorObject, thread_); proto = realmHandle->GetAsyncGeneratorPrototype(); } JSHandle hclass = NewEcmaHClass(JSAsyncGeneratorObject::SIZE, @@ -1813,6 +1822,7 @@ JSHandle ObjectFactory::NewJSPrimitiveRef(const JSHandle(thread_, JSTaggedValue(length)), false, false, false); JSTaggedValue::DefinePropertyOrThrow(thread_, JSHandle(obj), lengthStr, desc); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSPrimitiveRef, thread_); } return obj; @@ -1882,6 +1892,18 @@ JSHandle ObjectFactory::NewLexicalEnv(int numSlots) return array; } +JSHandle ObjectFactory::NewEmptySymbol() +{ + NewObjectHook(); + TaggedObject *header = heap_->AllocateNonMovableOrHugeObject( + JSHClass::Cast(thread_->GlobalConstants()->GetSymbolClass().GetTaggedObject())); + JSHandle obj(thread_, JSSymbol::Cast(header)); + obj->SetDescription(thread_, JSTaggedValue::Undefined()); + obj->SetFlags(0); + obj->SetHashField(0); + return obj; +} + JSHandle ObjectFactory::NewJSSymbol() { NewObjectHook(); @@ -1956,25 +1978,25 @@ JSHandle ObjectFactory::NewSymbolWithTable(const JSHandle ObjectFactory::NewPrivateNameSymbolWithChar(const char *description) +JSHandle ObjectFactory::NewPrivateNameSymbolWithChar(std::string_view description) { JSHandle string = NewFromUtf8(description); return NewPrivateNameSymbol(JSHandle(string)); } -JSHandle ObjectFactory::NewWellKnownSymbolWithChar(const char *description) +JSHandle ObjectFactory::NewWellKnownSymbolWithChar(std::string_view description) { JSHandle string = NewFromUtf8(description); return NewWellKnownSymbol(JSHandle(string)); } -JSHandle ObjectFactory::NewPublicSymbolWithChar(const char *description) +JSHandle ObjectFactory::NewPublicSymbolWithChar(std::string_view description) { JSHandle string = NewFromUtf8(description); return NewPublicSymbol(JSHandle(string)); } -JSHandle ObjectFactory::NewSymbolWithTableWithChar(const char *description) +JSHandle ObjectFactory::NewSymbolWithTableWithChar(std::string_view description) { JSHandle string = NewFromUtf8(description); return NewSymbolWithTable(JSHandle(string)); @@ -2098,6 +2120,7 @@ JSHandle ObjectFactory::NewJSProxy(const JSHandle &targe proxy->SetMethod(thread_, vm_->GetMethodByIndex(MethodIndex::BUILTINS_GLOBAL_CALL_JS_PROXY)); proxy->SetTarget(thread_, target.GetTaggedValue()); proxy->SetHandler(thread_, handler.GetTaggedValue()); + proxy->SetIsRevoked(false); return proxy; } @@ -2179,9 +2202,8 @@ JSHandle ObjectFactory::NewTaggedArray(uint32_t length, JSTaggedVal return array; } -JSHandle ObjectFactory::NewAndCopyTaggedArray(JSHandle &srcElements, - uint32_t newLength, - uint32_t oldLength) +JSHandle ObjectFactory::NewAndCopyTaggedArray(JSHandle &srcElements, uint32_t newLength, + uint32_t oldLength, uint32_t k) { ASSERT(oldLength <= newLength); MemSpaceType spaceType = newLength < LENGTH_THRESHOLD ? MemSpaceType::SEMI_SPACE : MemSpaceType::OLD_SPACE; @@ -2193,12 +2215,12 @@ JSHandle ObjectFactory::NewAndCopyTaggedArray(JSHandle if (region->InYoungSpace() && !region->IsMarking()) { size_t size = oldLength * sizeof(JSTaggedType); if (memcpy_s(reinterpret_cast(dstElements->GetData()), size, - reinterpret_cast(srcElements->GetData()), size) != EOK) { + reinterpret_cast(srcElements->GetData() + k), size) != EOK) { LOG_FULL(FATAL) << "memcpy_s failed"; } } else { for (uint32_t i = 0; i < oldLength; i++) { - dstElements->Set(thread_, i, srcElements->Get(i)); + dstElements->Set(thread_, i, srcElements->Get(i + k)); } } for (uint32_t i = oldLength; i < newLength; i++) { @@ -2417,23 +2439,32 @@ JSHandle ObjectFactory::CopyArray(const JSHandle &old, return newArray; } -JSHandle ObjectFactory::CreateLayoutInfo(int properties, MemSpaceType type, - GrowMode mode, JSTaggedValue initVal) +JSHandle ObjectFactory::CreateLayoutInfo(int properties, MemSpaceType type, GrowMode mode) { int growLength = mode == GrowMode::GROW ? static_cast(LayoutInfo::ComputeGrowCapacity(properties)) : properties; uint32_t arrayLength = LayoutInfo::ComputeArrayLength(growLength); - JSHandle layoutInfoHandle = JSHandle::Cast(NewTaggedArray(arrayLength, initVal, type)); - layoutInfoHandle->SetNumberOfElements(thread_, 0); + JSHandle layoutInfoHandle = JSHandle::Cast(NewTaggedArrayWithoutInit(arrayLength, type)); + layoutInfoHandle->Initialize(thread_); return layoutInfoHandle; } -JSHandle ObjectFactory::ExtendLayoutInfo(const JSHandle &old, int properties, - JSTaggedValue initVal) +JSHandle ObjectFactory::ExtendLayoutInfo(const JSHandle &old, int properties) { ASSERT(properties >= old->NumberOfElements()); uint32_t arrayLength = LayoutInfo::ComputeArrayLength(LayoutInfo::ComputeGrowCapacity(properties)); - return JSHandle(ExtendArray(JSHandle(old), arrayLength, initVal)); + ASSERT(arrayLength > old->GetLength()); + + auto oldArray = JSHandle(old); + auto newArray = NewTaggedArrayWithoutInit(arrayLength, MemSpaceType::SEMI_SPACE); + JSHandle::Cast(newArray)->Initialize(thread_, oldArray->GetExtraLength()); + + uint32_t oldLength = old->GetLength(); + for (uint32_t i = 0; i < oldLength; i++) { + JSTaggedValue value = oldArray->Get(i); + newArray->Set(thread_, i, value); + } + return JSHandle::Cast(newArray); } JSHandle ObjectFactory::CopyLayoutInfo(const JSHandle &old) @@ -2461,7 +2492,7 @@ JSHandle ObjectFactory::NewConstantPool(uint32_t capacity) auto header = heap_->AllocateOldOrHugeObject( JSHClass::Cast(thread_->GlobalConstants()->GetConstantPoolClass().GetTaggedObject()), size); JSHandle array(thread_, header); - array->InitializeWithSpecialValue(JSTaggedValue::Hole(), capacity); + array->InitializeWithSpecialValue(thread_, JSTaggedValue::Hole(), capacity); return array; } @@ -2486,6 +2517,7 @@ JSHandle ObjectFactory::NewModuleNamespace() JSHandle moduleNamespace = JSHandle::Cast(obj); moduleNamespace->SetModule(thread_, JSTaggedValue::Undefined()); moduleNamespace->SetExports(thread_, JSTaggedValue::Undefined()); + moduleNamespace->SetDeregisterProcession(thread_, JSTaggedValue::Undefined()); return moduleNamespace; } @@ -2611,6 +2643,16 @@ JSHandle ObjectFactory::NewProtoChangeMarker() return marker; } +JSHandle ObjectFactory::NewMarkerCell() +{ + NewObjectHook(); + TaggedObject *header = heap_->AllocateYoungOrHugeObject( + JSHClass::Cast(thread_->GlobalConstants()->GetMarkerCellClass().GetTaggedObject())); + JSHandle marker(thread_, header); + marker->ClearBitField(); + return marker; +} + JSHandle ObjectFactory::NewProtoChangeDetails() { NewObjectHook(); @@ -2627,9 +2669,9 @@ JSHandle ObjectFactory::NewProfileTypeInfo(uint32_t length) NewObjectHook(); ASSERT(length > 0); - size_t size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), length); + size_t size = ProfileTypeInfo::ComputeSize(length); auto header = heap_->AllocateYoungOrHugeObject( - JSHClass::Cast(thread_->GlobalConstants()->GetArrayClass().GetTaggedObject()), size); + JSHClass::Cast(thread_->GlobalConstants()->GetProfileTypeInfoClass().GetTaggedObject()), size); JSHandle array(thread_, header); array->InitializeWithSpecialValue(JSTaggedValue::Undefined(), length); @@ -2654,7 +2696,7 @@ JSHandle ObjectFactory::NewBigInt(uint32_t length) void ObjectFactory::NewObjectHook() const { #ifndef NDEBUG - if (vm_->GetJSOptions().EnableForceGC() && vm_->IsInitialized()) { + if (vm_->GetJSOptions().EnableForceGC() && vm_->IsInitialized() && thread_->IsAllContextsInitialized()) { if (vm_->GetJSOptions().ForceFullGC()) { vm_->CollectGarbage(TriggerGCType::YOUNG_GC); vm_->CollectGarbage(TriggerGCType::OLD_GC); @@ -3089,17 +3131,16 @@ JSHandle ObjectFactory::CreateObjectClass(const JSHandle } attributes.SetIsInlinedProps(true); - attributes.SetRepresentation(Representation::MIXED); + attributes.SetRepresentation(Representation::TAGGED); attributes.SetOffset(fieldOrder); layoutInfoHandle->AddKey(thread_, fieldOrder, key.GetTaggedValue(), attributes); fieldOrder++; } - ASSERT(fieldOrder <= PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES); + ASSERT(fieldOrder <= PropertyAttributes::MAX_FAST_PROPS_CAPACITY); JSHandle objClass = NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, fieldOrder); objClass->SetPrototype(thread_, proto.GetTaggedValue()); { objClass->SetExtensible(true); - objClass->SetIsLiteral(true); objClass->SetLayout(thread_, layoutInfoHandle); objClass->SetNumberOfProps(fieldOrder); } @@ -3120,7 +3161,7 @@ JSHandle ObjectFactory::SetLayoutInObjHClass(const JSHandle ObjectFactory::GetObjectLiteralHClass(const JSHandleSetNumberOfProps(0); objHClass->SetExtensible(true); - objHClass->SetIsLiteral(true); } hclassCacheArr->Set(thread_, length, objHClass); return SetLayoutInObjHClass(properties, length, objHClass); @@ -3331,6 +3371,7 @@ JSHandle ObjectFactory::NewTSFunctionType(uint32_t length) functionType->SetReturnGT(GlobalTSTypeRef::Default()); functionType->SetThisGT(GlobalTSTypeRef::Default()); functionType->ClearBitField(); + functionType->SetMethodOffset(0); JSHandle parameterTypes = NewTaggedArray(length, JSTaggedValue::Undefined()); functionType->SetParameterTypes(thread_, parameterTypes); @@ -3410,31 +3451,31 @@ JSHandle ObjectFactory::NewTSNamespaceType() return namespaceType; } // ----------------------------------- new string ---------------------------------------- -JSHandle ObjectFactory::NewFromASCII(const CString &data) +JSHandle ObjectFactory::NewFromASCII(std::string_view data) { - auto utf8Data = reinterpret_cast(data.c_str()); + auto utf8Data = reinterpret_cast(data.data()); ASSERT(EcmaStringAccessor::CanBeCompressed(utf8Data, data.length())); return GetStringFromStringTable(utf8Data, data.length(), true); } -JSHandle ObjectFactory::NewFromASCIINonMovable(const CString &data) +JSHandle ObjectFactory::NewFromASCIINonMovable(std::string_view data) { - auto utf8Data = reinterpret_cast(data.c_str()); + auto utf8Data = reinterpret_cast(data.data()); ASSERT(EcmaStringAccessor::CanBeCompressed(utf8Data, data.length())); return GetStringFromStringTableNonMovable(utf8Data, data.length()); } -JSHandle ObjectFactory::NewFromUtf8(const CString &data) +JSHandle ObjectFactory::NewFromUtf8(std::string_view data) { - auto utf8Data = reinterpret_cast(data.c_str()); + auto utf8Data = reinterpret_cast(data.data()); bool canBeCompress = EcmaStringAccessor::CanBeCompressed(utf8Data, data.length()); return GetStringFromStringTable(utf8Data, data.length(), canBeCompress); } -JSHandle ObjectFactory::NewFromUtf16(const CS16tring &data) +JSHandle ObjectFactory::NewFromUtf16(std::u16string_view data) { uint32_t length = data.length(); - auto utf16Data = reinterpret_cast(data.c_str()); + auto utf16Data = reinterpret_cast(data.data()); bool canBeCompress = EcmaStringAccessor::CanBeCompressed(utf16Data, length); return GetStringFromStringTable(utf16Data, length, canBeCompress); } @@ -3905,6 +3946,7 @@ JSHandle ObjectFactory::NewSourceTextModule() obj->SetStatus(ModuleStatus::UNINSTANTIATED); obj->SetTypes(ModuleTypes::UNKNOWN); obj->SetIsNewBcVersion(false); + obj->SetRegisterCounts(UINT16_MAX); return obj; } @@ -3959,10 +4001,10 @@ JSHandle ObjectFactory::NewCellRecord() return obj; } -JSHandle ObjectFactory::CreateIteratorResultInstanceClass() +JSHandle ObjectFactory::CreateIteratorResultInstanceClass(const JSHandle &env) { auto globalConst = thread_->GlobalConstants(); - JSHandle proto = vm_->GetGlobalEnv()->GetObjectFunctionPrototype(); + JSHandle proto = env->GetObjectFunctionPrototype(); JSHandle iterResultClass = NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, proto); uint32_t fieldOrder = 0; @@ -3971,7 +4013,7 @@ JSHandle ObjectFactory::CreateIteratorResultInstanceClass() ASSERT(JSIterator::VALUE_INLINE_PROPERTY_INDEX == fieldOrder); PropertyAttributes attributes = PropertyAttributes::Default(); attributes.SetIsInlinedProps(true); - attributes.SetRepresentation(Representation::MIXED); + attributes.SetRepresentation(Representation::TAGGED); attributes.SetOffset(fieldOrder); layoutInfoHandle->AddKey(thread_, fieldOrder++, globalConst->GetValueString(), attributes); } @@ -3979,7 +4021,7 @@ JSHandle ObjectFactory::CreateIteratorResultInstanceClass() ASSERT(JSIterator::DONE_INLINE_PROPERTY_INDEX == fieldOrder); PropertyAttributes attributes = PropertyAttributes::Default(); attributes.SetIsInlinedProps(true); - attributes.SetRepresentation(Representation::MIXED); + attributes.SetRepresentation(Representation::TAGGED); attributes.SetOffset(fieldOrder); layoutInfoHandle->AddKey(thread_, fieldOrder++, globalConst->GetDoneString(), attributes); } @@ -4024,7 +4066,8 @@ JSHandle ObjectFactory::NewJSStableArrayWithElements(const JSHandle array = JSHandle::Cast(NewJSObject(cls)); array->SetElements(thread_, elements); - array->SetLength(thread_, JSTaggedValue(elements->GetLength())); + array->SetLength(elements->GetLength()); + array->SetTrackInfo(thread_, JSTaggedValue::Undefined()); auto accessor = thread_->GlobalConstants()->GetArrayLengthAccessor(); array->SetPropertyInlinedProps(thread_, JSArray::LENGTH_INLINE_PROPERTY_INDEX, accessor); return array; @@ -4069,7 +4112,7 @@ JSHandle ObjectFactory::NewAsyncIteratorRecord(const JSHand JSHandle ObjectFactory::NewAOTLiteralInfo(uint32_t length, JSTaggedValue initVal) { NewObjectHook(); - size_t size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), length); + size_t size = AOTLiteralInfo::ComputeSize(length); auto header = heap_->AllocateYoungOrHugeObject( JSHClass::Cast(thread_->GlobalConstants()->GetAOTLiteralInfoClass().GetTaggedObject()), size); diff --git a/ecmascript/object_factory.h b/ecmascript/object_factory.h index c6d4a09e7b96dc534231719f6292484f12720a37..39e63adff91a5683093da8b880fe06dc73182963 100644 --- a/ecmascript/object_factory.h +++ b/ecmascript/object_factory.h @@ -166,6 +166,7 @@ class StoreTSHandler; class PropertyBox; class ProtoChangeMarker; class ProtoChangeDetails; +class MarkerCell; class ProfileTypeInfo; class MachineCode; class ClassInfoExtractor; @@ -272,6 +273,8 @@ public: inline LexicalEnv *InlineNewLexicalEnv(int numSlots); + JSHandle NewEmptySymbol(); + JSHandle NewJSSymbol(); JSHandle NewPrivateSymbol(); @@ -284,13 +287,13 @@ public: JSHandle NewSymbolWithTable(const JSHandle &name); - JSHandle NewPrivateNameSymbolWithChar(const char *description); + JSHandle NewPrivateNameSymbolWithChar(std::string_view description); - JSHandle NewWellKnownSymbolWithChar(const char *description); + JSHandle NewWellKnownSymbolWithChar(std::string_view description); - JSHandle NewPublicSymbolWithChar(const char *description); + JSHandle NewPublicSymbolWithChar(std::string_view description); - JSHandle NewSymbolWithTableWithChar(const char *description); + JSHandle NewSymbolWithTableWithChar(std::string_view description); JSHandle NewAccessorData(); JSHandle NewInternalAccessor(void *setter, void *getter); @@ -340,7 +343,7 @@ public: } JSHandle NewAndCopyTaggedArray(JSHandle &srcElements, uint32_t newLength, - uint32_t oldLength); + uint32_t oldLength, uint32_t k = 0); JSHandle NewTaggedArray(uint32_t length, JSTaggedValue initVal = JSTaggedValue::Hole()); JSHandle NewTaggedArray(uint32_t length, JSTaggedValue initVal, bool nonMovable); JSHandle NewTaggedArray(uint32_t length, JSTaggedValue initVal, MemSpaceType spaceType); @@ -356,6 +359,8 @@ public: JSHandle NewProtoChangeMarker(); JSHandle NewProtoChangeDetails(); + + JSHandle NewMarkerCell(); JSHandle NewBigInt(uint32_t length); // use for copy properties keys's array to another array JSHandle ExtendArray(const JSHandle &old, uint32_t length, @@ -370,11 +375,9 @@ public: const JSHandle &obj); JSHandle CreateLayoutInfo(int properties, MemSpaceType type = MemSpaceType::SEMI_SPACE, - GrowMode mode = GrowMode::GROW, - JSTaggedValue initVal = JSTaggedValue::Hole()); + GrowMode mode = GrowMode::GROW); - JSHandle ExtendLayoutInfo(const JSHandle &old, int properties, - JSTaggedValue initVal = JSTaggedValue::Hole()); + JSHandle ExtendLayoutInfo(const JSHandle &old, int properties); JSHandle CopyLayoutInfo(const JSHandle &old); @@ -449,8 +452,6 @@ public: JSHandle NewJSArrayBuffer(int32_t length); - JSHandle NewJSArrayBuffer(int32_t length, const JSHandle &nativePtr); - JSHandle NewJSArrayBuffer(void *buffer, int32_t length, const DeleteEntryPoint &deleter, void *data, bool share = false); @@ -487,7 +488,8 @@ public: JSHandle NewMethod(const MethodLiteral *methodLiteral, MemSpaceType spaceType = OLD_SPACE); JSHandle NewMethod(const JSPandaFile *jsPandaFile, MethodLiteral *methodLiteral, - JSHandle constpool, uint32_t entryIndex, bool needSetAotFlag, bool *canFastCall = nullptr); + JSHandle constpool, JSHandle module, + uint32_t entryIndex, bool needSetAotFlag, bool *canFastCall = nullptr); // used for creating jsobject by constructor JSHandle NewJSObjectByConstructor(const JSHandle &constructor, @@ -516,9 +518,9 @@ public: JSHandle NewTSNamespaceType(); // ----------------------------------- new string ---------------------------------------- - JSHandle NewFromASCII(const CString &data); - JSHandle NewFromUtf8(const CString &data); - JSHandle NewFromUtf16(const CS16tring &data); + JSHandle NewFromASCII(std::string_view data); + JSHandle NewFromUtf8(std::string_view data); + JSHandle NewFromUtf16(std::u16string_view data); JSHandle NewFromStdString(const std::string &data); @@ -538,6 +540,7 @@ public: inline EcmaString *AllocLineStringObject(size_t size); inline EcmaString *AllocOldSpaceLineStringObject(size_t size); inline EcmaString *AllocNonMovableLineStringObject(size_t size); + inline EcmaString *AllocSlicedStringObject(MemSpaceType type); inline EcmaString *AllocConstantStringObject(MemSpaceType type); inline EcmaString *AllocTreeStringObject(); @@ -551,6 +554,10 @@ public: JSHandle NewEcmaHClass(uint32_t size, JSType type, const JSHandle &prototype, bool isOptimized = false, bool canFastCall = false); + // used for creating jshclass in Builtins, Function, Class_Linker + JSHandle NewEcmaHClass(uint32_t size, JSType type, + uint32_t inlinedProps = JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS); + // It is used to provide iterators for non ECMA standard jsapi containers. JSHandle NewJSAPIPlainArray(uint32_t capacity); JSHandle NewJSAPIPlainArrayIterator(const JSHandle &plainarray, @@ -616,7 +623,7 @@ public: JSHandle NewCjsExports(); JSHandle NewCjsRequire(); - JSHandle CreateIteratorResultInstanceClass(); + JSHandle CreateIteratorResultInstanceClass(const JSHandle &env); // --------------------------------------old space object-------------------------------------------- JSHandle NewOldSpaceJSObject(const JSHandle &jshclass); @@ -655,9 +662,6 @@ private: void NewObjectHook() const; - // used for creating jshclass in Builtins, Function, Class_Linker - JSHandle NewEcmaHClass(uint32_t size, JSType type, - uint32_t inlinedProps = JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS); // used for creating jshclass in GlobalEnv, EcmaVM JSHandle NewEcmaHClassClass(JSHClass *hclass, uint32_t size, JSType type); @@ -669,7 +673,7 @@ private: JSHandle NewNonMovableJSObject(const JSHandle &jshclass); // used to create nonmovable utf8 string at global constants - JSHandle NewFromASCIINonMovable(const CString &data); + JSHandle NewFromASCIINonMovable(std::string_view data); // used for creating Function JSHandle NewJSFunction(const JSHandle &env, const JSHandle &hclass); diff --git a/ecmascript/object_fast_operator-inl.h b/ecmascript/object_fast_operator-inl.h index dc9c6467a642b9950227532e984cda46b5bab3a5..d67395957b001f9e940da87663544aa7bb861227 100644 --- a/ecmascript/object_fast_operator-inl.h +++ b/ecmascript/object_fast_operator-inl.h @@ -41,6 +41,43 @@ namespace panda::ecmascript { return JSTaggedValue::Hole(); \ } +JSTaggedValue ObjectFastOperator::HasOwnProperty(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key) +{ + [[maybe_unused]] DisallowGarbageCollection noGc; + if (!receiver.IsHeapObject() || + !(receiver.GetTaggedObject()->GetClass()->GetObjectType() == JSType::JS_OBJECT)) { + return JSTaggedValue::Hole(); + } + if (!key.IsString() || !EcmaStringAccessor(key).IsInternString()) { + return JSTaggedValue::Hole(); + } + + auto *hclass = receiver.GetTaggedObject()->GetClass(); + if (LIKELY(!hclass->IsDictionaryMode())) { + ASSERT(!TaggedArray::Cast(JSObject::Cast(receiver)->GetProperties().GetTaggedObject())->IsDictionaryMode()); + + int entry = JSHClass::FindPropertyEntry(thread, hclass, key); + if (entry != -1) { + LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject()); + PropertyAttributes attr(layoutInfo->GetAttr(entry)); + ASSERT(static_cast(attr.GetOffset()) == entry); + auto value = JSObject::Cast(receiver)->GetProperty(hclass, attr); + return value; + } + } else { + TaggedArray *array = TaggedArray::Cast(JSObject::Cast(receiver)->GetProperties().GetTaggedObject()); + ASSERT(array->IsDictionaryMode()); + NameDictionary *dict = NameDictionary::Cast(array); + int entry = dict->FindEntry(key); + if (entry != -1) { + auto value = dict->GetValue(entry); + return value; + } + } + // not found + return JSTaggedValue::Hole(); +} + template JSTaggedValue ObjectFastOperator::GetPropertyByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key) { @@ -73,9 +110,6 @@ JSTaggedValue ObjectFastOperator::GetPropertyByName(JSThread *thread, JSTaggedVa PropertyAttributes attr(layoutInfo->GetAttr(entry)); ASSERT(static_cast(attr.GetOffset()) == entry); auto value = JSObject::Cast(holder)->GetProperty(hclass, attr); - if (value.IsPropertyBox()) { - return PropertyBox::Cast(value.GetTaggedObject())->GetValue(); - } if (UNLIKELY(attr.IsAccessor())) { return CallGetter(thread, receiver, holder, value); } @@ -230,9 +264,10 @@ JSTaggedValue ObjectFastOperator::GetPropertyByIndex(JSThread *thread, JSTaggedV auto *hclass = holder.GetTaggedObject()->GetClass(); JSType jsType = hclass->GetObjectType(); if (IsSpecialIndexedObj(jsType)) { + if (jsType == JSType::JS_TYPED_ARRAY) { + return JSTaggedValue::Hole(); + } if (IsFastTypeArray(jsType)) { - holder = JSObject::Cast(holder)->GetJSHClass()->GetPrototype(); - CHECK_IS_ON_PROTOTYPE_CHAIN(receiver, holder); return JSTypedArray::FastGetPropertyByIndex(thread, receiver, index, jsType); } if (IsSpecialContainer(jsType)) { @@ -285,6 +320,9 @@ JSTaggedValue ObjectFastOperator::SetPropertyByIndex(JSThread *thread, JSTaggedV auto *hclass = holder.GetTaggedObject()->GetClass(); JSType jsType = hclass->GetObjectType(); if (IsSpecialIndexedObj(jsType)) { + if (jsType == JSType::JS_TYPED_ARRAY) { + return JSTaggedValue::Hole(); + } if (IsFastTypeArray(jsType)) { CHECK_IS_ON_PROTOTYPE_CHAIN(receiver, holder); return JSTypedArray::FastSetPropertyByIndex(thread, receiver, index, value, jsType); @@ -304,7 +342,7 @@ JSTaggedValue ObjectFastOperator::SetPropertyByIndex(JSThread *thread, JSTaggedV if (!elements->Get(index).IsHole()) { if (holder.IsJSCOWArray()) { [[maybe_unused]] EcmaHandleScope handleScope(thread); - JSHandle holderHandler(thread, JSArray::Cast(holder.GetTaggedObject())); + JSHandle holderHandler(thread, holder); JSHandle valueHandle(thread, value); // CheckAndCopyArray may cause gc. JSArray::CheckAndCopyArray(thread, holderHandler); @@ -367,15 +405,20 @@ JSTaggedValue ObjectFastOperator::SetPropertyByValue(JSThread *thread, JSTaggedV return SetPropertyByIndex(thread, receiver, index, value); } if (!key.IsNumber()) { - if (key.IsString() && !EcmaStringAccessor(key).IsInternString()) { - // update string stable - [[maybe_unused]] EcmaHandleScope handleScope(thread); - JSHandle receiverHandler(thread, receiver); - JSHandle valueHandler(thread, value); - key = JSTaggedValue(thread->GetEcmaVM()->GetFactory()->InternString(JSHandle(thread, key))); - // Maybe moved by GC - receiver = receiverHandler.GetTaggedValue(); - value = valueHandler.GetTaggedValue(); + if (key.IsString()) { + if (!EcmaStringAccessor(key).IsInternString()) { + // update string stable + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle receiverHandler(thread, receiver); + JSHandle valueHandler(thread, value); + key = JSTaggedValue( + thread->GetEcmaVM()->GetFactory()->InternString(JSHandle(thread, key))); + // Maybe moved by GC + receiver = receiverHandler.GetTaggedValue(); + value = valueHandler.GetTaggedValue(); + } + } else { + ObjectOperator::UpdateDetector(thread, receiver, key); } return ObjectFastOperator::SetPropertyByName(thread, receiver, key, value); } @@ -436,12 +479,11 @@ JSTaggedValue ObjectFastOperator::FastGetPropertyByValue(JSThread *thread, JSTag return result; } -template // UseHole is only for Array::Sort() which requires Hole order JSTaggedValue ObjectFastOperator::FastGetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index) { INTERPRETER_TRACE(thread, FastGetPropertyByIndex); JSTaggedValue result = ObjectFastOperator::GetPropertyByIndex(thread, receiver, index); - if (result.IsHole() && !UseHole) { + if (result.IsHole()) { return JSTaggedValue::GetProperty(thread, JSHandle(thread, receiver), index).GetValue().GetTaggedValue(); } @@ -509,12 +551,12 @@ PropertyAttributes ObjectFastOperator::AddPropertyByName(JSThread *thread, JSHan if (!array->IsDictionaryMode()) { attr.SetIsInlinedProps(false); - uint32_t nonInlinedProps = static_cast(objHandle->GetJSHClass()->GetNextNonInlinedPropsIndex()); ASSERT(length >= nonInlinedProps); // if array is full, grow array or change to dictionary mode if (length == nonInlinedProps) { - if (UNLIKELY(length == JSHClass::MAX_CAPACITY_OF_OUT_OBJECTS)) { + uint32_t maxNonInlinedFastPropsCapacity = objHandle->GetNonInlinedFastPropsCapacity(); + if (UNLIKELY(length >= maxNonInlinedFastPropsCapacity)) { // change to dictionary and add one. JSHandle dict(JSObject::TransitionToDictionary(thread, objHandle)); JSHandle newDict = @@ -524,7 +566,7 @@ PropertyAttributes ObjectFastOperator::AddPropertyByName(JSThread *thread, JSHan return attr; } // Grow properties array size - uint32_t capacity = JSObject::ComputePropertyCapacity(length); + uint32_t capacity = JSObject::ComputeNonInlinedFastPropsCapacity(length, maxNonInlinedFastPropsCapacity); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); array.Update(factory->CopyArray(array, length, capacity).GetTaggedValue()); objHandle->SetProperties(thread, array.GetTaggedValue()); @@ -568,6 +610,7 @@ JSTaggedValue ObjectFastOperator::CallSetter(JSThread *thread, JSTaggedValue rec auto accessor = AccessorData::Cast(accessorValue.GetTaggedObject()); bool success = JSObject::CallSetter(thread, *accessor, objHandle, valueHandle, true); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return success ? JSTaggedValue::Undefined() : JSTaggedValue::Exception(); } @@ -742,7 +785,7 @@ JSTaggedValue ObjectFastOperator::AddPropertyByIndex(JSThread *thread, JSTaggedV return success ? JSTaggedValue::Undefined() : JSTaggedValue::Exception(); } -int32_t ObjectFastOperator::TryToElementsIndex(JSTaggedValue key) +int64_t ObjectFastOperator::TryToElementsIndex(JSTaggedValue key) { if (LIKELY(key.IsInt())) { return key.GetInt(); @@ -750,7 +793,7 @@ int32_t ObjectFastOperator::TryToElementsIndex(JSTaggedValue key) if (key.IsString()) { uint32_t index = 0; if (JSTaggedValue::StringToElementIndex(key, &index)) { - return static_cast(index); + return static_cast(index); } } else if (key.IsDouble()) { double number = key.GetDouble(); diff --git a/ecmascript/object_fast_operator.h b/ecmascript/object_fast_operator.h index b90c48d5ade212be6f21d22ca74fc0083d6279e0..252f24e3e04979545d137c22da56d3802cf37d2a 100644 --- a/ecmascript/object_fast_operator.h +++ b/ecmascript/object_fast_operator.h @@ -16,11 +16,14 @@ #ifndef ECMASCRIPT_OBJECT_FAST_OPERATOR_H #define ECMASCRIPT_OBJECT_FAST_OPERATOR_H +#include "ecmascript/js_hclass.h" #include "ecmascript/js_tagged_value.h" +#include "ecmascript/property_attributes.h" namespace panda::ecmascript { class ObjectFastOperator final { public: + static inline JSTaggedValue HasOwnProperty(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key); template static inline JSTaggedValue GetPropertyByName(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key); @@ -52,7 +55,6 @@ public: static inline JSTaggedValue FastGetPropertyByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key); - template static inline JSTaggedValue FastGetPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index); static inline JSTaggedValue FastParseDate(const EcmaString *str); @@ -95,11 +97,11 @@ private: static inline JSTaggedValue AddPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index, JSTaggedValue value); - static inline int32_t TryToElementsIndex(JSTaggedValue key); + static inline int64_t TryToElementsIndex(JSTaggedValue key); static inline bool GetNumFromString(const char *str, int len, int *index, int *num); friend class FastRuntimeStub; }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_OBJECT_FAST_OPERATOR_H \ No newline at end of file +#endif // ECMASCRIPT_OBJECT_FAST_OPERATOR_H diff --git a/ecmascript/object_operator.cpp b/ecmascript/object_operator.cpp index 7c23645e2011f94a32467a7d3265274c443a4a69..4acbf7c969c6a7056c5a517dae91b66305349b12 100644 --- a/ecmascript/object_operator.cpp +++ b/ecmascript/object_operator.cpp @@ -30,6 +30,8 @@ #include "ecmascript/mem/c_string.h" #include "ecmascript/object_factory.h" #include "ecmascript/object_fast_operator-inl.h" +#include "ecmascript/property_attributes.h" +#include "ecmascript/property_detector-inl.h" #include "ecmascript/tagged_dictionary.h" namespace panda::ecmascript { @@ -90,8 +92,7 @@ void ObjectOperator::HandleKey(const JSHandle &key) void ObjectOperator::UpdateHolder() { - if (holder_->IsString() && - (IsElement() && elementIndex_ < EcmaStringAccessor(holder_->GetTaggedObject()).GetLength())) { + if (holder_->IsString() && (GetThroughElement() || GetStringLength())) { JSHandle undefined = thread_->GlobalConstants()->GetHandledUndefined(); holder_.Update(JSPrimitiveRef::StringCreate(thread_, holder_, undefined).GetTaggedValue()); } else { @@ -213,6 +214,54 @@ void ObjectOperator::FastAdd(JSThread *thread, const JSTaggedValue &receiver, co op.AddPropertyInternal(value); } +void ObjectOperator::UpdateDetector() +{ + if (IsElement()) { + return; + } + ObjectOperator::UpdateDetector(thread_, holder_.GetTaggedValue(), key_.GetTaggedValue()); +} + +// static +void ObjectOperator::UpdateDetector(const JSThread *thread, JSTaggedValue receiver, JSTaggedValue key) +{ + // skip env prepare + if (!thread->IsAllContextsInitialized()) { + return; + } + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + bool maybeDetector = IsDetectorName(env, key); + if (!maybeDetector) { + return; + } + // only support symbol keys now + ASSERT(key.IsSymbol()); + if (key == env->GetTaggedReplaceSymbol()) { + if (receiver.IsJSRegExp() || receiver == env->GetTaggedRegExpPrototype()) { + if (!PropertyDetector::IsRegExpReplaceDetectorValid(env)) { + return; + } + PropertyDetector::InvalidateRegExpReplaceDetector(env); + } + } else if (key == env->GetTaggedSplitSymbol()) { + if (receiver.IsJSRegExp() || receiver == env->GetTaggedRegExpPrototype()) { + if (!PropertyDetector::IsRegExpSplitDetectorValid(env)) { + return; + } + PropertyDetector::InvalidateRegExpSplitDetector(env); + } + } +} + +// static +bool ObjectOperator::IsDetectorName(JSHandle env, JSTaggedValue key) +{ + uintptr_t start = GlobalEnv::GetFirstDetectorSymbolAddr(*env); + uintptr_t end = GlobalEnv::GetLastDetectorSymbolAddr(*env); + uintptr_t addr = key.GetRawData(); + return (start <= addr) && (addr <= end); +} + void ObjectOperator::ToPropertyDescriptor(PropertyDescriptor &desc) const { DISALLOW_GARBAGE_COLLECTION; @@ -259,6 +308,7 @@ void ObjectOperator::GlobalLookupProperty() return; } JSTaggedValue proto = JSTaggedValue::GetPrototype(thread_, holder_); + RETURN_IF_ABRUPT_COMPLETION(thread_); if (!proto.IsHeapObject()) { return; } @@ -280,6 +330,7 @@ void ObjectOperator::LookupProperty() } JSTaggedValue proto = JSTaggedValue::GetPrototype(thread_, holder_); + RETURN_IF_ABRUPT_COMPLETION(thread_); if (!proto.IsHeapObject()) { return; } @@ -357,7 +408,7 @@ void ObjectOperator::LookupPropertyInlinedProps(const JSHandle &obj) ASSERT(entry == static_cast(attr.GetOffset())); JSTaggedValue value; if (attr.IsInlinedProps()) { - value = obj->GetPropertyInlinedProps(entry); + value = obj->GetPropertyInlinedPropsWithRep(entry, attr); if (value.IsHole()) { if (receiverHoleEntry_ == -1 && receiver_ == holder_) { receiverHoleEntry_ = entry; @@ -436,6 +487,38 @@ bool ObjectOperator::UpdateValueAndDetails(const JSHandle &receiver, c return UpdateDataValue(receiver, value, isInternalAccessor); } +JSTaggedValue ObjectOperator::ConvertOrTransitionWithRep(const JSHandle &receiver, + const JSHandle &value, PropertyAttributes &attr, bool &needBarrier) +{ + Representation oldRep = attr.GetRepresentation(); + if (oldRep == Representation::DOUBLE) { + if (value->IsInt()) { + double doubleValue = value->GetInt(); + needBarrier = false; + return JSTaggedValue(bit_cast(doubleValue)); + } else if (value->IsObject()) { + // Is Object + attributes_.SetRepresentation(Representation::TAGGED); + // Transtion + JSHClass::TransitionForRepChange(thread_, receiver, key_, attributes_); + } else { + // Is TaggedDouble + needBarrier = false; + return JSTaggedValue(bit_cast(value->GetDouble())); + } + } else if (oldRep == Representation::INT) { + if (value->IsInt()) { + int intValue = value->GetInt(); + needBarrier = false; + return JSTaggedValue(static_cast(intValue)); + } else { + attributes_.SetRepresentation(Representation::TAGGED); + JSHClass::TransitionForRepChange(thread_, receiver, key_, attributes_); + } + } + return value.GetTaggedValue(); +} + bool ObjectOperator::UpdateDataValue(const JSHandle &receiver, const JSHandle &value, bool isInternalAccessor, bool mayThrow) { @@ -446,9 +529,12 @@ bool ObjectOperator::UpdateDataValue(const JSHandle &receiver, const J JSArray::CheckAndCopyArray(thread_, JSHandle(receiver)); TaggedArray::Cast(JSHandle(receiver)->GetElements())->Set(thread_, GetIndex(), value.GetTaggedValue()); - return true; + } else { + elements->Set(thread_, GetIndex(), value.GetTaggedValue()); + } + if (JSHClass::TransitToElementsKind(thread_, receiver, value)) { + SetIsTransition(true); } - elements->Set(thread_, GetIndex(), value.GetTaggedValue()); return true; } @@ -476,12 +562,13 @@ bool ObjectOperator::UpdateDataValue(const JSHandle &receiver, const J bool res = accessor->CallInternalSet(thread_, JSHandle(receiver), value, mayThrow); if (receiver->GetJSHClass()->IsDictionaryMode()) { SetIsInlinedProps(false); + SetFastMode(false); } return res; } } - TaggedArray *properties = TaggedArray::Cast(receiver->GetProperties().GetTaggedObject()); + JSMutableHandle properties(thread_, TaggedArray::Cast(receiver->GetProperties().GetTaggedObject())); if (!properties->IsDictionaryMode()) { if (thread_->IsPGOProfilerEnable() && attributes_.UpdateTrackType(value.GetTaggedValue())) { LayoutInfo *layoutInfo = LayoutInfo::Cast(receiver->GetJSHClass()->GetLayout().GetTaggedObject()); @@ -492,21 +579,28 @@ bool ObjectOperator::UpdateDataValue(const JSHandle &receiver, const J } layoutInfo->SetNormalAttr(thread_, offset, attributes_); } + bool needBarrier = true; + JSTaggedValue actualValue = value.GetTaggedValue(); + if (IsTSHClass()) { + actualValue = ConvertOrTransitionWithRep(receiver, value, attributes_, needBarrier); + } PropertyAttributes attr = GetAttr(); if (attr.IsInlinedProps()) { - receiver->SetPropertyInlinedProps(thread_, GetIndex(), value.GetTaggedValue()); + receiver->SetPropertyInlinedPropsWithRep(thread_, GetIndex(), actualValue); } else { if (receiver.GetTaggedValue().IsJSCOWArray()) { JSArray::CheckAndCopyArray(thread_, JSHandle(receiver)); - TaggedArray::Cast(JSHandle(receiver)->GetProperties())->Set(thread_, - GetIndex(), value.GetTaggedValue()); - return true; + properties.Update(JSHandle(receiver)->GetProperties()); + } + if (needBarrier) { + properties->Set(thread_, GetIndex(), value.GetTaggedValue()); + } else { + properties->Set(thread_, GetIndex(), actualValue); } - properties->Set(thread_, GetIndex(), value.GetTaggedValue()); } } else { - NameDictionary::Cast(properties)->UpdateValue(thread_, GetIndex(), value.GetTaggedValue()); + properties.GetObject()->UpdateValue(thread_, GetIndex(), value.GetTaggedValue()); } return true; } @@ -604,7 +698,7 @@ void ObjectOperator::DeletePropertyInHolder() if (IsElement()) { return DeleteElementInHolder(); } - + ObjectOperator::UpdateDetector(thread_, holder_.GetTaggedValue(), key_.GetTaggedValue()); JSObject::DeletePropertyInternal(thread_, JSHandle(holder_), key_, GetIndex()); } @@ -632,7 +726,6 @@ void ObjectOperator::WriteElement(const JSHandle &receiver, JSTaggedVa TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject()); if (!elements->IsDictionaryMode()) { elements->Set(thread_, index_, value); - receiver->GetJSHClass()->UpdateRepresentation(value); return; } @@ -768,7 +861,15 @@ void ObjectOperator::AddPropertyInternal(const JSHandle &value) if (thread_->IsPGOProfilerEnable() && attr.UpdateTrackType(value.GetTaggedValue())) { layoutInfo->SetNormalAttr(thread_, receiverHoleEntry_, attr); } - JSObject::Cast(receiver_.GetTaggedValue())->SetProperty(thread_, hclass, attr, value.GetTaggedValue()); + bool needBarrier = true; + JSTaggedValue actualValue = ConvertOrTransitionWithRep(JSHandle(receiver_), value, attr, needBarrier); + if (needBarrier) { + JSObject::Cast(receiver_.GetTaggedValue())->SetProperty( + thread_, hclass, attr, value.GetTaggedValue()); + } else { + JSObject::Cast(receiver_.GetTaggedValue())->SetProperty( + thread_, hclass, attr, actualValue); + } uint32_t index = attr.IsInlinedProps() ? attr.GetOffset() : attr.GetOffset() - obj->GetJSHClass()->GetInlinedProperties(); SetIsTSHClass(true); diff --git a/ecmascript/object_operator.h b/ecmascript/object_operator.h index 72db82564a3b13a6c4641524ba357b7549249142..2b58e93b7827fcebbcf64c61e11deda02cd61172 100644 --- a/ecmascript/object_operator.h +++ b/ecmascript/object_operator.h @@ -19,6 +19,7 @@ #include "ecmascript/js_handle.h" #include "ecmascript/property_attributes.h" +#include "ecmascript/ecma_string.h" #include "libpandabase/utils/bit_field.h" namespace panda::ecmascript { @@ -59,6 +60,10 @@ public: static void FastAdd(JSThread *thread, const JSTaggedValue &receiver, const JSTaggedValue &name, const JSHandle &value, const PropertyAttributes &attr); + void UpdateDetector(); + static void UpdateDetector(const JSThread *thread, JSTaggedValue receiver, JSTaggedValue key); + static bool IsDetectorName(JSHandle env, JSTaggedValue key); + NO_COPY_SEMANTIC(ObjectOperator); DEFAULT_NOEXCEPT_MOVE_SEMANTIC(ObjectOperator); ~ObjectOperator() = default; @@ -90,6 +95,24 @@ public: return key_.IsEmpty(); } + inline bool GetThroughElement() const + { + uint32_t len = EcmaStringAccessor(holder_->GetTaggedObject()).GetLength(); + bool flag = elementIndex_ < len; + return key_.IsEmpty() && flag; + } + + inline bool GetStringLength() const + { + JSTaggedValue lenKey = thread_->GlobalConstants()->GetLengthString(); + if (GetKey()->IsUndefined() || !GetKey()->IsString()) { + return false; + } + EcmaString *proKey = EcmaString::Cast(GetKey()->GetTaggedObject()); + return receiver_->IsString() && EcmaStringAccessor::StringsAreEqual(proKey, + EcmaString::Cast(lenKey.GetTaggedObject())); + } + inline bool IsOnPrototype() const { return IsOnPrototypeField::Get(metaData_); @@ -180,6 +203,11 @@ public: attributes_.SetIsInlinedProps(flag); } + inline Representation GetRepresentation() const + { + return GetAttr().GetRepresentation(); + } + inline JSTaggedValue GetValue() const { if (value_.IsEmpty()) { @@ -252,6 +280,8 @@ public: { SetFound(NOT_FOUND_INDEX, JSTaggedValue::Undefined(), PropertyAttributes::GetDefaultAttributes(), false, false); } + JSTaggedValue ConvertOrTransitionWithRep(const JSHandle &receiver, + const JSHandle &value, PropertyAttributes &attr, bool &needBarrier); bool UpdateDataValue(const JSHandle &receiver, const JSHandle &value, bool isInternalAccessor, bool mayThrow = false); bool WriteDataPropertyInHolder(const PropertyDescriptor &desc) diff --git a/ecmascript/patch/patch_loader.cpp b/ecmascript/patch/patch_loader.cpp index c3a0ed193d1980f5d3b267054e213b302e66773b..22baca2f1819fcd6c0e0bec17c9928472f33a303 100644 --- a/ecmascript/patch/patch_loader.cpp +++ b/ecmascript/patch/patch_loader.cpp @@ -113,7 +113,7 @@ bool PatchLoader::ExecutePatchMain(JSThread *thread, const JSPandaFile *patchFil JSHandle module = thread->GetCurrentEcmaContext()->GetModuleManager()->HostResolveImportedModuleWithMerge( patchFile->GetJSPandaFileDesc(), recordName); - func->SetModule(thread, module); + Method::Cast(func->GetMethod())->SetModule(thread, module); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, JSHandle(func), undefined, undefined, 0); EcmaInterpreter::Execute(info); @@ -183,7 +183,7 @@ PatchErrorCode PatchLoader::UnloadPatchInternal(JSThread *thread, const CString baseFile.get(), baseMethodLiteral->GetMethodId()); ReplaceMethod(thread, patchMethod, baseMethodLiteral, baseConstpoolValue); LOG_ECMA(INFO) << "Replace base method: " - << patchMethod->GetRecordName() + << patchMethod->GetRecordNameStr() << ":" << patchMethod->GetMethodName(); } @@ -220,10 +220,13 @@ void PatchLoader::ReplaceMethod(JSThread *thread, MethodLiteral *srcMethodLiteral, JSTaggedValue srcConstpool) { + // Update destmethod exclude ExtraLiteralInfo(FunctionKind). Method FunctionKind will be set after + // building class inheritance relationship or defining gettersetter by value. + // + // HotReload of class inheritance will be affected. destMethod->SetCallField(srcMethodLiteral->GetCallField()); destMethod->SetLiteralInfo(srcMethodLiteral->GetLiteralInfo()); destMethod->SetCodeEntryOrLiteral(reinterpret_cast(srcMethodLiteral)); - destMethod->SetExtraLiteralInfo(srcMethodLiteral->GetExtraLiteralInfo()); destMethod->SetNativePointerOrBytecodeArray(const_cast(srcMethodLiteral->GetNativePointer())); destMethod->SetConstantPool(thread, srcConstpool); destMethod->SetProfileTypeInfo(thread, JSTaggedValue::Undefined()); diff --git a/ecmascript/patch/quick_fix_manager.cpp b/ecmascript/patch/quick_fix_manager.cpp index 9f3153563e322f11416f879fb28a7d2ed70fe8d8..52bd6427ddd73962e7963368ba4fd761970782ab 100644 --- a/ecmascript/patch/quick_fix_manager.cpp +++ b/ecmascript/patch/quick_fix_manager.cpp @@ -14,6 +14,7 @@ */ #include "ecmascript/patch/quick_fix_manager.h" +#include "ecmascript/global_env_constants-inl.h" #include "ecmascript/jspandafile/js_pandafile_manager.h" #include "ecmascript/mem/c_string.h" #include "ecmascript/napi/include/jsnapi.h" diff --git a/ecmascript/pgo_profiler/ap_file/pgo_file_info.cpp b/ecmascript/pgo_profiler/ap_file/pgo_file_info.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1ff2d80f2902ae40a34a10dc6cb9decd33dda11a --- /dev/null +++ b/ecmascript/pgo_profiler/ap_file/pgo_file_info.cpp @@ -0,0 +1,154 @@ +/* + * 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/pgo_profiler/ap_file/pgo_file_info.h" +#include "ecmascript/base/bit_helper.h" + +namespace panda::ecmascript::pgo { +using StringHelper = base::StringHelper; +bool PGOProfilerHeader::BuildFromLegacy(void *buffer, PGOProfilerHeader **header) +{ + auto *inHeader = reinterpret_cast(buffer); + size_t desSize = Size(inHeader->GetSectionNumber()); + if (desSize > LastSize()) { + LOG_ECMA(ERROR) << "header size error, expected size is less than " << LastSize() << ", but got " << desSize; + return false; + } + Build(header, desSize); + // copy header base. + if (memcpy_s(*header, sizeof(FileHeaderBase), inHeader, sizeof(FileHeaderBase)) != EOK) { + UNREACHABLE(); + } + // skip elastic header field, and copy section info from incoming buffer. + auto sectionSize = desSize - sizeof(FileHeaderElastic); + if (memcpy_s(&((*header)->sectionNumber_), sectionSize, &(inHeader->GetSectionNumber()), sectionSize) != EOK) { + UNREACHABLE(); + } + return true; +} + +bool PGOProfilerHeader::BuildFromElastic(void *buffer, size_t bufferSize, PGOProfilerHeader **header) +{ + auto *inHeader = reinterpret_cast(buffer); + if (!inHeader->Verify(buffer, bufferSize)) { + return false; + } + size_t desSize = inHeader->Size(); + if (desSize > LastSize()) { + LOG_ECMA(ERROR) << "header size error, expected size is less than " << LastSize() << ", but got " << desSize; + return false; + } + Build(header, desSize); + if (memcpy_s(*header, desSize, inHeader, desSize) != EOK) { + UNREACHABLE(); + } + return true; +} + +bool PGOProfilerHeader::ParseFromBinary(void *buffer, size_t bufferSize, PGOProfilerHeader **header) +{ + auto *inHeaderBase = reinterpret_cast(buffer); + if (inHeaderBase->VerifyVersion("apPath file", LAST_VERSION, false)) { + if (!inHeaderBase->CompatibleVerify(ELASTIC_HEADER_MINI_VERSION)) { + return BuildFromLegacy(buffer, header); + } + return BuildFromElastic(buffer, bufferSize, header); + } + return false; +} + +bool PGOProfilerHeader::VerifyFileSize(size_t bufferSize) const +{ + if (!SupportFileSize()) { + return true; + } + if (GetFileSize() != bufferSize) { + LOG_ECMA(ERROR) << "Verify ap file's file size failed. size: " << std::hex << bufferSize << " vs " + << GetFileSize(); + return false; + } + return true; +} + +bool PGOProfilerHeader::VerifyConsistency(void *buffer, size_t bufferSize) const +{ + if (!SupportFileConsistency()) { + return true; + } + uint32_t checksum = adler32(0, reinterpret_cast(buffer) + MAGIC_SIZE, VERSION_SIZE); + checksum = adler32(checksum, reinterpret_cast(buffer) + CHECKSUM_END_OFFSET, + bufferSize - CHECKSUM_END_OFFSET); + if (checksum != GetChecksum()) { + LOG_ECMA(ERROR) << "Verify ap file's consistency failed. checksum: " << std::hex << checksum << " vs " + << std::hex << GetChecksum(); + return false; + } + return true; +} + +void PGOProfilerHeader::ProcessToBinary(std::fstream &fileStream) const +{ + fileStream.seekp(0); + if (base::FileHeaderBase::CompatibleVerify(ELASTIC_HEADER_MINI_VERSION)) { + fileStream.write(reinterpret_cast(this), Size()); + } else { + // copy header base. + fileStream.write(reinterpret_cast(this), sizeof(FileHeaderBase)); + // skip elastic header field, and copy section info from incoming buffer. + auto sectionSize = Size() - sizeof(FileHeaderElastic); + fileStream.write(reinterpret_cast(§ionNumber_), sectionSize); + } +} + +bool PGOProfilerHeader::ParseFromText(std::ifstream &stream) +{ + std::string header; + if (std::getline(stream, header)) { + if (header.empty()) { + return false; + } + auto index = header.find(DumpUtils::BLOCK_START); + if (index == std::string::npos) { + return false; + } + auto version = header.substr(index + 1); + if (!InternalSetVersion(version)) { + return false; + } + if (!Verify()) { + return false; + } + if (!base::FileHeaderBase::CompatibleVerify(ELASTIC_HEADER_MINI_VERSION)) { + auto *pandaInfoSection = GetPandaInfoSection(); + pandaInfoSection->offset_ -= sizeof(PGOProfilerHeader) - sizeof(PGOProfilerHeaderLegacy); + } + return true; + } + return false; +} + +bool PGOProfilerHeader::ProcessToText(std::ofstream &stream) const +{ + if (!Verify()) { + return false; + } + stream << DumpUtils::VERSION_HEADER << InternalGetVersion() << DumpUtils::NEW_LINE; + if (SupportFileConsistency()) { + stream << "FileSize: " << GetFileSize() << " ,HeaderSize: " << GetHeaderSize() << " ,Checksum: " << std::hex + << GetChecksum() << DumpUtils::NEW_LINE; + } + return true; +} +} // namespace panda::ecmascript::pgo \ No newline at end of file diff --git a/ecmascript/pgo_profiler/ap_file/pgo_file_info.h b/ecmascript/pgo_profiler/ap_file/pgo_file_info.h new file mode 100644 index 0000000000000000000000000000000000000000..dc95e317211ce34cc769fbc036490fa59f5ad83f --- /dev/null +++ b/ecmascript/pgo_profiler/ap_file/pgo_file_info.h @@ -0,0 +1,359 @@ +/* + * 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_PGO_PROFILER_AP_FILE_BASE_INFO_H +#define ECMASCRIPT_PGO_PROFILER_AP_FILE_BASE_INFO_H + +#include + +#include "ecmascript/common.h" +#include "libpandafile/file.h" +#include "macros.h" + +#include "ecmascript/base/file_header.h" + +namespace panda::ecmascript::pgo { +class PGOProfilerHeader; + +struct SectionInfo { + uint32_t offset_ {0}; + // reserve + uint32_t size_ {0}; + uint32_t number_ {0}; +}; + +/** + |----PGOProfilerHeader + |--------MAGIC(8) + |------------{ 'P', 'A', 'N', 'D', 'A', '\0', '\0', '\0' } + |--------VERSION(4) + |------------{ '0', '0', '0', '0' } + |--------CHECKSUM(4) + |------------{ checksum } + |--------FILE_SIZE(4) + |------------{ fileSize } + |--------HEADER_SIZE(4) + |------------{ headerSize, from MAGIC to SECTION_NUMBER } + |--------ENDIAN_TAG(4) + |------------{ ENDIAN_TAG } + |--------SECTION_NUMBER(4) + |------------{ 4 } + |--------PANDA_FILE_INFO_SECTION_INFO(12) + |------------{ offset, size (reserve), number1 } + |--------RECORD_INFO_SECTION_INFO(12) + |------------{ offset, size (reserve), number2 } + |--------LAYOUT_DESC_SECTION_INFO(12) + |------------{ offset, size (reserve), number3 } + |--------RECORD_POOL(12) + |------------{ offset, size (reserve), recordPoolCount } + | + |----Section1: PGOPandaFileInfos(number1) + |--------[{ size, CHECK_SUM }, { size, CHECK_SUM }, ...] + | + |----Section2: PGORecordDetailInfos(number2) + |--------[ PGOMethodInfoMap(number4) + |------------{ recordId, offset, size, number4 } + |------------[ PGOMethodInfo(size1) + |----------------{ size1, entityId, count, mode, methodName, + |-------------------- [{ size, offset, profileTypeId }, { size, offset, profileTypeId }, ...]}, + |------------ PGOMethodInfo(size1) + |----------------{ size1, entityId, count, mode, methodName, + |-------------------- [{ size, offset, profileTypeId }, { size, offset, profileTypeId }, ...]}, + |--------------... ] + |-------- PGOMethodInfoMap() + |--------... ] + | + |----Section3: PGHClassLayoutDescs(number3) + |--------{ offset, size, number5 } + |--------[ PGOHClassLayoutDescInner(size) + |------------{ size, profileTypeId, superProfileTypeId, count, ptCount, ctorCount, + |---------------- [{ size, handle, key }, { size, heandle, key }, ...]} + |-------- PGOHClassLayoutDescInner(size) + |------------{ size, profileTypeId, superProfileTypeId, count, ptCount, ctorCount, + |---------------- [{ size, handle, key }, { size, heandle, key }, ...]} + | + |----Section4: PGORecord(recordPoolCount) + |--------{ offset, size, recordItemCount } + |--------[ recordId, recordName ](recordItemCount) + | + |----Section5: ProfileTypes(ProfileTypePoolCount) + |--------{ offset, size, profileTypeItemCount } + |--------[ profileTypeId, profileType(64bit) ](profileTypeItemCount) + */ +class PGOProfilerHeader : public base::FileHeaderElastic { +public: + static constexpr VersionType TYPE_MINI_VERSION = {0, 0, 0, 2}; + static constexpr VersionType METHOD_CHECKSUM_MINI_VERSION = {0, 0, 0, 4}; + static constexpr VersionType USE_HCLASS_TYPE_MINI_VERSION = {0, 0, 0, 5}; + static constexpr VersionType FILE_CONSISTENCY_MINI_VERSION = {0, 0, 0, 6}; + static constexpr VersionType TRACK_FIELD_MINI_VERSION = {0, 0, 0, 7}; + static constexpr VersionType ELEMENTS_KIND_MINI_VERSION = {0, 0, 0, 8}; + static constexpr VersionType RECORD_POOL_MINI_VERSION = {0, 0, 0, 9}; + static constexpr VersionType WIDE_CLASS_TYPE_MINI_VERSION = {0, 0, 0, 10}; + static constexpr VersionType PROFILE_TYPE_WITH_ABC_ID_MINI_VERSION = {0, 0, 0, 11}; + static constexpr VersionType FILE_SIZE_MINI_VERSION = FILE_CONSISTENCY_MINI_VERSION; + static constexpr VersionType HEADER_SIZE_MINI_VERSION = FILE_CONSISTENCY_MINI_VERSION; + static constexpr VersionType ELASTIC_HEADER_MINI_VERSION = FILE_CONSISTENCY_MINI_VERSION; + static constexpr VersionType LAST_VERSION = {0, 0, 0, 11}; + static constexpr size_t SECTION_SIZE = 6; + static constexpr size_t PANDA_FILE_SECTION_INDEX = 0; + static constexpr size_t RECORD_INFO_SECTION_INDEX = 1; + static constexpr size_t LAYOUT_DESC_SECTION_INDEX = 2; + static constexpr size_t RECORD_POOL_SECTION_INDEX = 3; + static constexpr size_t CLASS_TYPE_POOL_SECTION_INDEX = 4; + static constexpr size_t ABC_FILE_POOL_SECTION_INDEX = 5; + + PGOProfilerHeader() : base::FileHeaderElastic(LAST_VERSION), sectionNumber_(SECTION_SIZE) + { + GetPandaInfoSection()->offset_ = Size(); + SetHeaderSize(Size()); + } + + static size_t LastSize() + { + return sizeof(PGOProfilerHeader) + (SECTION_SIZE - 1) * sizeof(SectionInfo); + } + + static size_t Size(uint32_t sectionNumber) + { + return sizeof(PGOProfilerHeader) + (sectionNumber - 1) * sizeof(SectionInfo); + } + + size_t Size() const + { + return Size(sectionNumber_); + } + + bool Verify() const + { + return VerifyVersion("apPath file", LAST_VERSION, false); + } + + bool Verify(void *buffer, size_t bufferSize) const + { + if (!Verify()) { + return false; + } + if (!VerifyConsistency(buffer, bufferSize)) { + return false; + } + if (!VerifyFileSize(bufferSize)) { + return false; + } + return true; + } + + bool VerifyFileSize(size_t bufferSize) const; + + bool VerifyConsistency(void *buffer, size_t bufferSize) const; + + static void Build(PGOProfilerHeader **header, size_t size) + { + *header = reinterpret_cast(malloc(size)); + new (*header) PGOProfilerHeader(); + } + + static void Destroy(PGOProfilerHeader **header) + { + if (*header != nullptr) { + free(*header); + *header = nullptr; + } + } + + // Copy Header. + static bool ParseFromBinary(void *buffer, size_t bufferSize, PGOProfilerHeader **header); + void ProcessToBinary(std::fstream &fileStream) const; + + bool ParseFromText(std::ifstream &stream); + bool ProcessToText(std::ofstream &stream) const; + + SectionInfo *GetPandaInfoSection() const + { + return GetSectionInfo(PANDA_FILE_SECTION_INDEX); + } + + SectionInfo *GetRecordInfoSection() const + { + return GetSectionInfo(RECORD_INFO_SECTION_INDEX); + } + + SectionInfo *GetLayoutDescSection() const + { + return GetSectionInfo(LAYOUT_DESC_SECTION_INDEX); + } + + SectionInfo *GetRecordPoolSection() const + { + return GetSectionInfo(RECORD_POOL_SECTION_INDEX); + } + + SectionInfo *GetProfileTypeSection() const + { + return GetSectionInfo(CLASS_TYPE_POOL_SECTION_INDEX); + } + + SectionInfo *GetAbcFilePoolSection() const + { + return GetSectionInfo(ABC_FILE_POOL_SECTION_INDEX); + } + + bool SupportType() const + { + return CompatibleVerify(TYPE_MINI_VERSION); + } + + bool SupportMethodChecksum() const + { + return CompatibleVerify(METHOD_CHECKSUM_MINI_VERSION); + } + + bool SupportUseHClassType() const + { + return CompatibleVerify(USE_HCLASS_TYPE_MINI_VERSION); + } + + bool SupportFileConsistency() const + { + return CompatibleVerify(FILE_CONSISTENCY_MINI_VERSION); + } + + bool SupportFileSize() const + { + return CompatibleVerify(FILE_SIZE_MINI_VERSION); + } + + bool SupportHeaderSize() const + { + return CompatibleVerify(HEADER_SIZE_MINI_VERSION); + } + + bool SupportTrackField() const + { + return CompatibleVerify(TRACK_FIELD_MINI_VERSION); + } + + bool SupportElementsKind() const + { + return CompatibleVerify(ELEMENTS_KIND_MINI_VERSION); + } + + bool SupportRecordPool() const + { + return CompatibleVerify(RECORD_POOL_MINI_VERSION); + } + + bool SupportWideProfileType() const + { + return CompatibleVerify(WIDE_CLASS_TYPE_MINI_VERSION); + } + + bool SupportProfileTypeWithAbcId() const + { + return CompatibleVerify(PROFILE_TYPE_WITH_ABC_ID_MINI_VERSION); + } + + NO_COPY_SEMANTIC(PGOProfilerHeader); + NO_MOVE_SEMANTIC(PGOProfilerHeader); + +private: + static bool BuildFromLegacy(void *buffer, PGOProfilerHeader **header); + static bool BuildFromElastic(void *buffer, size_t bufferSize, PGOProfilerHeader **header); + + SectionInfo *GetSectionInfo(size_t index) const + { + if (index >= sectionNumber_) { + return nullptr; + } + return const_cast(§ionInfos_) + index; + } + + uint32_t sectionNumber_ {SECTION_SIZE}; + SectionInfo sectionInfos_; +}; + +class PGOProfilerHeaderLegacy : public base::FileHeaderBase { +public: + static constexpr size_t SECTION_SIZE = 3; + static constexpr VersionType LAST_VERSION = {0, 0, 0, 5}; + PGOProfilerHeaderLegacy() : base::FileHeaderBase(LAST_VERSION), sectionNumber_(SECTION_SIZE) {}; + + const uint32_t &GetSectionNumber() const + { + return sectionNumber_; + } + +private: + uint32_t sectionNumber_ {SECTION_SIZE}; + SectionInfo sectionInfos_; +}; + +class PGOFileDataInterface { +public: + PGOFileDataInterface() = default; + virtual ~PGOFileDataInterface() = default; + virtual uint32_t ProcessToBinary(std::fstream &stream) = 0; + virtual uint32_t ParseFromBinary(void **buffer, PGOProfilerHeader const *header) = 0; + virtual bool ProcessToText(std::ofstream &stream) = 0; + // not support yet + virtual bool ParseFromText([[maybe_unused]] std::ifstream &stream) + { + return true; + }; + +private: + NO_COPY_SEMANTIC(PGOFileDataInterface); + NO_MOVE_SEMANTIC(PGOFileDataInterface); +}; + +class PGOFileSectionInterface : public PGOFileDataInterface { +public: + PGOFileSectionInterface() = default; + ~PGOFileSectionInterface() override = default; + virtual bool Support(PGOProfilerHeader const *header) const = 0; + virtual SectionInfo *GetSection(PGOProfilerHeader const *header) const = 0; + + static bool ParseSectionFromBinary(void *buffer, PGOProfilerHeader const *header, PGOFileSectionInterface §ion) + { + if (section.Support(header)) { + SectionInfo *info = section.GetSection(header); + if (info == nullptr) { + return false; + } + void *addr = reinterpret_cast(reinterpret_cast(buffer) + info->offset_); + section.ParseFromBinary(&addr, header); + } + return true; + } + + static bool ProcessSectionToBinary(std::fstream &fileStream, PGOProfilerHeader *const header, + PGOFileSectionInterface §ion) + { + auto *info = section.GetSection(header); + if (info == nullptr) { + return false; + } + info->offset_ = static_cast(fileStream.tellp()); + info->number_ = section.ProcessToBinary(fileStream); + info->size_ = static_cast(fileStream.tellp()) - info->offset_; + return true; + } + +private: + NO_COPY_SEMANTIC(PGOFileSectionInterface); + NO_MOVE_SEMANTIC(PGOFileSectionInterface); +}; +} // namespace panda::ecmascript::pgo +#endif // ECMASCRIPT_PGO_PROFILER_AP_FILE_BASE_INFO_H \ No newline at end of file diff --git a/ecmascript/pgo_profiler/ap_file/pgo_method_type_set.cpp b/ecmascript/pgo_profiler/ap_file/pgo_method_type_set.cpp new file mode 100644 index 0000000000000000000000000000000000000000..39ee3463e7748618f30276bc59634766dc5f0c25 --- /dev/null +++ b/ecmascript/pgo_profiler/ap_file/pgo_method_type_set.cpp @@ -0,0 +1,187 @@ +/* + * 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/pgo_profiler/ap_file/pgo_method_type_set.h" +#include "ecmascript/base/bit_helper.h" +#include "ecmascript/common.h" +#include "ecmascript/log_wrapper.h" +#include "ecmascript/pgo_profiler/ap_file/pgo_file_info.h" +#include "ecmascript/pgo_profiler/pgo_context.h" +#include "ecmascript/pgo_profiler/pgo_utils.h" + +namespace panda::ecmascript::pgo { +using StringHelper = base::StringHelper; +void PGOMethodTypeSet::Merge(const PGOMethodTypeSet *info) +{ + for (const auto &fromType : info->scalarOpTypeInfos_) { + auto iter = scalarOpTypeInfos_.find(fromType); + if (iter != scalarOpTypeInfos_.end()) { + const_cast(*iter).Merge(fromType); + } else { + scalarOpTypeInfos_.emplace(fromType); + } + } + for (const auto &fromType : info->rwScalarOpTypeInfos_) { + auto iter = rwScalarOpTypeInfos_.find(fromType); + if (iter != rwScalarOpTypeInfos_.end()) { + const_cast(*iter).Merge(fromType); + } else { + rwScalarOpTypeInfos_.emplace(fromType); + } + } + for (const auto &fromType : info->objDefOpTypeInfos_) { + AddDefine(fromType.GetOffset(), fromType.GetType(), fromType.GetSuperType()); + } +} + +void PGOMethodTypeSet::SkipFromBinary(void **buffer) +{ + uint32_t size = base::ReadBuffer(buffer, sizeof(uint32_t)); + for (uint32_t i = 0; i < size; i++) { + base::ReadBufferInSize(buffer); + } +} + +bool PGOMethodTypeSet::ParseFromBinary(PGOContext &context, void **buffer) +{ + PGOProfilerHeader *const header = context.GetHeader(); + ASSERT(header != nullptr); + uint32_t size = base::ReadBuffer(buffer, sizeof(uint32_t)); + for (uint32_t i = 0; i < size; i++) { + auto typeInfo = base::ReadBufferInSize(buffer); + if (typeInfo->GetInfoType() == InfoType::OP_TYPE) { + auto *scalerInfo = reinterpret_cast(typeInfo); + scalarOpTypeInfos_.emplace(scalerInfo->GetOffset(), + PGOSampleType::ConvertFrom(context, scalerInfo->GetType())); + } else if (typeInfo->GetInfoType() == InfoType::DEFINE_CLASS_TYPE) { + auto *defineInfo = reinterpret_cast(typeInfo); + ObjDefOpTypeInfo info(defineInfo->GetOffset(), + PGOSampleType::ConvertFrom(context, defineInfo->GetType()), + PGOSampleType::ConvertFrom(context, defineInfo->GetSuperType())); + objDefOpTypeInfos_.emplace(info); + } else if (header->SupportUseHClassType() && typeInfo->GetInfoType() == InfoType::USE_HCLASS_TYPE) { + auto *opTypeInfo = reinterpret_cast(typeInfo); + RWScalarOpTypeInfo info(opTypeInfo->GetOffset()); + info.ConvertFrom(context, *opTypeInfo); + rwScalarOpTypeInfos_.emplace(info); + } + } + return true; +} + +bool PGOMethodTypeSet::ProcessToBinary(PGOContext &context, std::stringstream &stream) const +{ + uint32_t number = 0; + std::stringstream methodStream; + for (auto typeInfo : scalarOpTypeInfos_) { + if (!typeInfo.GetType().IsNone()) { + PGOSampleTypeRef sampleTypeRef = PGOSampleTypeRef::ConvertFrom(context, typeInfo.GetType()); + ScalarOpTypeInfoRef infoRef(typeInfo.GetOffset(), sampleTypeRef); + methodStream.write(reinterpret_cast(&infoRef), infoRef.Size()); + number++; + } + } + for (auto typeInfo : rwScalarOpTypeInfos_) { + if (typeInfo.GetCount() != 0) { + RWScalarOpTypeInfoRef infoRef(typeInfo.GetOffset()); + infoRef.ConvertFrom(context, typeInfo); + methodStream.write(reinterpret_cast(&infoRef), infoRef.Size()); + number++; + } + } + + for (const auto &typeInfo : objDefOpTypeInfos_) { + ObjDefOpTypeInfoRef infoRef(typeInfo.GetOffset(), + PGOSampleTypeRef::ConvertFrom(context, typeInfo.GetType()), + PGOSampleTypeRef::ConvertFrom(context, typeInfo.GetSuperType())); + methodStream.write(reinterpret_cast(&infoRef), infoRef.Size()); + number++; + } + + stream.write(reinterpret_cast(&number), sizeof(uint32_t)); + if (number > 0) { + stream << methodStream.rdbuf(); + return true; + } + return false; +} + +bool PGOMethodTypeSet::ParseFromText(const std::string &typeString) +{ + std::vector typeInfoVector = StringHelper::SplitString(typeString, DumpUtils::TYPE_SEPARATOR); + if (typeInfoVector.size() > 0) { + for (const auto &iter : typeInfoVector) { + std::vector typeStrings = StringHelper::SplitString(iter, DumpUtils::BLOCK_START); + if (typeStrings.size() < METHOD_TYPE_INFO_COUNT) { + return false; + } + + uint32_t offset = 0; + if (!StringHelper::StrToUInt32(typeStrings[METHOD_OFFSET_INDEX].c_str(), &offset)) { + return false; + } + uint32_t type = 0; + if (!StringHelper::StrToUInt32(typeStrings[METHOD_TYPE_INDEX].c_str(), &type)) { + return false; + } + scalarOpTypeInfos_.emplace(offset, PGOSampleType(type)); + } + } + return true; +} + +void PGOMethodTypeSet::ProcessToText(std::string &text) const +{ + bool isFirst = true; + for (auto typeInfoIter : scalarOpTypeInfos_) { + if (typeInfoIter.GetType().IsNone()) { + continue; + } + if (isFirst) { + text += DumpUtils::ARRAY_START + DumpUtils::SPACE; + isFirst = false; + } else { + text += DumpUtils::TYPE_SEPARATOR + DumpUtils::SPACE; + } + text += std::to_string(typeInfoIter.GetOffset()); + text += DumpUtils::BLOCK_START; + text += typeInfoIter.GetType().GetTypeString(); + } + for (auto rwScalarOpTypeInfoIter : rwScalarOpTypeInfos_) { + if (rwScalarOpTypeInfoIter.GetCount() == 0) { + continue; + } + if (isFirst) { + text += DumpUtils::ARRAY_START + DumpUtils::SPACE; + isFirst = false; + } else { + text += DumpUtils::TYPE_SEPARATOR + DumpUtils::SPACE; + } + rwScalarOpTypeInfoIter.ProcessToText(text); + } + for (const auto &defTypeInfoIter : objDefOpTypeInfos_) { + if (isFirst) { + text += DumpUtils::ARRAY_START + DumpUtils::SPACE; + isFirst = false; + } else { + text += DumpUtils::TYPE_SEPARATOR + DumpUtils::SPACE; + } + defTypeInfoIter.ProcessToText(text); + } + if (!isFirst) { + text += (DumpUtils::SPACE + DumpUtils::ARRAY_END); + } +} +} // namespace panda::ecmascript::pgo \ No newline at end of file diff --git a/ecmascript/pgo_profiler/ap_file/pgo_method_type_set.h b/ecmascript/pgo_profiler/ap_file/pgo_method_type_set.h new file mode 100644 index 0000000000000000000000000000000000000000..1354c14d503507b4b9ddee2ddb3ce34747607ac7 --- /dev/null +++ b/ecmascript/pgo_profiler/ap_file/pgo_method_type_set.h @@ -0,0 +1,288 @@ +/* + * 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_PGO_PROFILER_AP_FILE_PGO_METHOD_TYPE_SET_H +#define ECMASCRIPT_PGO_PROFILER_AP_FILE_PGO_METHOD_TYPE_SET_H + +#include + +#include "ecmascript/pgo_profiler/ap_file/pgo_file_info.h" +#include "ecmascript/pgo_profiler/pgo_utils.h" +#include "ecmascript/pgo_profiler/types/pgo_profiler_type.h" + +namespace panda::ecmascript::pgo { +class PGOMethodTypeSet { +public: + static constexpr int METHOD_TYPE_INFO_INDEX = 4; + static constexpr int METHOD_TYPE_INFO_COUNT = 2; + static constexpr int METHOD_OFFSET_INDEX = 0; + static constexpr int METHOD_TYPE_INDEX = 1; + + PGOMethodTypeSet() = default; + + void AddType(uint32_t offset, PGOSampleType type) + { + auto result = scalarOpTypeInfos_.find(ScalarOpTypeInfo(offset, type)); + if (result != scalarOpTypeInfos_.end()) { + auto combineType = result->GetType().CombineType(type); + const_cast(*result).SetType(combineType); + } else { + scalarOpTypeInfos_.emplace(offset, type); + } + } + + void AddCallTargetType(uint32_t offset, PGOSampleType type) + { + auto result = scalarOpTypeInfos_.find(ScalarOpTypeInfo(offset, type)); + if (result != scalarOpTypeInfos_.end()) { + auto combineType = result->GetType().CombineCallTargetType(type); + const_cast(*result).SetType(combineType); + } else { + scalarOpTypeInfos_.emplace(offset, type); + } + } + + void AddObjectInfo(uint32_t offset, const PGOObjectInfo &info) + { + auto result = rwScalarOpTypeInfos_.find(RWScalarOpTypeInfo(offset)); + if (result != rwScalarOpTypeInfos_.end()) { + const_cast(*result).AddObjectInfo(info); + } else { + rwScalarOpTypeInfos_.emplace(offset, info); + } + } + + void AddDefine(uint32_t offset, PGOSampleType type, PGOSampleType superType) + { + auto result = objDefOpTypeInfos_.find(ObjDefOpTypeInfo(offset, type, superType)); + if (result != objDefOpTypeInfos_.end()) { + return; + } + objDefOpTypeInfos_.emplace(offset, type, superType); + } + + template + void GetTypeInfo(Callback callback) + { + for (const auto &typeInfo : scalarOpTypeInfos_) { + auto type = typeInfo.GetType(); + callback(typeInfo.GetOffset(), &type); + } + for (const auto &typeInfo : rwScalarOpTypeInfos_) { + auto type = typeInfo.GetType(); + callback(typeInfo.GetOffset(), &type); + } + for (const auto &typeInfo : objDefOpTypeInfos_) { + auto type = typeInfo.GetType(); + callback(typeInfo.GetOffset(), &type); + } + } + + void Merge(const PGOMethodTypeSet *info); + static void SkipFromBinary(void **buffer); + + bool ParseFromBinary(PGOContext &context, void **buffer); + bool ProcessToBinary(PGOContext &context, std::stringstream &stream) const; + + bool ParseFromText(const std::string &typeString); + void ProcessToText(std::string &text) const; + + NO_COPY_SEMANTIC(PGOMethodTypeSet); + NO_MOVE_SEMANTIC(PGOMethodTypeSet); + +private: + enum class InfoType : uint8_t { + NONE, + OP_TYPE, + DEFINE_CLASS_TYPE = 3, + USE_HCLASS_TYPE + }; + + class TypeInfoHeader { + public: + TypeInfoHeader(InfoType type, uint32_t offset) : infoType_(type), offset_(offset) {} + TypeInfoHeader(uint32_t size, InfoType type, uint32_t offset) + : size_(size), infoType_(type), offset_(offset) {} + + InfoType GetInfoType() + { + return infoType_; + } + + int32_t Size() const + { + return size_; + } + + uint32_t GetOffset() const + { + return offset_; + } + + protected: + uint32_t size_ {0}; + InfoType infoType_ {InfoType::NONE}; + uint32_t offset_ {0}; + }; + + template + class RWScalarOpTemplate : public TypeInfoHeader { + public: + explicit RWScalarOpTemplate(uint32_t offset) : TypeInfoHeader(InfoType::USE_HCLASS_TYPE, offset) {}; + RWScalarOpTemplate(uint32_t offset, ObjectInfoType info) + : TypeInfoHeader(sizeof(RWScalarOpTemplate), InfoType::USE_HCLASS_TYPE, offset) + { + type_.AddObjectInfo(info); + } + + template + void ConvertFrom(PGOContext &context, const FromType &from) + { + size_ = sizeof(RWScalarOpTemplate); + type_.ConvertFrom(context, from.GetType()); + } + + bool operator<(const RWScalarOpTemplate &right) const + { + return offset_ < right.offset_; + } + + int32_t GetCount() + { + return type_.GetCount(); + } + + void Merge(const RWScalarOpTemplate &type) + { + type_.Merge(type.type_); + } + + void AddObjectInfo(const ObjectInfoType &info) + { + type_.AddObjectInfo(info); + } + + const RWOpType &GetType() const + { + return type_; + } + + void ProcessToText(std::string &text) const + { + text += std::to_string(GetOffset()); + text += DumpUtils::BLOCK_START; + text += DumpUtils::ARRAY_START + DumpUtils::SPACE; + bool isFirst = true; + for (uint32_t i = 0; i < type_.GetCount(); i++) { + if (!isFirst) { + text += DumpUtils::TYPE_SEPARATOR + DumpUtils::SPACE; + } + isFirst = false; + text += type_.GetObjectInfo(i).GetInfoString(); + } + text += (DumpUtils::SPACE + DumpUtils::ARRAY_END); + } + + private: + RWOpType type_; + }; + using RWScalarOpTypeInfo = RWScalarOpTemplate; + using RWScalarOpTypeInfoRef = RWScalarOpTemplate; + + template + class ScalarOpTemplate : public TypeInfoHeader { + public: + ScalarOpTemplate(uint32_t offset, SampleType type) + : TypeInfoHeader(sizeof(ScalarOpTemplate), InfoType::OP_TYPE, offset), type_(type) {} + + bool operator<(const ScalarOpTemplate &right) const + { + return offset_ < right.offset_; + } + + void SetType(SampleType type) + { + if (type_ != type) { + type_ = type; + } + } + + void Merge(const ScalarOpTemplate &typeInfo) + { + SampleType combineType = GetType().CombineType(typeInfo.GetType()); + SetType(combineType); + } + + SampleType GetType() const + { + return type_; + } + + protected: + ScalarOpTemplate(uint32_t size, InfoType infoType, uint32_t offset, SampleType type) + : TypeInfoHeader(size, infoType, offset), type_(type) {} + + private: + SampleType type_; + }; + using ScalarOpTypeInfo = ScalarOpTemplate; + using ScalarOpTypeInfoRef = ScalarOpTemplate; + + template + class ObjDefOpTemplate : public ScalarOpTypeInfoType { + public: + ObjDefOpTemplate(uint32_t offset, SampleType type, SampleType superType) + : ScalarOpTypeInfoType(sizeof(ObjDefOpTemplate), InfoType::DEFINE_CLASS_TYPE, offset, type), + superType_(superType) {} + + SampleType GetSuperType() const + { + return superType_; + } + + bool operator<(const ObjDefOpTemplate &right) const + { + return this->offset_ < right.GetOffset() || this->GetType() < right.GetType() || + superType_ < right.superType_; + } + + void ProcessToText(std::string &text) const + { + text += std::to_string(this->GetOffset()); + text += DumpUtils::BLOCK_START; + text += DumpUtils::ARRAY_START + DumpUtils::SPACE; + text += this->GetType().GetTypeString(); + text += DumpUtils::TYPE_SEPARATOR; + text += GetSuperType().GetTypeString(); + text += (DumpUtils::SPACE + DumpUtils::ARRAY_END); + } + + protected: + ObjDefOpTemplate( + uint32_t size, InfoType infoType, uint32_t offset, SampleType type, SampleType superType) + : ScalarOpTypeInfoType(size, infoType, offset, type), superType_(superType) {} + + private: + SampleType superType_; + }; + using ObjDefOpTypeInfo = ObjDefOpTemplate; + using ObjDefOpTypeInfoRef = ObjDefOpTemplate; + + std::set scalarOpTypeInfos_; + std::set rwScalarOpTypeInfos_; + std::set objDefOpTypeInfos_; +}; +} // namespace panda::ecmascript::pgo +#endif // ECMASCRIPT_PGO_PROFILER_AP_FILE_PGO_METHOD_TYPE_SET_H \ No newline at end of file diff --git a/ecmascript/pgo_profiler/ap_file/pgo_profile_type_pool.h b/ecmascript/pgo_profiler/ap_file/pgo_profile_type_pool.h new file mode 100644 index 0000000000000000000000000000000000000000..dab2c52984cdf28dbd75ac5d7a3880f18ca01db7 --- /dev/null +++ b/ecmascript/pgo_profiler/ap_file/pgo_profile_type_pool.h @@ -0,0 +1,169 @@ +/* + * 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_PGO_PROFILER_AP_FILE_PGO_PROFILE_TYPE_POOL_H +#define ECMASCRIPT_PGO_PROFILER_AP_FILE_PGO_PROFILE_TYPE_POOL_H + +#include +#include +#include +#include +#include + +#include "ecmascript/common.h" +#include "ecmascript/log.h" +#include "ecmascript/log_wrapper.h" +#include "ecmascript/mem/c_string.h" +#include "ecmascript/mem/mem.h" +#include "ecmascript/pgo_profiler/ap_file/pgo_file_info.h" +#include "ecmascript/pgo_profiler/ap_file/pool_template.h" +#include "ecmascript/pgo_profiler/pgo_context.h" +#include "ecmascript/pgo_profiler/pgo_utils.h" +#include "ecmascript/pgo_profiler/types/pgo_profile_type.h" +#include "ecmascript/pgo_profiler/types/pgo_profiler_type.h" +#include "macros.h" + +namespace panda::ecmascript::pgo { +class PGOProfileTypePool { +public: + class Entry : public PGOFileDataInterface { + public: + explicit Entry(ProfileType type) : type_(type) {} + Entry() = default; + ApEntityId GetEntryId() const + { + return entryId_; + } + + void SetEntryId(ApEntityId entryId) + { + entryId_ = entryId; + } + + ProfileType GetProfileType() const + { + return type_; + } + + const ProfileType &GetData() const + { + return type_; + } + + uint32_t ProcessToBinary(std::fstream &stream) override + { + auto profileType = type_.GetRaw(); + stream.write(reinterpret_cast(&(profileType)), sizeof(ProfileType)); + return 1; + } + + uint32_t ParseFromBinary(void **buffer, [[maybe_unused]] PGOProfilerHeader const *header) override + { + type_ = base::ReadBuffer(buffer, sizeof(ProfileType)); + return 1; + } + + bool ProcessToText(std::ofstream &stream) override + { + stream << type_.GetTypeString(); + return true; + } + + private: + ApEntityId entryId_ {0}; + ProfileType type_; + }; + + using PoolType = PoolTemplate; + + enum class ReservedType : uint8_t { NONE_CLASS_TYPE_REF = 0, END }; + static constexpr uint32_t RESERVED_COUNT = 64; + + static_assert(static_cast(ReservedType::END) < RESERVED_COUNT); + + static bool IsReserved(const ProfileType &value) + { + return value.IsNone(); + } + + static ApEntityId GetReservedId([[maybe_unused]] const ProfileType &value) + { + ASSERT(value.IsNone()); + return ApEntityId(static_cast(ReservedType::NONE_CLASS_TYPE_REF)); + } + + static bool Support(PGOProfilerHeader const *header) + { + return header->SupportWideProfileType(); + } + + static SectionInfo *GetSection(PGOProfilerHeader const *header) + { + return header->GetProfileTypeSection(); + } + + PGOProfileTypePool() + { + pool_ = std::make_shared("ProfileTypePool", RESERVED_COUNT); + pool_->SetIsReservedCb(IsReserved); + pool_->SetGetReservedIdCb(GetReservedId); + pool_->SetGetSectionCb(GetSection); + pool_->SetSupportCb(Support); + } + ~PGOProfileTypePool() + { + Clear(); + } + + bool TryAdd(const ProfileType &profileType, ApEntityId &entryId) + { + return pool_->TryAdd(profileType, entryId); + } + + bool GetEntryId(const ProfileType &profileType, ApEntityId &entryId) const + { + return pool_->GetEntryId(profileType, entryId); + } + + const Entry *GetEntry(ApEntityId entryId) const + { + return pool_->GetEntry(entryId); + } + + void Clear() + { + pool_->Clear(); + } + + void Merge(const PGOProfileTypePool &profileTypePool, std::map &idMapping) + { + pool_->Merge(*profileTypePool.pool_, [&](ApEntityId oldEntryId, ApEntityId newEntryId) { + idMapping.try_emplace(oldEntryId, newEntryId); + }); + } + + std::shared_ptr &GetPool() + { + return pool_; + } + +private: + NO_COPY_SEMANTIC(PGOProfileTypePool); + NO_MOVE_SEMANTIC(PGOProfileTypePool); + + std::shared_ptr pool_; +}; +} // namespace panda::ecmascript::pgo +#endif // ECMASCRIPT_PGO_PROFILER_AP_FILE_PGO_PROFILE_TYPE_POOL_H diff --git a/ecmascript/pgo_profiler/ap_file/pgo_record_pool.h b/ecmascript/pgo_profiler/ap_file/pgo_record_pool.h new file mode 100644 index 0000000000000000000000000000000000000000..15b6d8bbdd974cfcb38adca8b37d26121bc04ef5 --- /dev/null +++ b/ecmascript/pgo_profiler/ap_file/pgo_record_pool.h @@ -0,0 +1,199 @@ +/* + * 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_PGO_PROFILER_AP_FILE_PGO_RECORD_POOL_H +#define ECMASCRIPT_PGO_PROFILER_AP_FILE_PGO_RECORD_POOL_H + +#include +#include +#include +#include + +#include "ecmascript/common.h" +#include "ecmascript/log_wrapper.h" +#include "ecmascript/mem/c_string.h" +#include "ecmascript/pgo_profiler/ap_file/pgo_file_info.h" +#include "ecmascript/pgo_profiler/ap_file/pool_template.h" +#include "ecmascript/pgo_profiler/pgo_utils.h" +#include "macros.h" + +namespace panda::ecmascript::pgo { +class PGOProfilerHeader; + +class PGOStringPool { +public: + class Entry : public PGOFileDataInterface { + public: + explicit Entry(CString name) : name_(std::move(name)) {} + Entry() = default; + ApEntityId GetEntryId() const + { + return entryId_; + } + + void SetEntryId(ApEntityId entryId) + { + entryId_ = entryId; + } + + const CString &GetData() const + { + return name_; + } + + uint32_t ProcessToBinary(std::fstream &stream) override + { + stream << name_ << '\0'; + return 1; + } + + uint32_t ParseFromBinary(void **buffer, [[maybe_unused]] PGOProfilerHeader const *header) override + { + name_ = base::ReadBuffer(buffer); + return 1; + } + + bool ProcessToText(std::ofstream &stream) override + { + stream << name_; + return true; + } + + private: + ApEntityId entryId_ {}; + CString name_; + }; + using PoolType = PoolTemplate; + + PGOStringPool(const std::string &poolName, uint32_t reservedCount) + { + pool_ = std::make_shared(poolName, reservedCount); + }; + ~PGOStringPool() + { + Clear(); + } + + bool TryAdd(const CString &value, ApEntityId &entryId) + { + return pool_->TryAdd(value, entryId); + } + + bool GetEntryId(const CString &value, ApEntityId &entryId) const + { + return pool_->GetEntryId(value, entryId); + } + + const Entry *GetEntry(ApEntityId entryId) const + { + return pool_->GetEntry(entryId); + } + + void Clear() + { + pool_->Clear(); + } + + std::shared_ptr &GetPool() + { + return pool_; + } + +protected: + NO_COPY_SEMANTIC(PGOStringPool); + NO_MOVE_SEMANTIC(PGOStringPool); + std::shared_ptr pool_; +}; + +class PGORecordPool : public PGOStringPool { +public: + enum class ReservedType : uint8_t { EMPTY_RECORD_ID = 0, END }; + static constexpr uint32_t RESERVED_COUNT = 64; + static_assert(static_cast(ReservedType::END) < RESERVED_COUNT); + + static bool IsReserved(const CString &value) + { + return value.empty(); + } + + static ApEntityId GetReservedId([[maybe_unused]] const CString &value) + { + ASSERT(value.empty()); + return ApEntityId(static_cast(ReservedType::EMPTY_RECORD_ID)); + } + + static bool Support(PGOProfilerHeader const *header) + { + return header->SupportRecordPool(); + } + + static SectionInfo *GetSection(PGOProfilerHeader const *header) + { + return header->GetRecordPoolSection(); + } + + PGORecordPool() : PGOStringPool("RecordPool", RESERVED_COUNT) + { + pool_->SetIsReservedCb(IsReserved); + pool_->SetGetReservedIdCb(GetReservedId); + pool_->SetGetSectionCb(GetSection); + pool_->SetSupportCb(Support); + } + + void Merge(const PGORecordPool &recordPool, std::map &idMapping) + { + pool_->Merge(*recordPool.pool_, + [&](ApEntityId oldId, ApEntityId newId) { idMapping.try_emplace(oldId, newId); }); + } +}; + +class PGOAbcFilePool : public PGOStringPool { +public: + enum class ReservedType : uint8_t { EMPTY_ABC_FILE_ID = 0, END }; + static constexpr uint32_t RESERVED_COUNT = 64; + static_assert(static_cast(ReservedType::END) < RESERVED_COUNT); + + static bool IsReserved(const CString &value) + { + return value.empty(); + } + + static ApEntityId GetReservedId([[maybe_unused]] const CString &value) + { + ASSERT(value.empty()); + return ApEntityId(static_cast(ReservedType::EMPTY_ABC_FILE_ID)); + } + + static bool Support(PGOProfilerHeader const *header) + { + return header->SupportProfileTypeWithAbcId(); + } + + static SectionInfo *GetSection(PGOProfilerHeader const *header) + { + return header->GetAbcFilePoolSection(); + } + + PGOAbcFilePool() : PGOStringPool("AbcFilePool", RESERVED_COUNT) + { + pool_->SetIsReservedCb(IsReserved); + pool_->SetGetReservedIdCb(GetReservedId); + pool_->SetGetSectionCb(GetSection); + pool_->SetSupportCb(Support); + } +}; + +} // namespace panda::ecmascript::pgo +#endif // ECMASCRIPT_PGO_PROFILER_AP_FILE_PGO_RECORD_POOL_H diff --git a/ecmascript/pgo_profiler/ap_file/pool_template.h b/ecmascript/pgo_profiler/ap_file/pool_template.h new file mode 100644 index 0000000000000000000000000000000000000000..af8b10aca91e3e6d82200b7d870502fde43d19cb --- /dev/null +++ b/ecmascript/pgo_profiler/ap_file/pool_template.h @@ -0,0 +1,225 @@ +/* + * 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_PGO_PROFILER_AP_FILE_POOL_TEMPLATE_H +#define ECMASCRIPT_PGO_PROFILER_AP_FILE_POOL_TEMPLATE_H + +#include +#include +#include +#include + +#include "ecmascript/common.h" +#include "ecmascript/log_wrapper.h" +#include "ecmascript/mem/c_string.h" +#include "ecmascript/pgo_profiler/ap_file/pgo_file_info.h" +#include "ecmascript/pgo_profiler/pgo_utils.h" +#include "macros.h" + +namespace panda::ecmascript::pgo { +template +class PoolTemplate : public PGOFileSectionInterface { +public: + using IsReservedCb = std::function; + using GetReservedIdCb = std::function; + + using SupportCb = std::function; + using GetSectionCb = std::function; + + PoolTemplate(std::string poolName, uint32_t reservedCount) + : poolName_(std::move(poolName)), RESERVED_COUNT(reservedCount) {}; + + ~PoolTemplate() override + { + Clear(); + } + + bool TryAdd(const V &value, ApEntityId &entryId) + { + for (auto &entry : pool_) { + if (entry.second.GetData() == value) { + entryId = entry.second.GetEntryId(); + return true; + } + } + + entryId = ApEntityId(IsReserved(value) ? (++reservedUsed_, GetReservedId(value)) + : RESERVED_COUNT + pool_.size() - reservedUsed_); + + auto result = pool_.emplace(entryId, value); + auto &entry = result.first->second; + entry.SetEntryId(entryId); + return true; + } + + bool GetEntryId(const V &value, ApEntityId &entryId) const + { + for (const auto &entry : pool_) { + if (entry.second.GetData() == value) { + entryId = entry.first; + return true; + } + } + return false; + } + + const Entry *GetEntry(ApEntityId id) const + { + auto iter = pool_.find(id); + if (iter == pool_.end()) { + return nullptr; + } + return &(iter->second); + } + + void Clear() + { + pool_.clear(); + reservedUsed_ = 0; + } + + bool Empty() const + { + return pool_.empty(); + } + + void Merge(const PoolTemplate &pool, const std::function &callback) + { + for (const auto &entry : pool.pool_) { + ApEntityId newId(0); + TryAdd(entry.second.GetData(), newId); + if (callback != nullptr) { + callback(entry.first, newId); + } + } + } + + uint32_t ProcessToBinary(std::fstream &stream) override + { + LOG_ECMA(DEBUG) << "ProcessToBinary. name: " << poolName_ << ", count: " << pool_.size(); + SectionInfo secInfo; + secInfo.number_ = pool_.size(); + secInfo.offset_ = sizeof(SectionInfo); + auto secInfoPos = stream.tellp(); + stream.seekp(secInfo.offset_, std::ofstream::cur); + for (auto &entry : pool_) { + stream.write(reinterpret_cast(&(entry.first)), sizeof(ApEntityId)); + entry.second.ProcessToBinary(stream); + } + secInfo.size_ = static_cast(stream.tellp()) - static_cast(secInfoPos); + auto tail = stream.tellp(); + stream.seekp(secInfoPos, std::ofstream::beg); + stream.write(reinterpret_cast(&(secInfo)), sizeof(SectionInfo)); + stream.seekp(tail, std::ofstream::beg); + return 1; + } + + bool ProcessToText(std::ofstream &stream) override + { + bool isFirst = true; + for (auto &entry : pool_) { + if (isFirst) { + stream << DumpUtils::NEW_LINE; + stream << poolName_; + stream << DumpUtils::BLOCK_START; + isFirst = false; + } + stream << DumpUtils::NEW_LINE; + stream << std::to_string(entry.first); + stream << DumpUtils::SPACE; + stream << DumpUtils::ARRAY_START; + entry.second.ProcessToText(stream); + stream << DumpUtils::ARRAY_END; + } + if (!isFirst) { + stream << (DumpUtils::SPACE + DumpUtils::NEW_LINE); + } + return true; + } + + uint32_t ParseFromBinary(void **buffer, PGOProfilerHeader const *header) override + { + auto secInfo = base::ReadBuffer(buffer); + for (uint32_t i = 0; i < secInfo.number_; i++) { + auto entryId = base::ReadBuffer(buffer, sizeof(ApEntityId)); + auto result = pool_.try_emplace(entryId); + result.first->second.SetEntryId(entryId); + result.first->second.ParseFromBinary(buffer, header); + } + LOG_ECMA(DEBUG) << "ParseFromBinary. name: " << poolName_ << ", count: " << secInfo.number_; + return 1; + } + + void SetIsReservedCb(const IsReservedCb &isReservedCb) + { + isReservedCb_ = isReservedCb; + } + + void SetGetReservedIdCb(const GetReservedIdCb &getReservedIdCb) + { + getReservedIdCb_ = getReservedIdCb; + } + + void SetSupportCb(const SupportCb &supportCb) + { + supportCb_ = supportCb; + } + + void SetGetSectionCb(const GetSectionCb &getSectionCb) + { + getSectionCb_ = getSectionCb; + } + + std::unordered_map &GetPool() + { + return pool_; + } + +private: + NO_COPY_SEMANTIC(PoolTemplate); + NO_MOVE_SEMANTIC(PoolTemplate); + + bool Support(PGOProfilerHeader const *header) const override + { + return supportCb_(header); + } + + SectionInfo *GetSection(PGOProfilerHeader const *header) const override + { + return getSectionCb_(header); + } + + bool IsReserved(const V &value) + { + return isReservedCb_(value); + } + + ApEntityId GetReservedId(const V &value) + { + return getReservedIdCb_(value); + } + + const std::string poolName_; + const uint32_t RESERVED_COUNT {}; + uint32_t reservedUsed_ {0}; + + IsReservedCb isReservedCb_; + GetReservedIdCb getReservedIdCb_; + SupportCb supportCb_; + GetSectionCb getSectionCb_; + std::unordered_map pool_; +}; +} // namespace panda::ecmascript::pgo +#endif // ECMASCRIPT_PGO_PROFILER_AP_FILE_POOL_TEMPLATE_H diff --git a/ecmascript/pgo_profiler/pgo_context.h b/ecmascript/pgo_profiler/pgo_context.h new file mode 100644 index 0000000000000000000000000000000000000000..b539ecd0dea245c8e2f429d5573be5f0b041a4ca --- /dev/null +++ b/ecmascript/pgo_profiler/pgo_context.h @@ -0,0 +1,46 @@ +/* + * 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_PGO_PROFILER_PGO_CONTEXT_H +#define ECMASCRIPT_PGO_PROFILER_PGO_CONTEXT_H + +#include +#include +#include +#include +#include + +#include "macros.h" + +namespace panda::ecmascript::pgo { +class PGOAbcFilePool; +class PGOProfileTypePool; +class PGOProfilerHeader; + +class PGOContext { +public: + PGOContext() = default; + virtual ~PGOContext() = default; + virtual std::shared_ptr GetProfileTypePool() const = 0; + virtual uint32_t GetHotnessThreshold() const = 0; + virtual PGOProfilerHeader *GetHeader() const = 0; + virtual bool SupportElementsKind() const = 0; + +private: + NO_COPY_SEMANTIC(PGOContext); + NO_MOVE_SEMANTIC(PGOContext); +}; +} // namespace panda::ecmascript::pgo +#endif // ECMASCRIPT_PGO_PROFILER_PGO_CONTEXT_H diff --git a/ecmascript/pgo_profiler/pgo_profiler.cpp b/ecmascript/pgo_profiler/pgo_profiler.cpp index 38664d4d233efbf9310553a8a8eb342618f10c29..3393b466722f4abab8b1301d951731464b4e7836 100644 --- a/ecmascript/pgo_profiler/pgo_profiler.cpp +++ b/ecmascript/pgo_profiler/pgo_profiler.cpp @@ -15,169 +15,968 @@ #include "ecmascript/pgo_profiler/pgo_profiler.h" +#include + +#include "ecmascript/ic/ic_handler.h" +#include "ecmascript/ic/profile_type_info.h" +#include "ecmascript/interpreter/interpreter-inl.h" #include "ecmascript/js_function.h" +#include "ecmascript/log_wrapper.h" +#include "ecmascript/pgo_profiler/pgo_context.h" +#include "ecmascript/pgo_profiler/pgo_profiler_info.h" #include "ecmascript/pgo_profiler/pgo_profiler_manager.h" +#include "ecmascript/pgo_profiler/types/pgo_profile_type.h" +#include "ecmascript/pgo_profiler/pgo_utils.h" +#include "ecmascript/module/js_module_source_text.h" +#include "ecmascript/jspandafile/js_pandafile.h" +#include "macros.h" -namespace panda::ecmascript { -void PGOProfiler::ProfileCall(JSTaggedType value, SampleMode mode) +namespace panda::ecmascript::pgo { +void PGOProfiler::ProfileCall(JSTaggedType callTarget, SampleMode mode, int32_t incCount) { if (!isEnable_) { return; } DISALLOW_GARBAGE_COLLECTION; - JSTaggedValue jsValue(value); - if (jsValue.IsJSFunction() && JSFunction::Cast(jsValue)->GetMethod().IsMethod()) { - auto jsMethod = Method::Cast(JSFunction::Cast(jsValue)->GetMethod()); - JSTaggedValue recordNameValue = JSFunction::Cast(jsValue)->GetRecordName(); - if (recordNameValue.IsHole()) { + JSTaggedValue calleeFunc(callTarget); + if (!calleeFunc.IsJSFunction()) { + return; + } + if (!JSFunction::Cast(calleeFunc)->GetMethod().IsMethod()) { + return; + } + auto calleeMethod = Method::Cast(JSFunction::Cast(calleeFunc)->GetMethod()); + JSTaggedValue calleeRecordNameValue = calleeMethod->GetRecordName(); + if (calleeRecordNameValue.IsHole()) { + return; + } + CString calleeRecordName = ConvertToString(calleeRecordNameValue); + ProfileType calleeRecordType = GetRecordProfileType(JSFunction::Cast(calleeFunc), calleeRecordName); + recordInfos_->AddMethod(calleeRecordType, calleeMethod, mode, incCount); +} + +void PGOProfiler::ProfileCreateObject(JSTaggedType object, ApEntityId abcId, int32_t traceId) +{ + if (!isEnable_) { + return; + } + + JSTaggedValue objectValue(object); + if (objectValue.IsJSObject()) { + auto hclass = objectValue.GetTaggedObject()->GetClass(); + hclass->SetParent(vm_->GetJSThread(), JSTaggedValue::Undefined()); + InsertLiteralTraceId(JSTaggedType(hclass), abcId, traceId); + } +} + +void PGOProfiler::ProfileDefineClass(JSTaggedType ctor) +{ + if (!isEnable_) { + return; + } + auto ctorValue = JSTaggedValue(ctor); + if (!ctorValue.IsJSFunction()) { + return; + } + auto ctorFunc = JSFunction::Cast(ctorValue.GetTaggedObject()); + auto ctorMethodValue = ctorFunc->GetMethod(); + if (!ctorMethodValue.IsMethod()) { + return; + } + auto ctorMethod = Method::Cast(ctorMethodValue); + auto traceId = ctorMethod->GetMethodId().GetOffset(); + auto ctorMethodHClass = ctorFunc->GetClass(); + ctorMethodHClass->SetParent(vm_->GetJSThread(), JSTaggedValue::Undefined()); + InsertLiteralTraceId(JSTaggedType(ctorFunc->GetClass()), GetMethodAbcId(ctorFunc), traceId); + + auto prototype = ctorFunc->GetProtoOrHClass(); + if (prototype.IsJSObject()) { + auto prototypeHClass = prototype.GetTaggedObject()->GetClass(); + prototypeHClass->SetParent(vm_->GetJSThread(), JSTaggedValue::Undefined()); + InsertLiteralTraceId(JSTaggedType(prototypeHClass), GetMethodAbcId(ctorFunc), traceId); + } +} + +void PGOProfiler::UpdateTrackInfo(JSTaggedValue trackInfoVal, ElementsKind newKind) +{ + if (trackInfoVal.IsHeapObject() && trackInfoVal.IsWeak()) { + auto trackInfo = TrackInfo::Cast(trackInfoVal.GetWeakReferentUnChecked()); + auto oldKind = trackInfo->GetElementsKind(); + if (oldKind == newKind) { return; } - CString recordName = ConvertToString(recordNameValue); - auto checksum = PGOMethodInfo::CalcChecksum(jsMethod->GetMethodName(), jsMethod->GetBytecodeArray(), - jsMethod->GetCodeSize()); - if (recordInfos_->AddMethod(recordName, jsMethod->GetMethodId(), checksum, jsMethod->GetMethodName(), mode)) { - methodCount_++; + trackInfo->SetElementsKind(newKind); + auto func = trackInfo->GetCachedFunc(); + if (!func.IsJSFunction()) { + return; + } + auto method = JSFunction::Cast(func)->GetMethod(); + auto profileTypeInfoVal = Method::Cast(method)->GetProfileTypeInfo(); + if (profileTypeInfoVal.IsUndefined()) { + return; } - // Merged every 10 methods - if (methodCount_ >= MERGED_EVERY_COUNT) { - LOG_ECMA(DEBUG) << "Sample: post task to save profiler"; - PGOProfilerManager::GetInstance()->Merge(this); - PGOProfilerManager::GetInstance()->AsynSave(); - methodCount_ = 0; + auto profileTypeInfo = ProfileTypeInfo::Cast(profileTypeInfoVal.GetTaggedObject()); + if (!profileTypeInfo->IsProfileTypeInfoChanged()) { + profileTypeInfo->ResetPeriodCount(); + PGOPreDump(JSTaggedType(func.GetTaggedObject())); } } } -void PGOProfiler::ProfileOpType(JSTaggedType func, int32_t offset, uint32_t type) +void PGOProfiler::PGODump(JSTaggedType func) { if (!isEnable_) { return; } + { + LockHolder lock(mutex_); + dumpWorkList_.emplace_back(func); + if (state_ == State::STOP) { + state_ = State::START; + Taskpool::GetCurrentTaskpool()->PostTask( + std::make_unique(this, vm_->GetJSThread()->GetThreadId())); + } + } +} + +void PGOProfiler::PausePGODump() +{ + if (!isEnable_) { + return; + } + LockHolder lock(mutex_); + if (state_ == State::START) { + state_ = State::PAUSE; + condition_.Wait(&mutex_); + } +} + +void PGOProfiler::ResumePGODump() +{ + if (!isEnable_) { + return; + } + LockHolder lock(mutex_); + if (state_ == State::PAUSE) { + state_ = State::START; + Taskpool::GetCurrentTaskpool()->PostTask( + std::make_unique(this, vm_->GetJSThread()->GetThreadId())); + } +} + +void PGOProfiler::WaitPGODumpFinish() +{ + if (!isEnable_) { + return; + } + LockHolder lock(mutex_); + while (state_ == State::START) { + condition_.Wait(&mutex_); + } +} +void PGOProfiler::PGOPreDump(JSTaggedType func) +{ + if (!isEnable_) { + return; + } + LockHolder lock(mutex_); + preDumpWorkList_.emplace(func); +} + +void PGOProfiler::HandlePGOPreDump() +{ + if (!isEnable_) { + return; + } DISALLOW_GARBAGE_COLLECTION; - JSTaggedValue funcValue(func); - if (funcValue.IsJSFunction() && JSFunction::Cast(funcValue)->GetMethod().IsMethod()) { - auto jsMethod = Method::Cast(JSFunction::Cast(funcValue)->GetMethod()); - JSTaggedValue recordNameValue = JSFunction::Cast(funcValue)->GetRecordName(); - if (recordNameValue.IsHole()) { - return; + CSet preDumpWorkList; + { + LockHolder lock(mutex_); + preDumpWorkList = preDumpWorkList_; + } + for (auto iter : preDumpWorkList) { + auto funcValue = JSTaggedValue(iter); + if (!funcValue.IsJSFunction()) { + continue; + } + JSFunction *func = JSFunction::Cast(funcValue.GetTaggedObject()); + JSTaggedValue methodValue = func->GetMethod(); + if (!methodValue.IsMethod()) { + continue; + } + JSTaggedValue recordNameValue = Method::Cast(methodValue)->GetRecordName(); + if (!recordNameValue.IsString()) { + continue; } CString recordName = ConvertToString(recordNameValue); - recordInfos_->AddType(recordName, jsMethod->GetMethodId(), offset, PGOSampleType(type)); + auto abcId = GetMethodAbcId(func); + ProfileByteCode(abcId, recordName, methodValue); } } -void PGOProfiler::ProfileDefineClass(JSThread *thread, JSTaggedType func, int32_t offset, JSTaggedType ctor) +void PGOProfiler::HandlePGODump() { if (!isEnable_) { return; } - DISALLOW_GARBAGE_COLLECTION; - JSTaggedValue funcValue(func); - if (funcValue.IsJSFunction()) { - JSFunction *funcFunction = JSFunction::Cast(funcValue); - JSTaggedValue recordNameValue = funcFunction->GetRecordName(); - if (recordNameValue.IsHole()) { - return; + JSTaggedValue current = PopFromProfileQueue(); + while (current.IsJSFunction()) { + JSFunction *func = JSFunction::Cast(current.GetTaggedObject()); + JSTaggedValue methodValue = func->GetMethod(); + if (!methodValue.IsMethod()) { + current = PopFromProfileQueue(); + continue; + } + JSTaggedValue recordNameValue = Method::Cast(methodValue)->GetRecordName(); + if (!recordNameValue.IsString()) { + current = PopFromProfileQueue(); + continue; } CString recordName = ConvertToString(recordNameValue); + auto abcId = GetMethodAbcId(func); + ProfileByteCode(abcId, recordName, methodValue); + methodCount_++; + current = PopFromProfileQueue(); + } + if (state_ == State::PAUSE) { + return; + } + // Merged every 10 methods and merge interval greater than minimal interval + auto interval = std::chrono::system_clock::now() - saveTimestamp_; + if (methodCount_ >= MERGED_EVERY_COUNT && interval > MERGED_MIN_INTERVAL) { + LOG_ECMA(DEBUG) << "Sample: post task to save profiler"; + PGOProfilerManager::GetInstance()->Merge(this); + PGOProfilerManager::GetInstance()->AsynSave(); + SetSaveTimestamp(std::chrono::system_clock::now()); + methodCount_ = 0; + } +} + +JSTaggedValue PGOProfiler::PopFromProfileQueue() +{ + LockHolder lock(mutex_); + auto result = JSTaggedValue::Undefined(); + while (result.IsUndefined()) { + if (dumpWorkList_.empty()) { + state_ = State::STOP; + condition_.SignalAll(); + break; + } + if (state_ == State::PAUSE) { + condition_.SignalAll(); + break; + } + auto func = dumpWorkList_.front(); + dumpWorkList_.pop_front(); + result = JSTaggedValue(func); + auto iter = std::find(preDumpWorkList_.begin(), preDumpWorkList_.end(), func); + if (iter != preDumpWorkList_.end()) { + preDumpWorkList_.erase(iter); + } + } + return result; +} + +void PGOProfiler::ProfileByteCode(ApEntityId abcId, const CString &recordName, JSTaggedValue value) +{ + Method *method = Method::Cast(value.GetTaggedObject()); + JSTaggedValue profileTypeInfoVal = method->GetProfileTypeInfo(); + ASSERT(!profileTypeInfoVal.IsUndefined()); + auto profileTypeInfo = ProfileTypeInfo::Cast(profileTypeInfoVal.GetTaggedObject()); + auto methodId = method->GetMethodId(); + auto pcStart = method->GetBytecodeArray(); + auto codeSize = method->GetCodeSize(); + BytecodeInstruction bcIns(pcStart); + auto bcInsLast = bcIns.JumpTo(codeSize); + + while (bcIns.GetAddress() != bcInsLast.GetAddress()) { + auto opcode = bcIns.GetOpcode(); + auto bcOffset = bcIns.GetAddress() - pcStart; + auto pc = bcIns.GetAddress(); + switch (opcode) { + case EcmaOpcode::LDTHISBYNAME_IMM8_ID16: + case EcmaOpcode::LDOBJBYNAME_IMM8_ID16: { + uint8_t slotId = READ_INST_8_0(); + DumpICByName(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo, BCType::LOAD); + break; + } + case EcmaOpcode::LDTHISBYNAME_IMM16_ID16: + case EcmaOpcode::LDOBJBYNAME_IMM16_ID16: { + uint16_t slotId = READ_INST_16_0(); + DumpICByName(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo, BCType::LOAD); + break; + } + case EcmaOpcode::LDOBJBYVALUE_IMM8_V8: + case EcmaOpcode::LDTHISBYVALUE_IMM8: { + uint8_t slotId = READ_INST_8_0(); + DumpICByValue(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo, BCType::LOAD); + break; + } + case EcmaOpcode::LDOBJBYVALUE_IMM16_V8: + case EcmaOpcode::LDTHISBYVALUE_IMM16: { + uint16_t slotId = READ_INST_16_0(); + DumpICByValue(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo, BCType::LOAD); + break; + } + case EcmaOpcode::STOBJBYNAME_IMM8_ID16_V8: + case EcmaOpcode::STTHISBYNAME_IMM8_ID16: { + uint8_t slotId = READ_INST_8_0(); + DumpICByName(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo, BCType::STORE); + break; + } + case EcmaOpcode::STOBJBYNAME_IMM16_ID16_V8: + case EcmaOpcode::STTHISBYNAME_IMM16_ID16: { + uint16_t slotId = READ_INST_16_0(); + DumpICByName(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo, BCType::STORE); + break; + } + case EcmaOpcode::STOBJBYVALUE_IMM8_V8_V8: + case EcmaOpcode::STTHISBYVALUE_IMM8_V8: { + uint8_t slotId = READ_INST_8_0(); + DumpICByValue(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo, BCType::STORE); + break; + } + case EcmaOpcode::STOBJBYVALUE_IMM16_V8_V8: + case EcmaOpcode::STTHISBYVALUE_IMM16_V8: { + uint16_t slotId = READ_INST_16_0(); + DumpICByValue(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo, BCType::STORE); + break; + } + // Op + case EcmaOpcode::ADD2_IMM8_V8: + case EcmaOpcode::SUB2_IMM8_V8: + case EcmaOpcode::MUL2_IMM8_V8: + case EcmaOpcode::DIV2_IMM8_V8: + case EcmaOpcode::MOD2_IMM8_V8: + case EcmaOpcode::SHL2_IMM8_V8: + case EcmaOpcode::SHR2_IMM8_V8: + case EcmaOpcode::AND2_IMM8_V8: + case EcmaOpcode::OR2_IMM8_V8: + case EcmaOpcode::XOR2_IMM8_V8: + case EcmaOpcode::ASHR2_IMM8_V8: + case EcmaOpcode::EXP_IMM8_V8: + case EcmaOpcode::NEG_IMM8: + case EcmaOpcode::NOT_IMM8: + case EcmaOpcode::INC_IMM8: + case EcmaOpcode::DEC_IMM8: + case EcmaOpcode::EQ_IMM8_V8: + case EcmaOpcode::NOTEQ_IMM8_V8: + case EcmaOpcode::LESS_IMM8_V8: + case EcmaOpcode::LESSEQ_IMM8_V8: + case EcmaOpcode::GREATER_IMM8_V8: + case EcmaOpcode::GREATEREQ_IMM8_V8: + case EcmaOpcode::STRICTNOTEQ_IMM8_V8: + case EcmaOpcode::STRICTEQ_IMM8_V8: { + uint8_t slotId = READ_INST_8_0(); + DumpOpType(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo); + break; + } + // Call + case EcmaOpcode::CALLARG0_IMM8: + case EcmaOpcode::CALLARG1_IMM8_V8: + case EcmaOpcode::CALLARGS2_IMM8_V8_V8: + case EcmaOpcode::CALLARGS3_IMM8_V8_V8_V8: + case EcmaOpcode::CALLRANGE_IMM8_IMM8_V8: + case EcmaOpcode::CALLTHIS0_IMM8_V8: + case EcmaOpcode::CALLTHIS1_IMM8_V8_V8: + case EcmaOpcode::CALLTHIS2_IMM8_V8_V8_V8: + case EcmaOpcode::CALLTHIS3_IMM8_V8_V8_V8_V8: + case EcmaOpcode::CALLTHISRANGE_IMM8_IMM8_V8: { + uint8_t slotId = READ_INST_8_0(); + DumpCall(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo); + break; + } + case EcmaOpcode::WIDE_CALLRANGE_PREF_IMM16_V8: + case EcmaOpcode::WIDE_CALLTHISRANGE_PREF_IMM16_V8: { + uint16_t slotId = READ_INST_16_0(); + DumpCall(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo); + break; + } + case EcmaOpcode::NEWOBJRANGE_IMM8_IMM8_V8: + case EcmaOpcode::NEWOBJRANGE_IMM16_IMM8_V8: + case EcmaOpcode::WIDE_NEWOBJRANGE_PREF_IMM16_V8: { + break; + } + // Create object + case EcmaOpcode::DEFINECLASSWITHBUFFER_IMM8_ID16_ID16_IMM16_V8: { + uint8_t slotId = READ_INST_8_0(); + DumpDefineClass(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo); + break; + } + case EcmaOpcode::DEFINECLASSWITHBUFFER_IMM16_ID16_ID16_IMM16_V8: { + uint16_t slotId = READ_INST_16_0(); + DumpDefineClass(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo); + break; + } + case EcmaOpcode::CREATEOBJECTWITHBUFFER_IMM8_ID16: + case EcmaOpcode::CREATEARRAYWITHBUFFER_IMM8_ID16: + case EcmaOpcode::CREATEEMPTYARRAY_IMM8: { + auto header = method->GetJSPandaFile()->GetPandaFile()->GetHeader(); + auto traceId = + static_cast(reinterpret_cast(pc) - reinterpret_cast(header)); + uint8_t slotId = READ_INST_8_0(); + DumpCreateObject(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo, traceId); + break; + } + case EcmaOpcode::CREATEOBJECTWITHBUFFER_IMM16_ID16: + case EcmaOpcode::CREATEARRAYWITHBUFFER_IMM16_ID16: + case EcmaOpcode::CREATEEMPTYARRAY_IMM16: { + auto header = method->GetJSPandaFile()->GetPandaFile()->GetHeader(); + auto traceId = + static_cast(reinterpret_cast(pc) - reinterpret_cast(header)); + uint16_t slotId = READ_INST_16_0(); + DumpCreateObject(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo, traceId); + break; + } + case EcmaOpcode::DEFINEGETTERSETTERBYVALUE_V8_V8_V8_V8: + default: + break; + } + bcIns = bcIns.GetNext(); + } +} + +void PGOProfiler::DumpICByName(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, + uint32_t slotId, ProfileTypeInfo *profileTypeInfo, BCType type) +{ + JSTaggedValue firstValue = profileTypeInfo->Get(slotId); + if (!firstValue.IsHeapObject()) { + return; + } + if (firstValue.IsWeak()) { + TaggedObject *object = firstValue.GetWeakReferentUnChecked(); + if (object->GetClass()->IsHClass()) { + JSTaggedValue secondValue = profileTypeInfo->Get(slotId + 1); + JSHClass *hclass = JSHClass::Cast(object); + DumpICByNameWithHandler(abcId, recordName, methodId, bcOffset, hclass, secondValue, type); + } + return; + } + DumpICByNameWithPoly(abcId, recordName, methodId, bcOffset, firstValue, type); +} + +void PGOProfiler::DumpICByValue(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, + uint32_t slotId, ProfileTypeInfo *profileTypeInfo, BCType type) +{ + JSTaggedValue firstValue = profileTypeInfo->Get(slotId); + if (!firstValue.IsHeapObject()) { + return; + } + if (firstValue.IsWeak()) { + TaggedObject *object = firstValue.GetWeakReferentUnChecked(); + if (object->GetClass()->IsHClass()) { + JSTaggedValue secondValue = profileTypeInfo->Get(slotId + 1); + JSHClass *hclass = JSHClass::Cast(object); + DumpICByValueWithHandler(abcId, recordName, methodId, bcOffset, hclass, secondValue, type); + } + return; + } + // Check key + if ((firstValue.IsString() || firstValue.IsSymbol())) { + JSTaggedValue secondValue = profileTypeInfo->Get(slotId + 1); + DumpICByNameWithPoly(abcId, recordName, methodId, bcOffset, secondValue, type); + return; + } + // Check without key + DumpICByValueWithPoly(abcId, recordName, methodId, bcOffset, firstValue, type); +} - auto method = funcFunction->GetMethod(); - if (!method.IsMethod()) { +void PGOProfiler::DumpICByNameWithPoly(ApEntityId abcId, + const CString &recordName, EntityId methodId, int32_t bcOffset, JSTaggedValue cacheValue, BCType type) +{ + if (cacheValue.IsWeak()) { + return; + } + ASSERT(cacheValue.IsTaggedArray()); + auto array = TaggedArray::Cast(cacheValue); + uint32_t length = array->GetLength(); + for (uint32_t i = 0; i < length; i += 2) { // 2 means one ic, two slot + auto result = array->Get(i); + auto handler = array->Get(i + 1); + if (!result.IsHeapObject() || !result.IsWeak()) { + continue; + } + TaggedObject *object = result.GetWeakReferentUnChecked(); + if (!object->GetClass()->IsHClass()) { + continue; + } + JSHClass *hclass = JSHClass::Cast(object); + DumpICByNameWithHandler(abcId, recordName, methodId, bcOffset, hclass, handler, type); + } +} + +void PGOProfiler::DumpICByValueWithPoly(ApEntityId abcId, + const CString &recordName, EntityId methodId, int32_t bcOffset, JSTaggedValue cacheValue, BCType type) +{ + if (cacheValue.IsWeak()) { + return; + } + ASSERT(cacheValue.IsTaggedArray()); + auto array = TaggedArray::Cast(cacheValue); + uint32_t length = array->GetLength(); + for (uint32_t i = 0; i < length; i += 2) { // 2 means one ic, two slot + auto result = array->Get(i); + auto handler = array->Get(i + 1); + if (!result.IsHeapObject() || !result.IsWeak()) { + continue; + } + TaggedObject *object = result.GetWeakReferentUnChecked(); + if (!object->GetClass()->IsHClass()) { + continue; + } + JSHClass *hclass = JSHClass::Cast(object); + DumpICByValueWithHandler(abcId, recordName, methodId, bcOffset, hclass, handler, type); + } +} + +void PGOProfiler::DumpICByNameWithHandler(ApEntityId abcId, const CString &recordName, EntityId methodId, + int32_t bcOffset, JSHClass *hclass, JSTaggedValue secondValue, BCType type) +{ + if (type == BCType::LOAD) { + if (secondValue.IsInt()) { + auto handlerInfo = static_cast(secondValue.GetInt()); + if (HandlerBase::IsNonExist(handlerInfo)) { + return; + } + auto kind = PGOObjKind::LOCAL; + AddObjectInfo(abcId, recordName, methodId, bcOffset, hclass, kind); + } + if (secondValue.IsPrototypeHandler()) { + auto kind = PGOObjKind::PROTOTYPE; + AddObjectInfo(abcId, recordName, methodId, bcOffset, hclass, kind); + } + // LoadGlobal + return; + } + if (secondValue.IsInt()) { + auto kind = PGOObjKind::LOCAL; + AddObjectInfo(abcId, recordName, methodId, bcOffset, hclass, kind); + } + if (secondValue.IsTransitionHandler()) { + auto kind = PGOObjKind::LOCAL; + auto transitionHandler = TransitionHandler::Cast(secondValue.GetTaggedObject()); + auto transitionHClassVal = transitionHandler->GetTransitionHClass(); + if (transitionHClassVal.IsJSHClass()) { + auto transitionHClass = JSHClass::Cast(transitionHClassVal.GetTaggedObject()); + AddObjectInfo(abcId, recordName, methodId, bcOffset, transitionHClass, kind); + } + } + if (secondValue.IsTransWithProtoHandler()) { + auto kind = PGOObjKind::PROTOTYPE; + auto transWithProtoHandler = TransWithProtoHandler::Cast(secondValue.GetTaggedObject()); + auto transitionHClassVal = transWithProtoHandler->GetTransitionHClass(); + if (transitionHClassVal.IsJSHClass()) { + auto transitionHClass = JSHClass::Cast(transitionHClassVal.GetTaggedObject()); + AddObjectInfo(abcId, recordName, methodId, bcOffset, transitionHClass, kind); + } + } + if (secondValue.IsTransitionHandler()) { + auto kind = PGOObjKind::LOCAL; + auto transitionHandler = TransitionHandler::Cast(secondValue.GetTaggedObject()); + auto transitionHClassVal = transitionHandler->GetTransitionHClass(); + if (transitionHClassVal.IsJSHClass()) { + auto transitionHClass = JSHClass::Cast(transitionHClassVal.GetTaggedObject()); + AddObjectInfo(abcId, recordName, methodId, bcOffset, transitionHClass, kind); + } + } + if (secondValue.IsPrototypeHandler()) { + auto kind = PGOObjKind::PROTOTYPE; + AddObjectInfo(abcId, recordName, methodId, bcOffset, hclass, kind); + } + if (secondValue.IsPropertyBox()) { + // StoreGlobal + } + if (secondValue.IsStoreTSHandler()) { + StoreTSHandler *storeTSHandler = StoreTSHandler::Cast(secondValue.GetTaggedObject()); + auto cellValue = storeTSHandler->GetProtoCell(); + ASSERT(cellValue.IsProtoChangeMarker()); + ProtoChangeMarker *cell = ProtoChangeMarker::Cast(cellValue.GetTaggedObject()); + if (cell->GetHasChanged()) { return; } - auto jsMethod = Method::Cast(method); - auto funcMethodId = jsMethod->GetMethodId(); + auto kind = PGOObjKind::LOCAL; + AddObjectInfo(abcId, recordName, methodId, bcOffset, hclass, kind); + } +} + +void PGOProfiler::DumpICByValueWithHandler(ApEntityId abcId, const CString &recordName, EntityId methodId, + int32_t bcOffset, JSHClass *hclass, JSTaggedValue secondValue, BCType type) +{ + if (type == BCType::LOAD) { + if (secondValue.IsInt()) { + auto handlerInfo = static_cast(secondValue.GetInt()); + PGOObjKind kind = PGOObjKind::LOCAL; + if (HandlerBase::IsJSArray(handlerInfo)) { + kind = PGOObjKind::ELEMENT; + } + AddObjectInfo(abcId, recordName, methodId, bcOffset, hclass, kind); + } + return; + } + if (secondValue.IsInt()) { + auto handlerInfo = static_cast(secondValue.GetInt()); + PGOObjKind kind = PGOObjKind::LOCAL; + if (HandlerBase::IsJSArray(handlerInfo)) { + kind = PGOObjKind::ELEMENT; + } + AddObjectInfo(abcId, recordName, methodId, bcOffset, hclass, kind); + } else if (secondValue.IsTransitionHandler()) { + auto transitionHandler = TransitionHandler::Cast(secondValue.GetTaggedObject()); + auto transitionHClassVal = transitionHandler->GetTransitionHClass(); + + auto handlerInfoValue = transitionHandler->GetHandlerInfo(); + ASSERT(handlerInfoValue.IsInt()); + auto handlerInfo = static_cast(handlerInfoValue.GetInt()); + PGOObjKind kind = PGOObjKind::LOCAL; + if (HandlerBase::IsJSArray(handlerInfo)) { + kind = PGOObjKind::ELEMENT; + } + if (transitionHClassVal.IsJSHClass()) { + auto transitionHClass = JSHClass::Cast(transitionHClassVal.GetTaggedObject()); + AddObjectInfo(abcId, recordName, methodId, bcOffset, transitionHClass, kind); + } + } else if (secondValue.IsTransWithProtoHandler()) { + auto transWithProtoHandler = TransWithProtoHandler::Cast(secondValue.GetTaggedObject()); + auto transitionHClassVal = transWithProtoHandler->GetTransitionHClass(); - JSHandle ctorValue(thread, JSTaggedValue(ctor)); - if (!ctorValue->IsJSFunction()) { + auto handlerInfoValue = transWithProtoHandler->GetHandlerInfo(); + ASSERT(handlerInfoValue.IsInt()); + auto handlerInfo = static_cast(handlerInfoValue.GetInt()); + auto kind = PGOObjKind::PROTOTYPE; + if (HandlerBase::IsJSArray(handlerInfo)) { + kind = PGOObjKind::ELEMENT; + } + if (transitionHClassVal.IsJSHClass()) { + auto transitionHClass = JSHClass::Cast(transitionHClassVal.GetTaggedObject()); + AddObjectInfo(abcId, recordName, methodId, bcOffset, transitionHClass, kind); + } + } else { + ASSERT(secondValue.IsPrototypeHandler()); + PrototypeHandler *prototypeHandler = PrototypeHandler::Cast(secondValue.GetTaggedObject()); + auto cellValue = prototypeHandler->GetProtoCell(); + ASSERT(cellValue.IsProtoChangeMarker()); + ProtoChangeMarker *cell = ProtoChangeMarker::Cast(cellValue.GetTaggedObject()); + if (cell->GetHasChanged()) { return; } - JSFunction *ctorFunction = JSFunction::Cast(ctorValue->GetTaggedObject()); + JSTaggedValue handlerInfoValue = prototypeHandler->GetHandlerInfo(); + ASSERT(handlerInfoValue.IsInt()); + auto handlerInfo = static_cast(handlerInfoValue.GetInt()); + PGOObjKind kind = PGOObjKind::PROTOTYPE; + if (HandlerBase::IsJSArray(handlerInfo)) { + kind = PGOObjKind::ELEMENT; + } + AddObjectInfo(abcId, recordName, methodId, bcOffset, hclass, kind); + } +} + +void PGOProfiler::DumpOpType(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, + uint32_t slotId, ProfileTypeInfo *profileTypeInfo) +{ + JSTaggedValue slotValue = profileTypeInfo->Get(slotId); + if (slotValue.IsInt()) { + auto type = slotValue.GetInt(); + ProfileType recordType = GetRecordProfileType(abcId, recordName); + recordInfos_->AddType(recordType, methodId, bcOffset, PGOSampleType(type)); + } +} + +void PGOProfiler::DumpDefineClass(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, + uint32_t slotId, ProfileTypeInfo *profileTypeInfo) +{ + JSTaggedValue slotValue = profileTypeInfo->Get(slotId); + if (!slotValue.IsHeapObject() || !slotValue.IsWeak()) { + return; + } + auto object = slotValue.GetWeakReferentUnChecked(); + if (object->GetClass()->IsJSFunction()) { + JSFunction *ctorFunction = JSFunction::Cast(object); auto ctorMethod = ctorFunction->GetMethod(); if (!ctorMethod.IsMethod()) { return; } + ApEntityId ctorAbcId = GetMethodAbcId(ctorFunction); auto ctorJSMethod = Method::Cast(ctorMethod); int32_t ctorMethodId = static_cast(ctorJSMethod->GetMethodId().GetOffset()); - auto currentType = PGOSampleType::CreateClassType(ctorMethodId); + auto currentType = PGOSampleType::CreateProfileType(ctorAbcId, ctorMethodId); - auto superFuncValue = JSTaggedValue::GetPrototype(thread, ctorValue); - PGOSampleType superType = PGOSampleType::CreateClassType(0); + auto superFuncValue = ctorFunction->GetJSHClass()->GetPrototype(); + PGOSampleType superType(ProfileType::PROFILE_TYPE_NONE); if (superFuncValue.IsJSFunction()) { auto superFuncFunction = JSFunction::Cast(superFuncValue); if (superFuncFunction->GetMethod().IsMethod()) { + ApEntityId superAbcId = GetMethodAbcId(superFuncFunction); auto superMethod = Method::Cast(superFuncFunction->GetMethod()); auto superMethodId = superMethod->GetMethodId().GetOffset(); - superType = PGOSampleType::CreateClassType(superMethodId); + superType = PGOSampleType::CreateProfileType(superAbcId, superMethodId); } } - recordInfos_->AddDefine(recordName, funcMethodId, offset, currentType, superType); + ProfileType recordType = GetRecordProfileType(abcId, recordName); + recordInfos_->AddDefine(recordType, methodId, bcOffset, currentType, superType); + + auto hclassValue = ctorFunction->GetProtoOrHClass(); + if (!hclassValue.IsJSHClass()) { + return; + } + auto hclass = JSHClass::Cast(hclassValue.GetTaggedObject()); + recordInfos_->AddLayout(currentType, JSTaggedType(hclass), PGOObjKind::LOCAL); + + auto ctorHClass = JSTaggedType(ctorFunction->GetJSHClass()); + recordInfos_->AddLayout(currentType, ctorHClass, PGOObjKind::CONSTRUCTOR); - auto prototype = ctorFunction->GetProtoOrHClass(); + auto prototype = hclass->GetProto(); if (!prototype.IsJSObject()) { return; } auto prototypeObj = JSObject::Cast(prototype); auto prototypeHClass = JSTaggedType(prototypeObj->GetClass()); - recordInfos_->AddLayout(currentType, prototypeHClass, PGOObjLayoutKind::PROTOTYPE); - - auto ctorHClass = JSTaggedType(ctorFunction->GetJSHClass()); - recordInfos_->AddLayout(currentType, ctorHClass, PGOObjLayoutKind::CONSTRUCTOR); + recordInfos_->AddLayout(currentType, prototypeHClass, PGOObjKind::PROTOTYPE); } } -void PGOProfiler::ProfileObjLayout(JSThread *thread, JSTaggedType func, int32_t offset, JSTaggedType object, bool store) +void PGOProfiler::DumpCreateObject(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, + uint32_t slotId, ProfileTypeInfo *profileTypeInfo, int32_t traceId) { - if (!isEnable_) { + JSTaggedValue slotValue = profileTypeInfo->Get(slotId); + if (!slotValue.IsHeapObject()) { return; } - - DISALLOW_GARBAGE_COLLECTION; - JSTaggedValue funcValue(func); - if (funcValue.IsJSFunction()) { - JSFunction *funcFunction = JSFunction::Cast(funcValue); - auto method = funcFunction->GetMethod(); - if (!method.IsMethod()) { - return; + ProfileType recordType = GetRecordProfileType(abcId, recordName); + if (slotValue.IsWeak()) { + auto object = slotValue.GetWeakReferentUnChecked(); + if (object->GetClass()->IsHClass()) { + auto newHClass = JSHClass::Cast(object); + ASSERT(newHClass->IsJSObject()); + auto iter = tracedProfiles_.find(JSTaggedType(newHClass)); + if (iter == tracedProfiles_.end()) { + return; + } + ASSERT(iter->second.GetKind() == ProfileType::Kind::LiteralId); + PGOSampleType currentType(iter->second); + PGOSampleType superType(ProfileType::PROFILE_TYPE_NONE); + recordInfos_->AddDefine(recordType, methodId, bcOffset, currentType, superType); + PGOObjKind kind = PGOObjKind::LOCAL; + recordInfos_->AddLayout(currentType, JSTaggedType(newHClass), kind); } - auto jsMethod = Method::Cast(method); - JSTaggedValue recordNameValue = funcFunction->GetRecordName(); - if (recordNameValue.IsHole()) { - return; + } else if (slotValue.IsTrackInfoObject()) { + auto currentType = PGOSampleType::CreateProfileType(abcId, traceId); + PGOSampleType superType(ProfileType::PROFILE_TYPE_NONE); + recordInfos_->AddDefine(recordType, methodId, bcOffset, currentType, superType); + TrackInfo *trackInfo = TrackInfo::Cast(slotValue.GetTaggedObject()); + auto cachedHClass = trackInfo->GetCachedHClass(); + if (cachedHClass.IsJSHClass()) { + auto hclass = JSHClass::Cast(cachedHClass.GetTaggedObject()); + PGOObjKind kind = PGOObjKind::ELEMENT; + recordInfos_->AddLayout(currentType, JSTaggedType(hclass), kind); } - CString recordName = ConvertToString(recordNameValue); + auto elementsKind = trackInfo->GetElementsKind(); + recordInfos_->UpdateElementsKind(currentType, elementsKind); + } +} + +void PGOProfiler::DumpCall(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, + uint32_t slotId, ProfileTypeInfo *profileTypeInfo) +{ + JSTaggedValue slotValue = profileTypeInfo->Get(slotId); + if (!slotValue.IsInt()) { + return; + } + auto calleeMethodId = slotValue.GetInt(); - auto holder = JSTaggedValue(object); - auto hclass = holder.GetTaggedObject()->GetClass(); - auto ctor = JSTaggedValue::Undefined(); - PGOObjLayoutKind kind = PGOObjLayoutKind::LOCAL; - if (hclass->IsClassPrototype()) { - ctor = JSObject::GetCtorFromPrototype(thread, holder); - kind = PGOObjLayoutKind::PROTOTYPE; - } else if (hclass->IsClassConstructor()) { - ctor = holder; - kind = PGOObjLayoutKind::CONSTRUCTOR; - } else { - auto prototype = hclass->GetProto(); - ctor = JSObject::GetCtorFromPrototype(thread, prototype); - } - - if (ctor.IsJSFunction()) { - auto ctorFunc = JSFunction::Cast(ctor); - auto ctorMethod = ctorFunc->GetMethod(); - if (!ctorMethod.IsMethod()) { + PGOSampleType type = PGOSampleType::CreateProfileType(abcId, calleeMethodId); + ProfileType recordType = GetRecordProfileType(abcId, recordName); + recordInfos_->AddCallTargetType(recordType, methodId, bcOffset, type); +} + +void PGOProfiler::AddObjectInfo(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, + JSHClass *hclass, PGOObjKind kind) +{ + ProfileType recordType = GetRecordProfileType(abcId, recordName); + if (hclass->IsClassConstructor()) { + kind = PGOObjKind::CONSTRUCTOR; + auto rootHClass = JSHClass::FindRootHClass(hclass); + AddObjectInfoByTraceId(abcId, recordName, methodId, bcOffset, rootHClass, ProfileType::Kind::ClassId, kind); + } else if (hclass->IsJSArray()) { + if (kind == PGOObjKind::ELEMENT) { + auto elementsKind = hclass->GetElementsKind(); + auto profileType = ProfileType(abcId, static_cast(elementsKind), ProfileType::Kind::ElementId); + PGOObjectInfo info(profileType, kind); + recordInfos_->AddObjectInfo(recordType, methodId, bcOffset, info); + } + } else if (hclass->IsTypedArray()) { + // Depend to TypedArray IC + auto id = static_cast(hclass->GetObjectType()); + PGOObjectInfo info(ProfileType(abcId, id, ProfileType::Kind::BuiltinsId), kind); + recordInfos_->AddObjectInfo(recordType, methodId, bcOffset, info); + } else { + if (hclass->IsJSObject()) { + // maybe object literal + auto rootHClass = JSHClass::FindRootHClass(hclass); + auto profileKind = ProfileType::Kind::LiteralId; + if (AddObjectInfoByTraceId(abcId, recordName, methodId, bcOffset, rootHClass, profileKind, kind)) { return; } - auto ctorJSMethod = Method::Cast(ctorMethod); - auto methodId = ctorJSMethod->GetMethodId(); - PGOSampleType type = PGOSampleType::CreateClassType(methodId.GetOffset()); - recordInfos_->AddType(recordName, jsMethod->GetMethodId(), offset, type); - if (store) { - recordInfos_->AddLayout(type, JSTaggedType(hclass), kind); + } + auto prototype = hclass->GetProto(); + if (prototype.IsJSObject()) { + // maybe class object + auto prototypeHClass = prototype.GetTaggedObject()->GetClass(); + auto rootHClass = JSHClass::FindRootHClass(prototypeHClass); + if (AddObjectInfoByTraceId(abcId, recordName, methodId, bcOffset, rootHClass, ProfileType::Kind::ClassId, + kind)) { + return; } } + auto profileType = ProfileType(abcId, static_cast(0), ProfileType::Kind::UnknowId); + PGOObjectInfo info(profileType, kind); + recordInfos_->AddObjectInfo(recordType, methodId, bcOffset, info); + } +} + +bool PGOProfiler::AddObjectInfoByTraceId(ApEntityId abcId, const CString &recordName, EntityId methodId, + int32_t bcOffset, JSHClass *hclass, ProfileType::Kind classKind, + PGOObjKind kind) +{ + ProfileType recordType = GetRecordProfileType(abcId, recordName); + auto iter = tracedProfiles_.find(JSTaggedType(hclass)); + if (iter != tracedProfiles_.end()) { + ProfileType traceType = iter->second; + traceType.UpdateKind(classKind); + PGOObjectInfo info(traceType, kind); + recordInfos_->AddObjectInfo(recordType, methodId, bcOffset, info); + return true; + } + return false; +} + +ProfileType PGOProfiler::InsertLiteralTraceId(JSTaggedType hclass, ApEntityId abcId, int32_t traceId) +{ + ProfileType literalType(abcId, traceId, ProfileType::Kind::LiteralId); + return InsertTraceId(hclass, literalType); +} + +ProfileType PGOProfiler::InsertTraceId(JSTaggedType hclass, ProfileType traceType) +{ + if (!isEnable_) { + return traceType; + } + auto result = tracedProfiles_.try_emplace(hclass, traceType); + return result.first->second; +} + +void PGOProfiler::ProcessReferences(const WeakRootVisitor &visitor) +{ + if (!isEnable_) { + return; + } + for (auto iter = tracedProfiles_.begin(); iter != tracedProfiles_.end();) { + JSTaggedType object = iter->first; + auto fwd = visitor(reinterpret_cast(object)); + if (fwd == nullptr) { + iter = tracedProfiles_.erase(iter); + continue; + } + if (fwd != reinterpret_cast(object)) { + UNREACHABLE(); + } + ++iter; + } + + std::vector fwdVector; + for (auto iter = preDumpWorkList_.begin(); iter != preDumpWorkList_.end();) { + auto object = reinterpret_cast(*iter); + auto fwd = visitor(object); + if (fwd == nullptr) { + iter = preDumpWorkList_.erase(iter); + continue; + } + if (fwd != object) { + fwdVector.emplace_back(JSTaggedType(fwd)); + iter = preDumpWorkList_.erase(iter); + continue; + } + ++iter; + } + for (auto iter : fwdVector) { + preDumpWorkList_.emplace(iter); + } +} + +void PGOProfiler::Iterate(const RootVisitor &visitor) +{ + if (!isEnable_) { + return; + } + // If the IC of the method is stable, the current design forces the dump data. + // Must pause dump during GC. + for (auto &iter : dumpWorkList_) { + visitor(Root::ROOT_VM, ObjectSlot(ToUintPtr(&iter))); + } +} + +PGOProfiler::PGOProfiler(EcmaVM *vm, bool isEnable) : vm_(vm), isEnable_(isEnable) +{ + if (isEnable_) { + recordInfos_ = std::make_unique(0); + } +}; + +PGOProfiler::~PGOProfiler() +{ + Reset(false); +} + +void PGOProfiler::Reset(bool isEnable) +{ + isEnable_ = isEnable; + methodCount_ = 0; + if (recordInfos_) { + recordInfos_->Clear(); + } else { + if (isEnable_) { + recordInfos_ = std::make_unique(0); + } + } +} + +ApEntityId PGOProfiler::GetMethodAbcId(JSFunction *jsFunction) +{ + CString pfName; + auto jsMethod = jsFunction->GetMethod(); + if (jsMethod.IsMethod()) { + const auto *pf = Method::Cast(jsMethod)->GetJSPandaFile(); + if (pf != nullptr) { + pfName = pf->GetJSPandaFileDesc(); + } + } + ApEntityId abcId(0); + if (!PGOProfilerManager::GetInstance()->GetPandaFileId(pfName, abcId) && !pfName.empty()) { + LOG_ECMA(ERROR) << "Get method abc id failed. abcName: " << pfName; } + return abcId; +} + +ApEntityId PGOProfiler::GetRecordId(const CString &recordName) +{ + ApEntityId recordId(0); + recordInfos_->GetRecordPool()->TryAdd(recordName, recordId); + return recordId; +} + +ProfileType PGOProfiler::GetRecordProfileType(JSFunction *jsFunction, const CString &recordName) +{ + return GetRecordProfileType(GetMethodAbcId(jsFunction), GetRecordId(recordName)); +} + +ProfileType PGOProfiler::GetRecordProfileType(ApEntityId abcId, const CString &recordName) +{ + return GetRecordProfileType(abcId, GetRecordId(recordName)); +} + +ProfileType PGOProfiler::GetRecordProfileType(ApEntityId abcId, ApEntityId recordId) +{ + return {abcId, recordId, ProfileType::Kind::LocalRecordId}; } -} // namespace panda::ecmascript +} // namespace panda::ecmascript::pgo diff --git a/ecmascript/pgo_profiler/pgo_profiler.h b/ecmascript/pgo_profiler/pgo_profiler.h index ed2e6726a85140c71f3a126b9b493bd5459d3556..863d60938152c3af66ef8d987b94eb816f51f29f 100644 --- a/ecmascript/pgo_profiler/pgo_profiler.h +++ b/ecmascript/pgo_profiler/pgo_profiler.h @@ -16,54 +16,152 @@ #ifndef ECMASCRIPT_PGO_PROFILER_H #define ECMASCRIPT_PGO_PROFILER_H +#include #include -#include "ecmascript/ecma_vm.h" -#include "ecmascript/pgo_profiler/pgo_profiler_info.h" +#include "ecmascript/common.h" +#include "ecmascript/elements.h" +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/jspandafile/method_literal.h" +#include "ecmascript/mem/c_containers.h" +#include "ecmascript/mem/visitor.h" +#include "ecmascript/platform/mutex.h" +#include "ecmascript/taskpool/task.h" +#include "ecmascript/pgo_profiler/pgo_utils.h" +#include "ecmascript/pgo_profiler/types/pgo_profile_type.h" namespace panda::ecmascript { +class ProfileTypeInfo; +class JSFunction; + +namespace pgo { +class PGORecordDetailInfos; + +enum class SampleMode : uint8_t { + HOTNESS_MODE, + CALL_MODE, +}; + class PGOProfiler { public: NO_COPY_SEMANTIC(PGOProfiler); NO_MOVE_SEMANTIC(PGOProfiler); - void ProfileCall(JSTaggedType value, SampleMode mode = SampleMode::CALL_MODE); - void ProfileOpType(JSTaggedType func, int32_t offset, uint32_t type); - void ProfileDefineClass(JSThread *thread, JSTaggedType func, int32_t offset, JSTaggedType ctor); - void ProfileObjLayout(JSThread *thread, JSTaggedType func, int32_t offset, JSTaggedType object, bool store); + static ProfileType GetRecordProfileType(ApEntityId abcId, ApEntityId recordId); + void ProfileCall(JSTaggedType callTarget, SampleMode mode = SampleMode::CALL_MODE, int32_t incCount = 1); + void ProfileCreateObject(JSTaggedType object, ApEntityId abcId, int32_t traceId); + void ProfileDefineClass(JSTaggedType ctor); + + void SetSaveTimestamp(std::chrono::system_clock::time_point timestamp) + { + saveTimestamp_ = timestamp; + } + + void PGOPreDump(JSTaggedType func); + void PGODump(JSTaggedType func); + + void PausePGODump(); + void ResumePGODump(); + void WaitPGODumpFinish(); + + void HandlePGOPreDump(); + void HandlePGODump(); + + void ProcessReferences(const WeakRootVisitor &visitor); + void Iterate(const RootVisitor &visitor); + + void UpdateTrackInfo(JSTaggedValue trackInfoVal, ElementsKind newKind); + ProfileType InsertLiteralTraceId(JSTaggedType hclass, ApEntityId abcId, int32_t traceId); + ProfileType InsertTraceId(JSTaggedType hclass, ProfileType traceType); private: - static constexpr uint32_t MERGED_EVERY_COUNT = 20; + static constexpr uint32_t MERGED_EVERY_COUNT = 50; + static constexpr auto MERGED_MIN_INTERVAL = std::chrono::milliseconds(1000); + enum class BCType : uint8_t { + STORE, + LOAD, + }; - PGOProfiler([[maybe_unused]] EcmaVM *vm, bool isEnable) : isEnable_(isEnable) - { - if (isEnable_) { - recordInfos_ = std::make_unique(0); - } + enum class State : uint8_t { + STOP, + PAUSE, + START, }; - virtual ~PGOProfiler() - { - Reset(false); - } + void ProfileByteCode(ApEntityId abcId, const CString &recordName, JSTaggedValue value); - void Reset(bool isEnable) - { - isEnable_ = isEnable; - methodCount_ = 0; - if (recordInfos_) { - recordInfos_->Clear(); - } else { - if (isEnable_) { - recordInfos_ = std::make_unique(0); - } + void DumpICByName(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, uint32_t slotId, + ProfileTypeInfo *profileTypeInfo, BCType type); + void DumpICByValue(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, + uint32_t slotId, ProfileTypeInfo *profileTypeInfo, BCType type); + + void DumpICByNameWithPoly(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, + JSTaggedValue cacheValue, BCType type); + void DumpICByValueWithPoly(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, + JSTaggedValue cacheValue, BCType type); + + void DumpICByNameWithHandler(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, + JSHClass *hclass, JSTaggedValue secondValue, BCType type); + void DumpICByValueWithHandler(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, + JSHClass *hclass, JSTaggedValue secondValue, BCType type); + + void DumpOpType(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, uint32_t slotId, + ProfileTypeInfo *profileTypeInfo); + void DumpDefineClass(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, + uint32_t slotId, ProfileTypeInfo *profileTypeInfo); + void DumpCreateObject(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, + uint32_t slotId, ProfileTypeInfo *profileTypeInfo, int32_t traceId); + void DumpCall(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, uint32_t slotId, + ProfileTypeInfo *profileTypeInfo); + + void AddObjectInfo(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, + JSHClass *hclass, PGOObjKind kind); + bool AddObjectInfoByTraceId(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, + JSHClass *hclass, ProfileType::Kind classKind, PGOObjKind kind); + + JSTaggedValue PopFromProfileQueue(); + + class PGOProfilerTask : public Task { + public: + explicit PGOProfilerTask(PGOProfiler *profiler, int32_t id) : Task(id), profiler_(profiler) {}; + virtual ~PGOProfilerTask() override = default; + + bool Run([[maybe_unused]] uint32_t threadIndex) override + { + profiler_->HandlePGODump(); + return true; } - } - bool isEnable_ {false}; - uint32_t methodCount_ {0}; + NO_COPY_SEMANTIC(PGOProfilerTask); + NO_MOVE_SEMANTIC(PGOProfilerTask); + private: + PGOProfiler *profiler_; + }; + + PGOProfiler(EcmaVM *vm, bool isEnable); + + virtual ~PGOProfiler(); + + static ApEntityId GetMethodAbcId(JSFunction *jsFunction); + ApEntityId GetRecordId(const CString &recordName); + ProfileType GetRecordProfileType(JSFunction *jsFunction, const CString &recordName); + ProfileType GetRecordProfileType(ApEntityId abcId, const CString &recordName); + + void Reset(bool isEnable); + + EcmaVM *vm_ { nullptr }; + bool isEnable_ { false }; + State state_ { State::STOP }; + uint32_t methodCount_ { 0 }; + std::chrono::system_clock::time_point saveTimestamp_; + Mutex mutex_; + ConditionVariable condition_; + CList dumpWorkList_; + CSet preDumpWorkList_; + CMap tracedProfiles_; std::unique_ptr recordInfos_; friend class PGOProfilerManager; }; +} // namespace pgo } // namespace panda::ecmascript #endif // ECMASCRIPT_PGO_PROFILER_H diff --git a/ecmascript/pgo_profiler/pgo_profiler_decoder.cpp b/ecmascript/pgo_profiler/pgo_profiler_decoder.cpp index 15a1c9d32c3e9733340ff80dfd7a38eb6d8cdd70..dfec72140f32ecb04f5908b06e8d16fd214820b0 100644 --- a/ecmascript/pgo_profiler/pgo_profiler_decoder.cpp +++ b/ecmascript/pgo_profiler/pgo_profiler_decoder.cpp @@ -14,13 +14,15 @@ */ #include "ecmascript/pgo_profiler/pgo_profiler_decoder.h" +#include +#include "ecmascript/base/file_header.h" #include "ecmascript/jspandafile/method_literal.h" #include "ecmascript/log_wrapper.h" #include "ecmascript/pgo_profiler/pgo_profiler_info.h" #include "ecmascript/platform/file.h" -namespace panda::ecmascript { +namespace panda::ecmascript::pgo { bool PGOProfilerDecoder::Load() { if (isLoaded_) { @@ -31,15 +33,22 @@ bool PGOProfilerDecoder::Load() } void *addr = fileMapAddr_.GetOriginAddr(); - if (!PGOProfilerHeader::ParseFromBinary(addr, &header_)) { + if (!PGOProfilerHeader::ParseFromBinary(addr, fileMapAddr_.GetSize(), &header_)) { UnLoadAPBinaryFile(); LOG_ECMA(ERROR) << "Parse profiler header failed"; return false; } pandaFileInfos_.ParseFromBinary(addr, header_->GetPandaInfoSection()); + if (!recordSimpleInfos_) { recordSimpleInfos_ = std::make_unique(hotnessThreshold_); } + if (!abcFilePool_) { + abcFilePool_ = std::make_unique(); + } + if (header_->SupportProfileTypeWithAbcId()) { + PGOFileSectionInterface::ParseSectionFromBinary(addr, header_, *abcFilePool_->GetPool()); + } recordSimpleInfos_->ParseFromBinary(addr, header_); UnLoadAPBinaryFile(); @@ -52,12 +61,11 @@ bool PGOProfilerDecoder::Verify(uint32_t checksum) if (!isLoaded_) { return false; } - if (header_->SupportMethodChecksum()) { + if (IsMethodMatchEnabled()) { LOG_ECMA(INFO) << "Skip verify file checksum, use method checksum instead."; - enableMethodMatch_ = true; isVerifySuccess_ = true; } else { - isVerifySuccess_ = pandaFileInfos_.CheckSum(checksum); + isVerifySuccess_ = pandaFileInfos_.Checksum(checksum); } return isVerifySuccess_; } @@ -81,19 +89,28 @@ bool PGOProfilerDecoder::LoadFull() if (isLoaded_) { Clear(); } - if (!LoadAPBinaryFile()) { + // profiler dump tools may write data to memory when merge ap files. + if (!LoadAPBinaryFile(PAGE_PROT_READWRITE)) { return false; } void *addr = fileMapAddr_.GetOriginAddr(); - if (!PGOProfilerHeader::ParseFromBinary(addr, &header_)) { + if (!PGOProfilerHeader::ParseFromBinary(addr, fileMapAddr_.GetSize(), &header_)) { UnLoadAPBinaryFile(); LOG_ECMA(ERROR) << "Parse profiler header failed"; return false; } pandaFileInfos_.ParseFromBinary(addr, header_->GetPandaInfoSection()); if (!recordDetailInfos_) { - recordDetailInfos_ = std::make_unique(hotnessThreshold_); + recordDetailInfos_ = std::make_shared(hotnessThreshold_); + } + + if (!abcFilePool_) { + abcFilePool_ = std::make_unique(); + } + + if (header_->SupportProfileTypeWithAbcId()) { + PGOFileSectionInterface::ParseSectionFromBinary(addr, header_, *abcFilePool_->GetPool()); } recordDetailInfos_->ParseFromBinary(addr, header_); @@ -121,10 +138,11 @@ bool PGOProfilerDecoder::SaveAPTextFile(const std::string &outPath) } pandaFileInfos_.ProcessToText(fileStream); recordDetailInfos_->ProcessToText(fileStream); + abcFilePool_->GetPool()->ProcessToText(fileStream); return true; } -bool PGOProfilerDecoder::LoadAPBinaryFile() +bool PGOProfilerDecoder::LoadAPBinaryFile(int prot) { std::string realPath; if (!RealPath(inPath_, realPath)) { @@ -137,7 +155,7 @@ bool PGOProfilerDecoder::LoadAPBinaryFile() return false; } LOG_ECMA(INFO) << "Load profiler from file:" << realPath; - fileMapAddr_ = FileMap(realPath.c_str(), FILE_RDONLY, PAGE_PROT_READ); + fileMapAddr_ = FileMap(realPath.c_str(), FILE_RDONLY, prot); if (fileMapAddr_.GetOriginAddr() == nullptr) { LOG_ECMA(ERROR) << "File mmap failed"; return false; @@ -158,10 +176,12 @@ void PGOProfilerDecoder::Clear() if (isLoaded_) { UnLoadAPBinaryFile(); isVerifySuccess_ = true; - enableMethodMatch_ = false; hotnessThreshold_ = 0; PGOProfilerHeader::Destroy(&header_); pandaFileInfos_.Clear(); + if (abcFilePool_) { + abcFilePool_->Clear(); + } if (recordDetailInfos_) { recordDetailInfos_->Clear(); } @@ -172,8 +192,7 @@ void PGOProfilerDecoder::Clear() } } -bool PGOProfilerDecoder::Match(const JSPandaFile *jsPandaFile, const CString &recordName, - const MethodLiteral *methodLiteral) +bool PGOProfilerDecoder::Match(const CString &recordName, PGOMethodId methodId) { if (!isLoaded_) { return true; @@ -181,28 +200,53 @@ bool PGOProfilerDecoder::Match(const JSPandaFile *jsPandaFile, const CString &re if (!isVerifySuccess_) { return false; } - EntityId methodId = methodLiteral->GetMethodId(); - EntityId pgoMethodId(methodId); - if (IsMethodMatchEnabled()) { - if (jsPandaFile == nullptr) { - return false; - } - const char *methodName = MethodLiteral::GetMethodName(jsPandaFile, methodId); - uint32_t checksum = - PGOMethodInfo::CalcChecksum(methodName, methodLiteral->GetBytecodeArray(), - MethodLiteral::GetCodeSize(jsPandaFile, methodLiteral->GetMethodId())); - if (!GetMethodIdInPGO(recordName, checksum, methodName, pgoMethodId)) { - return false; - } - } - return recordSimpleInfos_->Match(recordName, pgoMethodId); + return recordSimpleInfos_->Match(recordName, EntityId(methodId)); } -bool PGOProfilerDecoder::GetHClassLayoutDesc(PGOSampleType classType, PGOHClassLayoutDesc **desc) const +bool PGOProfilerDecoder::GetHClassLayoutDesc(PGOSampleType profileType, PGOHClassLayoutDesc **desc) const { if (!isLoaded_ || !isVerifySuccess_) { return false; } - return recordSimpleInfos_->GetHClassLayoutDesc(classType, desc); + return recordSimpleInfos_->GetHClassLayoutDesc(profileType, desc); +} + +void PGOProfilerDecoder::GetMismatchResult(uint32_t &totalMethodCount, uint32_t &mismatchMethodCount, + std::set> &mismatchMethodSet) const +{ + if (!isLoaded_ || !isVerifySuccess_) { + return; + } + return recordSimpleInfos_->GetMismatchResult(totalMethodCount, mismatchMethodCount, mismatchMethodSet); +} + +bool PGOProfilerDecoder::InitMergeData() +{ + ASSERT(!isLoaded_); + if (!recordSimpleInfos_) { + recordSimpleInfos_ = std::make_unique(hotnessThreshold_); + } + if (!header_) { + // For merge scene, we only care about the ap capability which is in the version field. + PGOProfilerHeader::Build(&header_, sizeof(PGOProfilerHeader)); + memset_s(header_, sizeof(PGOProfilerHeader), 0, sizeof(PGOProfilerHeader)); + } + isLoaded_ = true; + isVerifySuccess_ = true; + return true; +} + +void PGOProfilerDecoder::Merge(const PGOProfilerDecoder &decoder) +{ + if (!isLoaded_ || !isVerifySuccess_) { + return; + } + // For merge scene, we chose the highest version from input ap files + if (!(header_->CompatibleVerify(decoder.header_->GetVersion()))) { + // For merge scene, we only care about the ap capability which is in the version field. + memcpy_s(header_, sizeof(base::FileHeaderBase), decoder.header_, sizeof(base::FileHeaderBase)); + } + pandaFileInfos_.Merge(decoder.GetPandaFileInfos()); + recordSimpleInfos_->Merge(decoder.GetRecordSimpleInfos()); } -} // namespace panda::ecmascript +} // namespace panda::ecmascript::pgo diff --git a/ecmascript/pgo_profiler/pgo_profiler_decoder.h b/ecmascript/pgo_profiler/pgo_profiler_decoder.h index 7012c12e93da1a78bd8dfcc5f5dfda8b19169d3b..7984ac8fcc451d0db715bb41957acbc76e1f402c 100644 --- a/ecmascript/pgo_profiler/pgo_profiler_decoder.h +++ b/ecmascript/pgo_profiler/pgo_profiler_decoder.h @@ -20,23 +20,25 @@ #include "ecmascript/log.h" #include "ecmascript/log_wrapper.h" #include "ecmascript/pgo_profiler/pgo_profiler_info.h" -#include "ecmascript/pgo_profiler/pgo_profiler_type.h" +#include "ecmascript/pgo_profiler/types/pgo_profiler_type.h" #include "ecmascript/platform/map.h" -namespace panda::ecmascript { +namespace panda::ecmascript::pgo { class PGOProfilerDecoder { public: PGOProfilerDecoder() = default; PGOProfilerDecoder(const std::string &inPath, uint32_t hotnessThreshold) : inPath_(inPath), hotnessThreshold_(hotnessThreshold) {} - virtual ~PGOProfilerDecoder() = default; + virtual ~PGOProfilerDecoder() + { + Clear(); + }; NO_COPY_SEMANTIC(PGOProfilerDecoder); NO_MOVE_SEMANTIC(PGOProfilerDecoder); - bool PUBLIC_API Match(const JSPandaFile *jsPandaFile, const CString &recordName, - const MethodLiteral *methodLiteral); + bool PUBLIC_API Match(const CString &recordName, PGOMethodId methodId); bool PUBLIC_API LoadAndVerify(uint32_t checksum); bool PUBLIC_API LoadFull(); @@ -44,6 +46,20 @@ public: bool PUBLIC_API SaveAPTextFile(const std::string &outPath); + void Merge(const PGOProfilerDecoder &decoder); + + bool InitMergeData(); + + const std::string& GetInPath() const + { + return inPath_; + } + + uint32_t GetHotnessThreshold() const + { + return hotnessThreshold_; + } + template void Update(Callback callback) { @@ -69,67 +85,77 @@ public: if (!isLoaded_ || !isVerifySuccess_) { return; } - EntityId methodId(methodLiteral->GetMethodId()); - EntityId pgoMethodId(methodId); + const auto *methodName = MethodLiteral::GetMethodName(jsPandaFile, methodLiteral->GetMethodId()); if (IsMethodMatchEnabled()) { auto checksum = - PGOMethodInfo::CalcChecksum(MethodLiteral::GetMethodName(jsPandaFile, methodLiteral->GetMethodId()), - methodLiteral->GetBytecodeArray(), + PGOMethodInfo::CalcChecksum(methodName, methodLiteral->GetBytecodeArray(), MethodLiteral::GetCodeSize(jsPandaFile, methodLiteral->GetMethodId())); - const auto *methodName = MethodLiteral::GetMethodName(jsPandaFile, methodId); - if (!GetMethodIdInPGO(recordName, checksum, methodName, pgoMethodId)) { - return; - } + + return recordSimpleInfos_->GetTypeInfo(recordName, methodName, checksum, callback); } - recordSimpleInfos_->GetTypeInfo(recordName, pgoMethodId, callback); + recordSimpleInfos_->GetTypeInfo(recordName, methodName, callback); } - bool GetMethodIdInPGO(const CString &recordName, uint32_t checksum, const char *methodName, - EntityId &methodId) const + void MatchAndMarkMethod(const CString &recordName, const char *methodName, EntityId methodId) { - bool hasRecord = true; if (!isLoaded_ || !isVerifySuccess_) { - hasRecord = false; - } - if (hasRecord) { - hasRecord = recordSimpleInfos_->GetMethodIdInPGO(recordName, checksum, methodName, methodId); - } - if (!hasRecord) { - LOG_COMPILER(DEBUG) << "No matched method found in PGO. recordName: " << recordName - << ", methodName: " << methodName << ", checksum: " << std::hex << checksum; + return; } - return hasRecord; + recordSimpleInfos_->MatchAndMarkMethod(recordName, methodName, methodId); } + void GetMismatchResult(uint32_t &totalMethodCount, uint32_t &mismatchMethodCount, + std::set> &mismatchMethodSet) const; + bool IsMethodMatchEnabled() const { - return enableMethodMatch_; + return header_->SupportMethodChecksum(); } - bool GetHClassLayoutDesc(PGOSampleType classType, PGOHClassLayoutDesc **desc) const; + bool GetHClassLayoutDesc(PGOSampleType profileType, PGOHClassLayoutDesc **desc) const; bool IsLoaded() const { return isLoaded_; } + PGORecordDetailInfos &GetRecordDetailInfos() const + { + return *recordDetailInfos_; + } + + std::shared_ptr GetRecordDetailInfosPtr() const + { + return recordDetailInfos_; + } + + PGORecordSimpleInfos &GetRecordSimpleInfos() const + { + return *recordSimpleInfos_; + } + + const PGOPandaFileInfos &GetPandaFileInfos() const + { + return pandaFileInfos_; + } + private: bool Load(); bool Verify(uint32_t checksum); - bool LoadAPBinaryFile(); + bool LoadAPBinaryFile(int prot = PAGE_PROT_READ); void UnLoadAPBinaryFile(); bool isLoaded_ {false}; bool isVerifySuccess_ {false}; - bool enableMethodMatch_ {false}; std::string inPath_; uint32_t hotnessThreshold_ {0}; PGOProfilerHeader *header_ {nullptr}; PGOPandaFileInfos pandaFileInfos_; - std::unique_ptr recordDetailInfos_; + std::shared_ptr abcFilePool_; + std::shared_ptr recordDetailInfos_; std::unique_ptr recordSimpleInfos_; MemMap fileMapAddr_; }; -} // namespace panda::ecmascript +} // namespace panda::ecmascript::pgo #endif // ECMASCRIPT_PGO_PROFILE_DECODER_H diff --git a/ecmascript/pgo_profiler/pgo_profiler_encoder.cpp b/ecmascript/pgo_profiler/pgo_profiler_encoder.cpp index 2f7659b3c765795359739cfdb9a8ca7bcebc4ca9..59104289b0812f3b10905438a49ea1abe5b507f8 100644 --- a/ecmascript/pgo_profiler/pgo_profiler_encoder.cpp +++ b/ecmascript/pgo_profiler/pgo_profiler_encoder.cpp @@ -13,12 +13,25 @@ * limitations under the License. */ +#include +#include +#include +#include +#include + +#include "ecmascript/log_wrapper.h" +#include "ecmascript/pgo_profiler/ap_file/pgo_file_info.h" +#include "ecmascript/pgo_profiler/pgo_profiler_decoder.h" #include "ecmascript/pgo_profiler/pgo_profiler_encoder.h" +#include "ecmascript/pgo_profiler/pgo_utils.h" #include "ecmascript/platform/file.h" +#include "os/mutex.h" -namespace panda::ecmascript { -static const std::string PROFILE_FILE_NAME = "/modules.ap"; +namespace panda::ecmascript::pgo { +static const std::string AP_SUFFIX = ".ap"; +static const std::string PROFILE_FILE_NAME = "/modules" + AP_SUFFIX; +static const std::string RUNTIME_AP_PREFIX = "/rt_"; void PGOProfilerEncoder::Destroy() { if (!isInitialized_) { @@ -31,32 +44,63 @@ void PGOProfilerEncoder::Destroy() isInitialized_ = false; } +bool PGOProfilerEncoder::ResetOutPathByModuleName(const std::string &moduleName) +{ + LockHolder lock(mutex_); + // only first assign takes effect + if (!moduleName_.empty() || moduleName.empty()) { + return false; + } + moduleName_ = moduleName; + return ResetOutPath(RUNTIME_AP_PREFIX + moduleName_ + AP_SUFFIX); +} + +bool PGOProfilerEncoder::ResetOutPath(const std::string &profileFileName) +{ + if (!RealPath(outDir_, realOutPath_, false)) { + return false; + } + + if (realOutPath_.compare(realOutPath_.length() - AP_SUFFIX.length(), AP_SUFFIX.length(), AP_SUFFIX)) { + realOutPath_ += profileFileName; + } + LOG_ECMA(INFO) << "Save profiler to file:" << realOutPath_; + return true; +} + bool PGOProfilerEncoder::InitializeData() { if (!isInitialized_) { - if (!RealPath(outDir_, realOutPath_, false)) { + if (!ResetOutPath(PROFILE_FILE_NAME)) { return false; } - - static const std::string endString = ".ap"; - if (realOutPath_.compare(realOutPath_.length() - endString.length(), endString.length(), endString)) { - realOutPath_ += PROFILE_FILE_NAME; - } - LOG_ECMA(INFO) << "Save profiler to file:" << realOutPath_; PGOProfilerHeader::Build(&header_, PGOProfilerHeader::LastSize()); pandaFileInfos_ = std::make_unique(); - globalRecordInfos_ = std::make_unique(hotnessThreshold_); + abcFilePool_ = std::make_unique(); + globalRecordInfos_ = std::make_shared(hotnessThreshold_); isInitialized_ = true; } return true; } -void PGOProfilerEncoder::SamplePandaFileInfo(uint32_t checksum) +void PGOProfilerEncoder::SamplePandaFileInfo(uint32_t checksum, const CString &abcName) { if (!isInitialized_) { return; } + WriteLockHolder lock(rwLock_); pandaFileInfos_->Sample(checksum); + ApEntityId entryId(0); + abcFilePool_->TryAdd(abcName, entryId); +} + +bool PGOProfilerEncoder::GetPandaFileId(const CString &abcName, ApEntityId &entryId) +{ + if (!isInitialized_) { + return false; + } + ReadLockHolder lock(rwLock_); + return abcFilePool_->GetEntryId(abcName, entryId); } void PGOProfilerEncoder::Merge(const PGORecordDetailInfos &recordInfos) @@ -64,33 +108,107 @@ void PGOProfilerEncoder::Merge(const PGORecordDetailInfos &recordInfos) if (!isInitialized_) { return; } - os::memory::LockHolder lock(mutex_); + LockHolder lock(mutex_); globalRecordInfos_->Merge(recordInfos); } +void PGOProfilerEncoder::Merge(const PGOPandaFileInfos &pandaFileInfos) +{ + return pandaFileInfos_->Merge(pandaFileInfos); +} + +void PGOProfilerEncoder::Merge(const PGOProfilerEncoder &encoder) +{ + Merge(*encoder.pandaFileInfos_); + Merge(*encoder.globalRecordInfos_); +} + +bool PGOProfilerEncoder::VerifyPandaFileMatched(const PGOPandaFileInfos &pandaFileInfos, const std::string &base, + const std::string &incoming) const +{ + return pandaFileInfos_->VerifyChecksum(pandaFileInfos, base, incoming); +} + bool PGOProfilerEncoder::Save() { if (!isInitialized_) { return false; } - os::memory::LockHolder lock(mutex_); - return SaveProfiler(); + LockHolder lock(mutex_); + return InternalSave(); } -bool PGOProfilerEncoder::SaveProfiler(const SaveTask *task) +bool PGOProfilerEncoder::SaveAndRename(const SaveTask *task) { - std::ofstream fileStream(realOutPath_.c_str()); + static const char *tempSuffix = ".tmp"; + auto tmpOutPath = realOutPath_ + "." + std::to_string(getpid()) + tempSuffix; + std::fstream fileStream(tmpOutPath.c_str(), + std::fstream::binary | std::fstream::out | std::fstream::in | std::fstream::trunc); if (!fileStream.is_open()) { - LOG_ECMA(ERROR) << "The file path(" << realOutPath_ << ") open failure!"; + LOG_ECMA(ERROR) << "The file path(" << tmpOutPath << ") open failure! errno: " << errno; return false; } pandaFileInfos_->ProcessToBinary(fileStream, header_->GetPandaInfoSection()); globalRecordInfos_->ProcessToBinary(task, fileStream, header_); + { + ReadLockHolder lock(rwLock_); + PGOFileSectionInterface::ProcessSectionToBinary(fileStream, header_, *abcFilePool_->GetPool()); + } + header_->SetFileSize(static_cast(fileStream.tellp())); header_->ProcessToBinary(fileStream); + if (header_->SupportFileConsistency()) { + AddChecksum(fileStream); + } fileStream.close(); + if (task && task->IsTerminate()) { + LOG_ECMA(DEBUG) << "ProcessProfile: task is already terminate"; + return false; + } + if (FileExist(realOutPath_.c_str()) && remove(realOutPath_.c_str())) { + LOG_ECMA(ERROR) << "Remove " << realOutPath_ << " failure!, errno: " << errno; + return false; + } + if (rename(tmpOutPath.c_str(), realOutPath_.c_str())) { + LOG_ECMA(ERROR) << "Rename " << tmpOutPath << " --> " << realOutPath_ << " failure!, errno: " << errno; + return false; + } return true; } +bool PGOProfilerEncoder::InternalSave(const SaveTask *task) +{ + ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "PGOProfilerEncoder::InternalSave"); + if (!isInitialized_) { + return false; + } + return SaveAndRename(task); +} + +void PGOProfilerEncoder::AddChecksum(std::fstream &fileStream) +{ + static constexpr uint32_t KILO_BYTES = 1024; + static constexpr uint32_t STEP_IN_KB = 256; + static constexpr uint32_t STEP_SIZE = STEP_IN_KB * KILO_BYTES; + uint32_t size = static_cast(fileStream.seekp(0, std::fstream::end).tellp()); + std::unique_ptr> buffer = std::make_unique>(STEP_SIZE); + // first, calculate the version field's checksum. + fileStream.seekg(PGOProfilerHeader::MAGIC_SIZE, std::fstream::beg) + .read(reinterpret_cast(buffer->data()), PGOProfilerHeader::VERSION_SIZE); + uint32_t checksum = adler32(0, reinterpret_cast(buffer->data()), PGOProfilerHeader::VERSION_SIZE); + // second, calculate the checksum for remaining content(exclude checksum field). + uint32_t remainingSize = size - PGOProfilerHeader::CHECKSUM_END_OFFSET; + fileStream.seekg(PGOProfilerHeader::CHECKSUM_END_OFFSET); + while (remainingSize > 0) { + uint32_t readSize = std::min(STEP_SIZE, remainingSize); + remainingSize = remainingSize - readSize; + fileStream.read(reinterpret_cast(buffer->data()), readSize); + checksum = adler32(checksum, reinterpret_cast(buffer->data()), readSize); + } + // third, write the checksum back to the checksum field in the output stream. + fileStream.seekp(PGOProfilerHeader::MAGIC_SIZE + PGOProfilerHeader::VERSION_SIZE, std::fstream::beg); + fileStream.write(reinterpret_cast(&checksum), sizeof(checksum)); +} + void PGOProfilerEncoder::TerminateSaveTask() { if (!isInitialized_) { @@ -116,8 +234,8 @@ void PGOProfilerEncoder::StartSaveTask(const SaveTask *task) LOG_ECMA(ERROR) << "StartSaveTask: task is already terminate"; return; } - os::memory::LockHolder lock(mutex_); - SaveProfiler(task); + LockHolder lock(mutex_); + InternalSave(task); } bool PGOProfilerEncoder::LoadAPTextFile(const std::string &inPath) @@ -151,4 +269,4 @@ bool PGOProfilerEncoder::LoadAPTextFile(const std::string &inPath) return true; } -} // namespace panda::ecmascript +} // namespace panda::ecmascript::pgo diff --git a/ecmascript/pgo_profiler/pgo_profiler_encoder.h b/ecmascript/pgo_profiler/pgo_profiler_encoder.h index cdf0fea0d0a10264a3ac63bfe956186101e3728f..b8b376c549e33ef51306feafde7ee3a55f6735f5 100644 --- a/ecmascript/pgo_profiler/pgo_profiler_encoder.h +++ b/ecmascript/pgo_profiler/pgo_profiler_encoder.h @@ -16,18 +16,25 @@ #ifndef ECMASCRIPT_PGO_PROFILER_ENCODER_H #define ECMASCRIPT_PGO_PROFILER_ENCODER_H +#include + #include "ecmascript/pgo_profiler/pgo_profiler_info.h" #include "macros.h" -namespace panda::ecmascript { +namespace panda::ecmascript::pgo { +class PGOProfilerDecoder; class PGOProfilerEncoder { public: - PGOProfilerEncoder(const std::string &outDir, uint32_t hotnessThreshold) - : outDir_(outDir), hotnessThreshold_(hotnessThreshold) {} + enum ApGenMode { OVERWRITE }; + + PGOProfilerEncoder(const std::string &outDir, uint32_t hotnessThreshold, ApGenMode mode) + : outDir_(outDir), hotnessThreshold_(hotnessThreshold), mode_(mode) {} NO_COPY_SEMANTIC(PGOProfilerEncoder); NO_MOVE_SEMANTIC(PGOProfilerEncoder); + static void AddChecksum(std::fstream& fileStream); + bool PUBLIC_API InitializeData(); void PUBLIC_API Destroy(); @@ -37,18 +44,31 @@ public: return isInitialized_; } - void SamplePandaFileInfo(uint32_t checksum); + void SamplePandaFileInfo(uint32_t checksum, const CString &abcName); + bool GetPandaFileId(const CString &abcName, ApEntityId &entryId); void Merge(const PGORecordDetailInfos &recordInfos); + void Merge(const PGOPandaFileInfos &pandaFileInfos); + void Merge(const PGOProfilerEncoder &encoder); + bool VerifyPandaFileMatched(const PGOPandaFileInfos &pandaFileInfos, const std::string &base, + const std::string &incoming) const; void TerminateSaveTask(); void PostSaveTask(); + void SetApGenMode(ApGenMode mode) + { + mode_ = mode; + } bool PUBLIC_API Save(); bool PUBLIC_API LoadAPTextFile(const std::string &inPath); + bool ResetOutPathByModuleName(const std::string &moduleName); + private: void StartSaveTask(const SaveTask *task); - bool SaveProfiler(const SaveTask *task = nullptr); + bool InternalSave(const SaveTask *task = nullptr); + bool SaveAndRename(const SaveTask *task = nullptr); + bool ResetOutPath(const std::string& profileFileName); bool isInitialized_ {false}; std::string outDir_; @@ -56,8 +76,12 @@ private: std::string realOutPath_; PGOProfilerHeader *header_ {nullptr}; std::unique_ptr pandaFileInfos_; - std::unique_ptr globalRecordInfos_; - os::memory::Mutex mutex_; + std::shared_ptr abcFilePool_; + std::shared_ptr globalRecordInfos_; + Mutex mutex_; + RWLock rwLock_; + std::string moduleName_; + ApGenMode mode_ {OVERWRITE}; friend SaveTask; }; @@ -82,5 +106,5 @@ public: private: PGOProfilerEncoder *encoder_; }; -} // namespace panda::ecmascript +} // namespace panda::ecmascript::pgo #endif // ECMASCRIPT_PGO_PROFILER_ENCODER_H diff --git a/ecmascript/pgo_profiler/pgo_profiler_info.cpp b/ecmascript/pgo_profiler/pgo_profiler_info.cpp index 47bb3cecefc3a47ed89349c21100c6dabac93b9b..249a2eea0b470a793f7bea7f5919b1d4d016ac64 100644 --- a/ecmascript/pgo_profiler/pgo_profiler_info.cpp +++ b/ecmascript/pgo_profiler/pgo_profiler_info.cpp @@ -15,101 +15,83 @@ #include "ecmascript/pgo_profiler/pgo_profiler_info.h" #include +#include #include +#include #include "ecmascript/base/bit_helper.h" +#include "ecmascript/base/file_header.h" #include "ecmascript/js_function.h" +#include "ecmascript/jspandafile/method_literal.h" +#include "ecmascript/log_wrapper.h" +#include "ecmascript/mem/c_string.h" +#include "ecmascript/pgo_profiler/ap_file/pgo_file_info.h" +#include "ecmascript/pgo_profiler/ap_file/pgo_method_type_set.h" +#include "ecmascript/pgo_profiler/ap_file/pgo_profile_type_pool.h" +#include "ecmascript/pgo_profiler/ap_file/pgo_record_pool.h" +#include "ecmascript/pgo_profiler/pgo_context.h" #include "ecmascript/pgo_profiler/pgo_profiler_encoder.h" +#include "ecmascript/pgo_profiler/pgo_profiler_manager.h" +#include "ecmascript/pgo_profiler/pgo_utils.h" +#include "ecmascript/pgo_profiler/types/pgo_profile_type.h" +#include "ecmascript/pgo_profiler/types/pgo_profiler_type.h" #include "macros.h" +#include "securec.h" -namespace panda::ecmascript { -static const std::string ELEMENT_SEPARATOR = "/"; -static const std::string BLOCK_SEPARATOR = ","; -static const std::string TYPE_SEPARATOR = "|"; -static const std::string BLOCK_START = ":"; -static const std::string ARRAY_START = "["; -static const std::string ARRAY_END = "]"; -static const std::string NEW_LINE = "\n"; -static const std::string SPACE = " "; -static const std::string BLOCK_AND_ARRAY_START = BLOCK_START + SPACE + ARRAY_START + SPACE; -static const std::string VERSION_HEADER = "Profiler Version" + BLOCK_START + SPACE; -static const std::string PANDA_FILE_INFO_HEADER = "Panda file sumcheck list" + BLOCK_AND_ARRAY_START; -static const uint32_t HEX_FORMAT_WIDTH_FOR_32BITS = 10; // for example, 0xffffffff is 10 characters - -bool PGOProfilerHeader::ParseFromBinary(void *buffer, PGOProfilerHeader **header) +namespace panda::ecmascript::pgo { +namespace { +bool ParseSectionsFromBinary(const std::list> §ionList, void *buffer, + PGOProfilerHeader const *header) { - auto in = reinterpret_cast(buffer); - if (in->Verify()) { - size_t desSize = in->Size(); - if (desSize > LastSize()) { - LOG_ECMA(ERROR) << "header size error, expected size is less than " << LastSize() << ", but got " - << desSize; - return false; - } - Build(header, desSize); - if (memcpy_s(*header, desSize, in, in->Size()) != EOK) { - UNREACHABLE(); + return std::all_of(sectionList.begin(), sectionList.end(), [&](const auto §ionWeak) { + auto section = sectionWeak.lock(); + if (section == nullptr) { + return true; } - return true; - } - return false; -} - -void PGOProfilerHeader::ProcessToBinary(std::ofstream &fileStream) const -{ - fileStream.seekp(0); - fileStream.write(reinterpret_cast(this), Size()); + return PGOFileSectionInterface::ParseSectionFromBinary(buffer, header, *section); + }); } +} // namespace -bool PGOProfilerHeader::ParseFromText(std::ifstream &stream) +using StringHelper = base::StringHelper; +void PGOPandaFileInfos::ParseFromBinary(void *buffer, SectionInfo *const info) { - std::string header; - if (std::getline(stream, header)) { - if (header.empty()) { - return false; - } - auto index = header.find(BLOCK_START); - if (index == std::string::npos) { - return false; - } - auto version = header.substr(index + 1); - if (!InternalSetVersion(version)) { - return false; - } - if (!Verify()) { - return false; - } - return true; + void *addr = reinterpret_cast(reinterpret_cast(buffer) + info->offset_); + for (uint32_t i = 0; i < info->number_; i++) { + fileInfos_.emplace(*base::ReadBufferInSize(&addr)); } - return false; + LOG_ECMA(DEBUG) << "Profiler panda file count:" << info->number_; } -bool PGOProfilerHeader::ProcessToText(std::ofstream &stream) const +void PGOPandaFileInfos::ProcessToBinary(std::fstream &fileStream, SectionInfo *info) const { - if (!Verify()) { - return false; + fileStream.seekp(info->offset_); + info->number_ = fileInfos_.size(); + for (auto localInfo : fileInfos_) { + fileStream.write(reinterpret_cast(&localInfo), localInfo.Size()); } - stream << VERSION_HEADER << InternalGetVersion() << NEW_LINE; - return true; + info->size_ = static_cast(fileStream.tellp()) - info->offset_; } -void PGOPandaFileInfos::ParseFromBinary(void *buffer, SectionInfo *const info) +void PGOPandaFileInfos::Merge(const PGOPandaFileInfos &pandaFileInfos) { - void *addr = reinterpret_cast(reinterpret_cast(buffer) + info->offset_); - for (uint32_t i = 0; i < info->number_; i++) { - pandaFileInfos_.emplace(*base::ReadBufferInSize(&addr)); + for (const auto &info : pandaFileInfos.fileInfos_) { + fileInfos_.emplace(info.GetChecksum()); } - LOG_ECMA(DEBUG) << "Profiler panda file count:" << info->number_; } -void PGOPandaFileInfos::ProcessToBinary(std::ofstream &fileStream, SectionInfo *info) const +bool PGOPandaFileInfos::VerifyChecksum(const PGOPandaFileInfos &pandaFileInfos, const std::string &base, + const std::string &incoming) const { - fileStream.seekp(info->offset_); - info->number_ = pandaFileInfos_.size(); - for (auto localInfo : pandaFileInfos_) { - fileStream.write(reinterpret_cast(&localInfo), localInfo.Size()); + std::set unionChecksum; + set_union(fileInfos_.begin(), fileInfos_.end(), pandaFileInfos.fileInfos_.begin(), pandaFileInfos.fileInfos_.end(), + inserter(unionChecksum, unionChecksum.begin())); + if (!fileInfos_.empty() && unionChecksum.empty()) { + LOG_ECMA(ERROR) << "First AP file(" << base << ") and the incoming file(" << incoming + << ") do not come from the same abc file, skip merge the incoming file."; + return false; } - info->size_ = static_cast(fileStream.tellp()) - info->offset_; + return true; } bool PGOPandaFileInfos::ParseFromText(std::ifstream &stream) @@ -120,16 +102,16 @@ bool PGOPandaFileInfos::ParseFromText(std::ifstream &stream) continue; } - size_t start = pandaFileInfo.find_first_of(ARRAY_START); - size_t end = pandaFileInfo.find_last_of(ARRAY_END); + size_t start = pandaFileInfo.find_first_of(DumpUtils::ARRAY_START); + size_t end = pandaFileInfo.find_last_of(DumpUtils::ARRAY_END); if (start == std::string::npos || end == std::string::npos || start > end) { return false; } auto content = pandaFileInfo.substr(start + 1, end - (start + 1) - 1); - std::vector infos = base::StringHelper::SplitString(content, BLOCK_SEPARATOR); + std::vector infos = StringHelper::SplitString(content, DumpUtils::BLOCK_SEPARATOR); for (auto checksum : infos) { uint32_t result; - if (!base::StringHelper::StrToUInt32(checksum.c_str(), &result)) { + if (!StringHelper::StrToUInt32(checksum.c_str(), &result)) { LOG_ECMA(ERROR) << "checksum: " << checksum << " parse failed"; return false; } @@ -142,24 +124,24 @@ bool PGOPandaFileInfos::ParseFromText(std::ifstream &stream) void PGOPandaFileInfos::ProcessToText(std::ofstream &stream) const { - std::string pandaFileInfo = NEW_LINE + PANDA_FILE_INFO_HEADER; + std::string pandaFileInfo = DumpUtils::NEW_LINE + DumpUtils::PANDA_FILE_INFO_HEADER; bool isFirst = true; - for (auto &info : pandaFileInfos_) { + for (auto &info : fileInfos_) { if (!isFirst) { - pandaFileInfo += BLOCK_SEPARATOR + SPACE; + pandaFileInfo += DumpUtils::BLOCK_SEPARATOR + DumpUtils::SPACE; } else { isFirst = false; } pandaFileInfo += std::to_string(info.GetChecksum()); } - pandaFileInfo += (SPACE + ARRAY_END + NEW_LINE); + pandaFileInfo += (DumpUtils::SPACE + DumpUtils::ARRAY_END + DumpUtils::NEW_LINE); stream << pandaFileInfo; } -bool PGOPandaFileInfos::CheckSum(uint32_t checksum) const +bool PGOPandaFileInfos::Checksum(uint32_t checksum) const { - if (pandaFileInfos_.find(checksum) == pandaFileInfos_.end()) { + if (fileInfos_.find(checksum) == fileInfos_.end()) { LOG_ECMA(ERROR) << "Checksum verification failed. Please ensure that the .abc and .ap match."; return false; } @@ -169,333 +151,70 @@ bool PGOPandaFileInfos::CheckSum(uint32_t checksum) const void PGOMethodInfo::ProcessToText(std::string &text) const { text += std::to_string(GetMethodId().GetOffset()); - text += ELEMENT_SEPARATOR; + text += DumpUtils::ELEMENT_SEPARATOR; text += std::to_string(GetCount()); - text += ELEMENT_SEPARATOR; + text += DumpUtils::ELEMENT_SEPARATOR; text += GetSampleModeToString(); - text += ELEMENT_SEPARATOR; + text += DumpUtils::ELEMENT_SEPARATOR; text += GetMethodName(); } std::vector PGOMethodInfo::ParseFromText(const std::string &infoString) { - std::vector infoStrings = base::StringHelper::SplitString(infoString, ELEMENT_SEPARATOR); + std::vector infoStrings = StringHelper::SplitString(infoString, DumpUtils::ELEMENT_SEPARATOR); return infoStrings; } -void PGOMethodTypeSet::Merge(const PGOMethodTypeSet *info) +uint32_t PGOMethodInfo::CalcChecksum(const char *name, const uint8_t *byteCodeArray, uint32_t byteCodeLength) { - for (const auto &fromType : info->scalarOpTypeInfos_) { - auto iter = scalarOpTypeInfos_.find(fromType); - if (iter != scalarOpTypeInfos_.end()) { - const_cast(*iter).Merge(fromType); - } else { - scalarOpTypeInfos_.emplace(fromType); - } - } - for (const auto &fromType : info->rwScalarOpTypeInfos_) { - auto iter = rwScalarOpTypeInfos_.find(fromType); - if (iter != rwScalarOpTypeInfos_.end()) { - const_cast(*iter).Merge(fromType); - } else { - rwScalarOpTypeInfos_.emplace(fromType); - } + uint32_t checksum = 0; + if (byteCodeArray != nullptr) { + checksum = CalcOpCodeChecksum(byteCodeArray, byteCodeLength); } - for (const auto &fromType : info->objDefOpTypeInfos_) { - AddDefine(fromType.GetOffset(), fromType.GetType(), fromType.GetSuperType()); - } -} -void PGOMethodTypeSet::SkipFromBinary(void **buffer) -{ - uint32_t size = base::ReadBuffer(buffer, sizeof(uint32_t)); - for (uint32_t i = 0; i < size; i++) { - base::ReadBufferInSize(buffer); + if (name != nullptr) { + checksum = adler32(checksum, reinterpret_cast(name), strlen(name)); } + return checksum; } -bool PGOMethodTypeSet::ParseFromBinary(void **buffer) +uint32_t PGOMethodInfo::CalcOpCodeChecksum(const uint8_t *byteCodeArray, uint32_t byteCodeLength) { - uint32_t size = base::ReadBuffer(buffer, sizeof(uint32_t)); - for (uint32_t i = 0; i < size; i++) { - auto typeInfo = base::ReadBufferInSize(buffer); - if (typeInfo->GetInfoType() == InfoType::OP_TYPE) { - scalarOpTypeInfos_.emplace(*reinterpret_cast(typeInfo)); - } else if (typeInfo->GetInfoType() == InfoType::DEFINE_CLASS_TYPE) { - objDefOpTypeInfos_.emplace(*reinterpret_cast(typeInfo)); - } else if (typeInfo->GetInfoType() == InfoType::USE_HCLASS_TYPE) { - rwScalarOpTypeInfos_.emplace(*reinterpret_cast(typeInfo)); - } - } - return true; -} - -bool PGOMethodTypeSet::ProcessToBinary(std::stringstream &stream) const -{ - uint32_t number = 0; - std::stringstream methodStream; - for (auto typeInfo : scalarOpTypeInfos_) { - if (!typeInfo.GetType().IsNone()) { - methodStream.write(reinterpret_cast(&typeInfo), typeInfo.Size()); - number++; - } - } - for (auto typeInfo : rwScalarOpTypeInfos_) { - if (typeInfo.GetCount() != 0) { - methodStream.write(reinterpret_cast(&typeInfo), typeInfo.Size()); - number++; - } - } - - for (auto typeInfo : objDefOpTypeInfos_) { - methodStream.write(reinterpret_cast(&typeInfo), typeInfo.Size()); - number++; - } - - stream.write(reinterpret_cast(&number), sizeof(uint32_t)); - if (number > 0) { - stream << methodStream.rdbuf(); - return true; - } - return false; + uint32_t checksum = 0; + BytecodeInstruction bcIns(byteCodeArray); + auto bcInsLast = bcIns.JumpTo(byteCodeLength); + while (bcIns.GetAddress() != bcInsLast.GetAddress()) { + auto opCode = bcIns.GetOpcode(); + checksum = adler32(checksum, reinterpret_cast(&opCode), sizeof(decltype(opCode))); + bcIns = bcIns.GetNext(); + } + return checksum; } -bool PGOMethodTypeSet::ParseFromText(const std::string &typeString) -{ - std::vector typeInfoVector = base::StringHelper::SplitString(typeString, TYPE_SEPARATOR); - if (typeInfoVector.size() > 0) { - for (const auto &iter : typeInfoVector) { - std::vector typeStrings = base::StringHelper::SplitString(iter, BLOCK_START); - if (typeStrings.size() < METHOD_TYPE_INFO_COUNT) { - return false; - } - - uint32_t offset; - if (!base::StringHelper::StrToUInt32(typeStrings[METHOD_OFFSET_INDEX].c_str(), &offset)) { - return false; - } - uint32_t type; - if (!base::StringHelper::StrToUInt32(typeStrings[METHOD_TYPE_INDEX].c_str(), &type)) { - return false; - } - scalarOpTypeInfos_.emplace(offset, PGOSampleType(type)); - } - } - return true; -} - -void PGOMethodTypeSet::ProcessToText(std::string &text) const -{ - bool isFirst = true; - for (auto typeInfoIter : scalarOpTypeInfos_) { - if (typeInfoIter.GetType().IsNone()) { - continue; - } - if (isFirst) { - text += ARRAY_START + SPACE; - isFirst = false; - } else { - text += TYPE_SEPARATOR + SPACE; - } - text += std::to_string(typeInfoIter.GetOffset()); - text += BLOCK_START; - text += typeInfoIter.GetType().GetTypeString(); - } - for (auto rwScalarOpTypeInfoIter : rwScalarOpTypeInfos_) { - if (rwScalarOpTypeInfoIter.GetCount() == 0) { - continue; - } - if (isFirst) { - text += ARRAY_START + SPACE; - isFirst = false; - } else { - text += TYPE_SEPARATOR + SPACE; - } - rwScalarOpTypeInfoIter.ProcessToText(text); - } - for (const auto &defTypeInfoIter : objDefOpTypeInfos_) { - if (isFirst) { - text += ARRAY_START + SPACE; - isFirst = false; - } else { - text += TYPE_SEPARATOR + SPACE; - } - defTypeInfoIter.ProcessToText(text); - } - if (!isFirst) { - text += (SPACE + ARRAY_END); - } -} - -size_t PGOHClassLayoutDescInner::CaculateSize(const PGOHClassLayoutDesc &desc) -{ - if (desc.GetLayoutDesc().empty() && desc.GetPtLayoutDesc().empty() && desc.GetCtorLayoutDesc().empty()) { - return sizeof(PGOHClassLayoutDescInner); - } - size_t size = sizeof(PGOHClassLayoutDescInner) - sizeof(PGOLayoutDescInfo); - for (const auto &iter : desc.GetLayoutDesc()) { - auto key = iter.first; - if (key.size() > 0) { - size += static_cast(PGOLayoutDescInfo::Size(key.size())); - } - } - for (const auto &iter : desc.GetPtLayoutDesc()) { - auto key = iter.first; - if (key.size() > 0) { - size += static_cast(PGOLayoutDescInfo::Size(key.size())); - } - } - for (const auto &iter : desc.GetCtorLayoutDesc()) { - auto key = iter.first; - if (key.size() > 0) { - size += static_cast(PGOLayoutDescInfo::Size(key.size())); - } - } - return size; -} - -std::string PGOHClassLayoutDescInner::GetTypeString(const PGOHClassLayoutDesc &desc) -{ - std::string text; - text += desc.GetClassType().GetTypeString(); - text += TYPE_SEPARATOR + SPACE; - text += desc.GetSuperClassType().GetTypeString(); - text += BLOCK_AND_ARRAY_START; - bool isLayoutFirst = true; - for (const auto &layoutDesc : desc.GetLayoutDesc()) { - if (!isLayoutFirst) { - text += TYPE_SEPARATOR + SPACE; - } else { - text += ARRAY_START; - } - isLayoutFirst = false; - text += layoutDesc.first; - text += BLOCK_START; - text += std::to_string(layoutDesc.second.GetValue()); - } - if (!isLayoutFirst) { - text += ARRAY_END; - } - bool isPtLayoutFirst = true; - for (const auto &layoutDesc : desc.GetPtLayoutDesc()) { - if (!isPtLayoutFirst) { - text += TYPE_SEPARATOR + SPACE; - } else { - if (!isLayoutFirst) { - text += TYPE_SEPARATOR + SPACE; - } - text += ARRAY_START; - } - isPtLayoutFirst = false; - text += layoutDesc.first; - text += BLOCK_START; - text += std::to_string(layoutDesc.second.GetValue()); - } - if (!isPtLayoutFirst) { - text += ARRAY_END; - } - bool isCtorLayoutFirst = true; - for (const auto &layoutDesc : desc.GetCtorLayoutDesc()) { - if (!isCtorLayoutFirst) { - text += TYPE_SEPARATOR + SPACE; - } else { - if (!isLayoutFirst || !isPtLayoutFirst) { - text += TYPE_SEPARATOR + SPACE; - } - text += ARRAY_START; - } - isCtorLayoutFirst = false; - text += layoutDesc.first; - text += BLOCK_START; - text += std::to_string(layoutDesc.second.GetValue()); - } - if (!isCtorLayoutFirst) { - text += ARRAY_END; - } - text += (SPACE + ARRAY_END); - return text; -} - -void PGOHClassLayoutDescInner::Merge(const PGOHClassLayoutDesc &desc) -{ - auto current = const_cast(GetFirst()); - for (const auto &iter : desc.GetLayoutDesc()) { - auto key = iter.first; - auto type = iter.second; - if (key.size() > 0) { - new (current) PGOLayoutDescInfo(key, type); - current = const_cast(GetNext(current)); - count_++; - } - } - for (const auto &iter : desc.GetPtLayoutDesc()) { - auto key = iter.first; - auto type = iter.second; - if (key.size() > 0) { - new (current) PGOLayoutDescInfo(key, type); - current = const_cast(GetNext(current)); - ptCount_++; - } - } - for (const auto &iter : desc.GetCtorLayoutDesc()) { - auto key = iter.first; - auto type = iter.second; - if (key.size() > 0) { - new (current) PGOLayoutDescInfo(key, type); - current = const_cast(GetNext(current)); - ctorCount_++; - } - } -} - -void PGOMethodTypeSet::RWScalarOpTypeInfo::ProcessToText(std::string &text) const -{ - text += std::to_string(GetOffset()); - text += BLOCK_START; - text += ARRAY_START + SPACE; - bool isFirst = true; - for (int i = 0; i < type_.GetCount(); i++) { - if (!isFirst) { - text += TYPE_SEPARATOR + SPACE; - } - isFirst = false; - text += type_.GetType(i).GetTypeString(); - } - text += (SPACE + ARRAY_END); -} - -void PGOMethodTypeSet::ObjDefOpTypeInfo::ProcessToText(std::string &text) const -{ - text += std::to_string(GetOffset()); - text += BLOCK_START; - text += ARRAY_START + SPACE; - text += GetType().GetTypeString(); - text += TYPE_SEPARATOR; - text += GetSuperType().GetTypeString(); - text += (SPACE + ARRAY_END); -} - -bool PGOMethodInfoMap::AddMethod(Chunk *chunk, EntityId methodId, uint32_t checksum, const CString &methodName, - SampleMode mode) +bool PGOMethodInfoMap::AddMethod(NativeAreaAllocator *allocator, Method *jsMethod, SampleMode mode, int32_t incCount) { + PGOMethodId methodId(jsMethod->GetMethodId()); auto result = methodInfos_.find(methodId); if (result != methodInfos_.end()) { auto info = result->second; - info->IncreaseCount(); + info->IncreaseCount(incCount); info->SetSampleMode(mode); return false; } else { + CString methodName = jsMethod->GetMethodName(); size_t strlen = methodName.size(); size_t size = static_cast(PGOMethodInfo::Size(strlen)); - void *infoAddr = chunk->Allocate(size); - auto info = new (infoAddr) PGOMethodInfo(methodId, 1, mode, methodName.c_str()); + void *infoAddr = allocator->Allocate(size); + auto info = new (infoAddr) PGOMethodInfo(methodId, incCount, mode, methodName.c_str()); methodInfos_.emplace(methodId, info); + auto checksum = PGOMethodInfo::CalcChecksum(jsMethod->GetMethodName(), jsMethod->GetBytecodeArray(), + jsMethod->GetCodeSize()); methodsChecksum_.emplace(methodId, checksum); return true; } } -PGOMethodTypeSet *PGOMethodInfoMap::GetOrInsertMethodTypeSet(Chunk *chunk, EntityId methodId) +PGOMethodTypeSet *PGOMethodInfoMap::GetOrInsertMethodTypeSet(Chunk *chunk, PGOMethodId methodId) { auto typeInfoSetIter = methodTypeInfos_.find(methodId); if (typeInfoSetIter != methodTypeInfos_.end()) { @@ -507,7 +226,7 @@ PGOMethodTypeSet *PGOMethodInfoMap::GetOrInsertMethodTypeSet(Chunk *chunk, Entit } } -bool PGOMethodInfoMap::AddType(Chunk *chunk, EntityId methodId, int32_t offset, PGOSampleType type) +bool PGOMethodInfoMap::AddType(Chunk *chunk, PGOMethodId methodId, int32_t offset, PGOSampleType type) { auto typeInfoSet = GetOrInsertMethodTypeSet(chunk, methodId); ASSERT(typeInfoSet != nullptr); @@ -515,8 +234,24 @@ bool PGOMethodInfoMap::AddType(Chunk *chunk, EntityId methodId, int32_t offset, return true; } +bool PGOMethodInfoMap::AddCallTargetType(Chunk *chunk, PGOMethodId methodId, int32_t offset, PGOSampleType type) +{ + auto typeInfoSet = GetOrInsertMethodTypeSet(chunk, methodId); + ASSERT(typeInfoSet != nullptr); + typeInfoSet->AddCallTargetType(offset, type); + return true; +} + +bool PGOMethodInfoMap::AddObjectInfo(Chunk *chunk, PGOMethodId methodId, int32_t offset, const PGOObjectInfo &info) +{ + auto typeInfoSet = GetOrInsertMethodTypeSet(chunk, methodId); + ASSERT(typeInfoSet != nullptr); + typeInfoSet->AddObjectInfo(offset, info); + return true; +} + bool PGOMethodInfoMap::AddDefine( - Chunk *chunk, EntityId methodId, int32_t offset, PGOSampleType type, PGOSampleType superType) + Chunk *chunk, PGOMethodId methodId, int32_t offset, PGOSampleType type, PGOSampleType superType) { auto typeInfoSet = GetOrInsertMethodTypeSet(chunk, methodId); ASSERT(typeInfoSet != nullptr); @@ -569,12 +304,14 @@ void PGOMethodInfoMap::Merge(Chunk *chunk, PGOMethodInfoMap *methodInfos) } } -bool PGOMethodInfoMap::ParseFromBinary(Chunk *chunk, uint32_t threshold, void **buffer, PGOProfilerHeader *const header) +bool PGOMethodInfoMap::ParseFromBinary(Chunk *chunk, PGOContext &context, void **buffer) { + PGOProfilerHeader *const header = context.GetHeader(); + ASSERT(header != nullptr); SectionInfo secInfo = base::ReadBuffer(buffer); for (uint32_t j = 0; j < secInfo.number_; j++) { PGOMethodInfo *info = base::ReadBufferInSize(buffer); - if (info->IsFilter(threshold)) { + if (info->IsFilter(context.GetHotnessThreshold())) { if (header->SupportMethodChecksum()) { base::ReadBuffer(buffer, sizeof(uint32_t)); } @@ -584,44 +321,44 @@ bool PGOMethodInfoMap::ParseFromBinary(Chunk *chunk, uint32_t threshold, void ** continue; } methodInfos_.emplace(info->GetMethodId(), info); - LOG_ECMA(DEBUG) << "Method:" << info->GetMethodId() << ELEMENT_SEPARATOR << info->GetCount() - << ELEMENT_SEPARATOR << std::to_string(static_cast(info->GetSampleMode())) - << ELEMENT_SEPARATOR << info->GetMethodName(); + LOG_ECMA(DEBUG) << "Method:" << info->GetMethodId() << DumpUtils::ELEMENT_SEPARATOR << info->GetCount() + << DumpUtils::ELEMENT_SEPARATOR << std::to_string(static_cast(info->GetSampleMode())) + << DumpUtils::ELEMENT_SEPARATOR << info->GetMethodName(); if (header->SupportMethodChecksum()) { auto checksum = base::ReadBuffer(buffer, sizeof(uint32_t)); methodsChecksum_.emplace(info->GetMethodId(), checksum); } if (header->SupportType()) { auto typeInfoSet = chunk->New(); - typeInfoSet->ParseFromBinary(buffer); + typeInfoSet->ParseFromBinary(context, buffer); methodTypeInfos_.emplace(info->GetMethodId(), typeInfoSet); } } return !methodInfos_.empty(); } -bool PGOMethodInfoMap::ProcessToBinary(uint32_t threshold, const CString &recordName, const SaveTask *task, - std::ofstream &stream, PGOProfilerHeader *const header) const +bool PGOMethodInfoMap::ProcessToBinary(PGOContext &context, ProfileTypeRef recordProfileRef, const SaveTask *task, + std::fstream &stream, PGOProfilerHeader *const header) const { SectionInfo secInfo; std::stringstream methodStream; for (auto iter = methodInfos_.begin(); iter != methodInfos_.end(); iter++) { - LOG_ECMA(DEBUG) << "Method:" << iter->first << ELEMENT_SEPARATOR << iter->second->GetCount() - << ELEMENT_SEPARATOR << std::to_string(static_cast(iter->second->GetSampleMode())) - << ELEMENT_SEPARATOR << iter->second->GetMethodName(); + LOG_ECMA(DEBUG) << "Method:" << iter->first << DumpUtils::ELEMENT_SEPARATOR << iter->second->GetCount() + << DumpUtils::ELEMENT_SEPARATOR + << std::to_string(static_cast(iter->second->GetSampleMode())) + << DumpUtils::ELEMENT_SEPARATOR << iter->second->GetMethodName(); if (task && task->IsTerminate()) { LOG_ECMA(DEBUG) << "ProcessProfile: task is already terminate"; return false; } auto curMethodInfo = iter->second; - if (curMethodInfo->IsFilter(threshold)) { + if (curMethodInfo->IsFilter(context.GetHotnessThreshold())) { continue; } methodStream.write(reinterpret_cast(curMethodInfo), curMethodInfo->Size()); if (header->SupportMethodChecksum()) { auto checksumIter = methodsChecksum_.find(curMethodInfo->GetMethodId()); uint32_t checksum = 0; - ASSERT(checksumIter != methodsChecksum_.end()); if (checksumIter != methodsChecksum_.end()) { checksum = checksumIter->second; } @@ -630,7 +367,7 @@ bool PGOMethodInfoMap::ProcessToBinary(uint32_t threshold, const CString &record if (header->SupportType()) { auto typeInfoIter = methodTypeInfos_.find(curMethodInfo->GetMethodId()); if (typeInfoIter != methodTypeInfos_.end()) { - typeInfoIter->second->ProcessToBinary(methodStream); + typeInfoIter->second->ProcessToBinary(context, methodStream); } else { uint32_t number = 0; methodStream.write(reinterpret_cast(&number), sizeof(uint32_t)); @@ -640,8 +377,8 @@ bool PGOMethodInfoMap::ProcessToBinary(uint32_t threshold, const CString &record } if (secInfo.number_ > 0) { secInfo.offset_ = sizeof(SectionInfo); - secInfo.size_ = static_cast(methodStream.tellg()); - stream << recordName << '\0'; + secInfo.size_ = static_cast(methodStream.tellp()); + stream.write(reinterpret_cast(&recordProfileRef), sizeof(uint32_t)); stream.write(reinterpret_cast(&secInfo), sizeof(SectionInfo)); stream << methodStream.rdbuf(); return true; @@ -658,7 +395,7 @@ bool PGOMethodInfoMap::ParseFromText(Chunk *chunk, uint32_t threshold, const std return false; } uint32_t count; - if (!base::StringHelper::StrToUInt32(infoStrings[PGOMethodInfo::METHOD_COUNT_INDEX].c_str(), &count)) { + if (!StringHelper::StrToUInt32(infoStrings[PGOMethodInfo::METHOD_COUNT_INDEX].c_str(), &count)) { LOG_ECMA(ERROR) << "count: " << infoStrings[PGOMethodInfo::METHOD_COUNT_INDEX] << " parse failed"; return false; } @@ -671,7 +408,7 @@ bool PGOMethodInfoMap::ParseFromText(Chunk *chunk, uint32_t threshold, const std return true; } uint32_t methodId; - if (!base::StringHelper::StrToUInt32(infoStrings[PGOMethodInfo::METHOD_ID_INDEX].c_str(), &methodId)) { + if (!StringHelper::StrToUInt32(infoStrings[PGOMethodInfo::METHOD_ID_INDEX].c_str(), &methodId)) { LOG_ECMA(ERROR) << "method id: " << infoStrings[PGOMethodInfo::METHOD_ID_INDEX] << " parse failed"; return false; } @@ -679,7 +416,7 @@ bool PGOMethodInfoMap::ParseFromText(Chunk *chunk, uint32_t threshold, const std size_t len = methodName.size(); void *infoAddr = chunk->Allocate(PGOMethodInfo::Size(len)); - auto info = new (infoAddr) PGOMethodInfo(EntityId(methodId), count, mode, methodName.c_str()); + auto info = new (infoAddr) PGOMethodInfo(PGOMethodId(methodId), count, mode, methodName.c_str()); methodInfos_.emplace(methodId, info); // Parse Type Info @@ -688,8 +425,8 @@ bool PGOMethodInfoMap::ParseFromText(Chunk *chunk, uint32_t threshold, const std } std::string typeInfos = infoStrings[PGOMethodTypeSet::METHOD_TYPE_INFO_INDEX]; if (!typeInfos.empty()) { - size_t start = typeInfos.find_first_of(ARRAY_START); - size_t end = typeInfos.find_last_of(ARRAY_END); + size_t start = typeInfos.find_first_of(DumpUtils::ARRAY_START); + size_t end = typeInfos.find_last_of(DumpUtils::ARRAY_END); if (start == std::string::npos || end == std::string::npos || start > end) { LOG_ECMA(ERROR) << "Type info: " << typeInfos << " parse failed"; return false; @@ -718,20 +455,21 @@ void PGOMethodInfoMap::ProcessToText(uint32_t threshold, const CString &recordNa continue; } if (isFirst) { - profilerString += NEW_LINE; + profilerString += DumpUtils::NEW_LINE; profilerString += recordName; - profilerString += BLOCK_AND_ARRAY_START; + profilerString += DumpUtils::BLOCK_AND_ARRAY_START; isFirst = false; } else { - profilerString += BLOCK_SEPARATOR + SPACE; + profilerString += DumpUtils::BLOCK_SEPARATOR + DumpUtils::SPACE; } methodInfo->ProcessToText(profilerString); - profilerString += ELEMENT_SEPARATOR; + profilerString += DumpUtils::ELEMENT_SEPARATOR; auto checksumIter = methodsChecksum_.find(methodInfo->GetMethodId()); if (checksumIter != methodsChecksum_.end()) { std::stringstream parseStream; - parseStream << std::internal << std::setfill('0') << std::showbase << std::setw(HEX_FORMAT_WIDTH_FOR_32BITS) - << std::hex << checksumIter->second << ELEMENT_SEPARATOR; + parseStream << std::internal << std::setfill('0') << std::showbase + << std::setw(DumpUtils::HEX_FORMAT_WIDTH_FOR_32BITS) << std::hex << checksumIter->second + << DumpUtils::ELEMENT_SEPARATOR; profilerString += parseStream.str(); } auto iter = methodTypeInfos_.find(methodInfo->GetMethodId()); @@ -740,18 +478,19 @@ void PGOMethodInfoMap::ProcessToText(uint32_t threshold, const CString &recordNa } } if (!isFirst) { - profilerString += (SPACE + ARRAY_END + NEW_LINE); + profilerString += (DumpUtils::SPACE + DumpUtils::ARRAY_END + DumpUtils::NEW_LINE); stream << profilerString; } } -bool PGOMethodIdSet::ParseFromBinary( - NativeAreaAllocator *allocator, uint32_t threshold, void **buffer, PGOProfilerHeader *const header) +bool PGOMethodIdSet::ParseFromBinary(PGOContext &context, void **buffer) { + PGOProfilerHeader *const header = context.GetHeader(); + ASSERT(header != nullptr); SectionInfo secInfo = base::ReadBuffer(buffer); for (uint32_t j = 0; j < secInfo.number_; j++) { PGOMethodInfo *info = base::ReadBufferInSize(buffer); - if (info->IsFilter(threshold)) { + if (info->IsFilter(context.GetHotnessThreshold())) { if (header->SupportMethodChecksum()) { base::ReadBuffer(buffer, sizeof(uint32_t)); } @@ -760,67 +499,125 @@ bool PGOMethodIdSet::ParseFromBinary( } continue; } - methodIdSet_.emplace(info->GetMethodId()); - LOG_ECMA(DEBUG) << "Method:" << info->GetMethodId() << ELEMENT_SEPARATOR << info->GetCount() - << ELEMENT_SEPARATOR << std::to_string(static_cast(info->GetSampleMode())) - << ELEMENT_SEPARATOR << info->GetMethodName(); + uint32_t checksum = 0; if (header->SupportMethodChecksum()) { - auto checksum = base::ReadBuffer(buffer, sizeof(uint32_t)); - auto checksumIter = methodsChecksumMapping_.find(checksum); - if (checksumIter != methodsChecksumMapping_.end()) { - checksumIter->second.emplace(info->GetMethodId()); - } else { - std::unordered_set methodIdSet = { info->GetMethodId() }; - methodsChecksumMapping_.emplace(checksum, methodIdSet); - } - methodIdNameMapping_.emplace(info->GetMethodId(), info->GetMethodName()); - } + checksum = base::ReadBuffer(buffer, sizeof(uint32_t)); + } + auto ret = methodInfoMap_.try_emplace(info->GetMethodName(), chunk_); + auto methodNameSetIter = ret.first; + auto &methodInfo = methodNameSetIter->second.GetOrCreateMethodInfo(checksum, info->GetMethodId()); + LOG_ECMA(DEBUG) << "Method:" << info->GetMethodId() << DumpUtils::ELEMENT_SEPARATOR << info->GetCount() + << DumpUtils::ELEMENT_SEPARATOR << std::to_string(static_cast(info->GetSampleMode())) + << DumpUtils::ELEMENT_SEPARATOR << info->GetMethodName(); if (header->SupportType()) { - auto typeInfoSet = allocator->New(); - typeInfoSet->ParseFromBinary(buffer); - methodTypeSet_.emplace(info->GetMethodId(), typeInfoSet); + methodInfo.GetPGOMethodTypeSet().ParseFromBinary(context, buffer); } } - return methodIdSet_.size() != 0; + return !methodInfoMap_.empty(); +} + +void PGOMethodIdSet::GetMismatchResult(const CString &recordName, uint32_t &totalMethodCount, + uint32_t &mismatchMethodCount, + std::set> &mismatchMethodSet) const +{ + totalMethodCount += methodInfoMap_.size(); + for (const auto &methodNameSet : methodInfoMap_) { + if (methodNameSet.second.IsMatch()) { + continue; + } + auto info = std::make_pair(methodNameSet.first, recordName); + mismatchMethodSet.emplace(info); + mismatchMethodCount++; + } } -PGOMethodInfoMap *PGORecordDetailInfos::GetMethodInfoMap(const CString &recordName) +void PGOMethodIdSet::Merge(const PGOMethodIdSet &from) { - auto iter = recordInfos_.find(recordName.c_str()); + for (const auto &methodNameSet : from.methodInfoMap_) { + auto iter = methodInfoMap_.find(methodNameSet.first); + if (iter == methodInfoMap_.end()) { + auto ret = methodInfoMap_.try_emplace(methodNameSet.first, chunk_); + iter = ret.first; + } + const_cast(iter->second).Merge(methodNameSet.second); + } +} + +void PGODecodeMethodInfo::Merge(const PGODecodeMethodInfo &from) +{ + ASSERT(methodId_.IsValid() && from.methodId_.IsValid()); + if (!(methodId_ == from.methodId_)) { + LOG_ECMA(ERROR) << "MethodId not match. " << methodId_ << " vs " << from.methodId_; + return; + } + pgoMethodTypeSet_.Merge(&from.pgoMethodTypeSet_); +} + +PGORecordDetailInfos::PGORecordDetailInfos(uint32_t hotnessThreshold) : hotnessThreshold_(hotnessThreshold) +{ + chunk_ = std::make_unique(&nativeAreaAllocator_); + InitSections(); +}; + +PGORecordDetailInfos::~PGORecordDetailInfos() +{ + Clear(); +} + +PGOMethodInfoMap *PGORecordDetailInfos::GetMethodInfoMap(ProfileType recordProfileType) +{ + auto iter = recordInfos_.find(recordProfileType); if (iter != recordInfos_.end()) { return iter->second; } else { auto curMethodInfos = nativeAreaAllocator_.New(); - recordInfos_.emplace(recordName.c_str(), curMethodInfos); + recordInfos_.emplace(recordProfileType, curMethodInfos); return curMethodInfos; } } -bool PGORecordDetailInfos::AddMethod( - const CString &recordName, EntityId methodId, uint32_t checksum, const CString &methodName, SampleMode mode) +bool PGORecordDetailInfos::AddMethod(ProfileType recordProfileType, Method *jsMethod, SampleMode mode, int32_t incCount) { - auto curMethodInfos = GetMethodInfoMap(recordName); + auto curMethodInfos = GetMethodInfoMap(recordProfileType); ASSERT(curMethodInfos != nullptr); - return curMethodInfos->AddMethod(chunk_.get(), methodId, checksum, methodName, mode); + ASSERT(jsMethod != nullptr); + return curMethodInfos->AddMethod(&nativeAreaAllocator_, jsMethod, mode, incCount); } -bool PGORecordDetailInfos::AddType(const CString &recordName, EntityId methodId, int32_t offset, PGOSampleType type) +bool PGORecordDetailInfos::AddType(ProfileType recordProfileType, PGOMethodId methodId, int32_t offset, + PGOSampleType type) { - auto curMethodInfos = GetMethodInfoMap(recordName); + auto curMethodInfos = GetMethodInfoMap(recordProfileType); ASSERT(curMethodInfos != nullptr); return curMethodInfos->AddType(chunk_.get(), methodId, offset, type); } +bool PGORecordDetailInfos::AddCallTargetType(ProfileType recordProfileType, PGOMethodId methodId, int32_t offset, + PGOSampleType type) +{ + auto curMethodInfos = GetMethodInfoMap(recordProfileType); + ASSERT(curMethodInfos != nullptr); + return curMethodInfos->AddCallTargetType(chunk_.get(), methodId, offset, type); +} + +bool PGORecordDetailInfos::AddObjectInfo( + ProfileType recordProfileType, EntityId methodId, int32_t offset, const PGOObjectInfo &info) +{ + auto curMethodInfos = GetMethodInfoMap(recordProfileType); + ASSERT(curMethodInfos != nullptr); + return curMethodInfos->AddObjectInfo(chunk_.get(), methodId, offset, info); +} + bool PGORecordDetailInfos::AddDefine( - const CString &recordName, EntityId methodId, int32_t offset, PGOSampleType type, PGOSampleType superType) + ProfileType recordProfileType, PGOMethodId methodId, int32_t offset, PGOSampleType type, PGOSampleType superType) { - auto curMethodInfos = GetMethodInfoMap(recordName); + auto curMethodInfos = GetMethodInfoMap(recordProfileType); ASSERT(curMethodInfos != nullptr); curMethodInfos->AddDefine(chunk_.get(), methodId, offset, type, superType); - PGOHClassLayoutDesc descInfo(type.GetClassType()); - descInfo.SetSuperClassType(superType.GetClassType()); + PGOHClassLayoutDesc descInfo(type.GetProfileType()); + descInfo.SetSuperProfileType(superType.GetProfileType()); auto iter = moduleLayoutDescInfos_.find(descInfo); if (iter != moduleLayoutDescInfos_.end()) { moduleLayoutDescInfos_.erase(iter); @@ -829,10 +626,10 @@ bool PGORecordDetailInfos::AddDefine( return true; } -bool PGORecordDetailInfos::AddLayout(PGOSampleType type, JSTaggedType hclass, PGOObjLayoutKind kind) +bool PGORecordDetailInfos::AddLayout(PGOSampleType type, JSTaggedType hclass, PGOObjKind kind) { auto hclassObject = JSHClass::Cast(JSTaggedValue(hclass).GetTaggedObject()); - PGOHClassLayoutDesc descInfo(type.GetClassType()); + PGOHClassLayoutDesc descInfo(type.GetProfileType()); auto iter = moduleLayoutDescInfos_.find(descInfo); if (iter != moduleLayoutDescInfos_.end()) { auto &oldDescInfo = const_cast(*iter); @@ -840,7 +637,21 @@ bool PGORecordDetailInfos::AddLayout(PGOSampleType type, JSTaggedType hclass, PG return false; } } else { - LOG_ECMA(INFO) << "The current class did not find a definition"; + LOG_ECMA(DEBUG) << "The current class did not find a definition"; + return false; + } + return true; +} + +bool PGORecordDetailInfos::UpdateElementsKind(PGOSampleType type, ElementsKind kind) +{ + PGOHClassLayoutDesc descInfo(type.GetProfileType()); + auto iter = moduleLayoutDescInfos_.find(descInfo); + if (iter != moduleLayoutDescInfos_.end()) { + auto &oldDescInfo = const_cast(*iter); + oldDescInfo.UpdateElementKind(kind); + } else { + LOG_ECMA(DEBUG) << "The current class did not find a definition"; return false; } return true; @@ -848,43 +659,67 @@ bool PGORecordDetailInfos::AddLayout(PGOSampleType type, JSTaggedType hclass, PG void PGORecordDetailInfos::Merge(const PGORecordDetailInfos &recordInfos) { + std::map idMapping; + recordPool_->Merge(*recordInfos.recordPool_, idMapping); for (auto iter = recordInfos.recordInfos_.begin(); iter != recordInfos.recordInfos_.end(); iter++) { - auto recordName = iter->first; + auto oldRecordType = iter->first; + auto newIter = idMapping.find(oldRecordType.GetId()); + if (newIter == idMapping.end()) { + continue; + } + auto newRecordType = PGOProfiler::GetRecordProfileType(oldRecordType.GetAbcId(), newIter->second); auto fromMethodInfos = iter->second; - auto recordInfosIter = recordInfos_.find(recordName); + auto recordInfosIter = recordInfos_.find(newRecordType); PGOMethodInfoMap *toMethodInfos = nullptr; if (recordInfosIter == recordInfos_.end()) { toMethodInfos = nativeAreaAllocator_.New(); - recordInfos_.emplace(recordName, toMethodInfos); + recordInfos_.emplace(newRecordType, toMethodInfos); } else { toMethodInfos = recordInfosIter->second; } toMethodInfos->Merge(chunk_.get(), fromMethodInfos); - - // Merge global layout desc infos to global method info map - for (auto info = recordInfos.moduleLayoutDescInfos_.begin(); info != recordInfos.moduleLayoutDescInfos_.end(); - info++) { - auto result = moduleLayoutDescInfos_.find(*info); - if (result == moduleLayoutDescInfos_.end()) { - moduleLayoutDescInfos_.emplace(*info); - } else { - const_cast(*result).Merge(*info); - } + } + // Merge global layout desc infos to global method info map + for (auto info = recordInfos.moduleLayoutDescInfos_.begin(); info != recordInfos.moduleLayoutDescInfos_.end(); + info++) { + auto fromInfo = *info; + auto result = moduleLayoutDescInfos_.find(fromInfo); + if (result == moduleLayoutDescInfos_.end()) { + moduleLayoutDescInfos_.emplace(fromInfo); + } else { + const_cast(*result).Merge(fromInfo); } } } void PGORecordDetailInfos::ParseFromBinary(void *buffer, PGOProfilerHeader *const header) { + header_ = header; + if (!ParseSectionsFromBinary(apSectionList_, buffer, header)) { + return; + } SectionInfo *info = header->GetRecordInfoSection(); void *addr = reinterpret_cast(reinterpret_cast(buffer) + info->offset_); for (uint32_t i = 0; i < info->number_; i++) { - auto recordName = base::ReadBuffer(&addr); + ApEntityId recordId(0); + ProfileType recordType; + if (header->SupportProfileTypeWithAbcId()) { + auto recordTypeRef = ProfileTypeRef(base::ReadBuffer(&addr, sizeof(ApEntityId))); + recordType = ProfileType(*this, recordTypeRef); + recordId = recordType.GetId(); + } else if (header->SupportRecordPool()) { + recordId = base::ReadBuffer(&addr, sizeof(ApEntityId)); + } else { + auto *recordName = base::ReadBuffer(&addr); + recordPool_->TryAdd(recordName, recordId); + } + recordType.UpdateId(recordId); + recordType.UpdateKind(ProfileType::Kind::LocalRecordId); PGOMethodInfoMap *methodInfos = nativeAreaAllocator_.New(); - if (methodInfos->ParseFromBinary(chunk_.get(), hotnessThreshold_, &addr, header)) { - recordInfos_.emplace(recordName, methodInfos); + if (methodInfos->ParseFromBinary(chunk_.get(), *this, &addr)) { + recordInfos_.emplace(recordType, methodInfos); } } @@ -892,7 +727,7 @@ void PGORecordDetailInfos::ParseFromBinary(void *buffer, PGOProfilerHeader *cons if (info == nullptr) { return; } - if (header->SupportType()) { + if (header->SupportTrackField()) { ParseFromBinaryForLayout(&addr); } } @@ -901,19 +736,20 @@ bool PGORecordDetailInfos::ParseFromBinaryForLayout(void **buffer) { SectionInfo secInfo = base::ReadBuffer(buffer); for (uint32_t i = 0; i < secInfo.number_; i++) { - PGOHClassLayoutDescInner *info = base::ReadBufferInSize(buffer); + auto *info = base::ReadBufferInSize(buffer); if (info == nullptr) { LOG_ECMA(INFO) << "Binary format error!"; continue; } - moduleLayoutDescInfos_.emplace(info->Convert()); + moduleLayoutDescInfos_.emplace(info->Convert(*this)); } return true; } void PGORecordDetailInfos::ProcessToBinary( - const SaveTask *task, std::ofstream &fileStream, PGOProfilerHeader *const header) const + const SaveTask *task, std::fstream &fileStream, PGOProfilerHeader *const header) { + header_ = header; auto info = header->GetRecordInfoSection(); info->number_ = 0; info->offset_ = static_cast(fileStream.tellp()); @@ -922,9 +758,9 @@ void PGORecordDetailInfos::ProcessToBinary( LOG_ECMA(DEBUG) << "ProcessProfile: task is already terminate"; break; } - auto recordName = iter->first; + auto recordId = iter->first; auto curMethodInfos = iter->second; - if (curMethodInfos->ProcessToBinary(hotnessThreshold_, recordName, task, fileStream, header)) { + if (curMethodInfos->ProcessToBinary(*this, ProfileTypeRef(*this, recordId), task, fileStream, header)) { info->number_++; } } @@ -943,39 +779,51 @@ void PGORecordDetailInfos::ProcessToBinary( info->number_++; } info->size_ = static_cast(fileStream.tellp()) - info->offset_; + + for (const auto §ionWeak : apSectionList_) { + auto section = sectionWeak.lock(); + if (section == nullptr) { + continue; + } + PGOFileSectionInterface::ProcessSectionToBinary(fileStream, header, *section); + } } bool PGORecordDetailInfos::ProcessToBinaryForLayout( - NativeAreaAllocator *allocator, const SaveTask *task, std::ofstream &stream) const + NativeAreaAllocator *allocator, const SaveTask *task, std::fstream &stream) { SectionInfo secInfo; - std::stringstream layoutDescStream; - + auto layoutBeginPosition = stream.tellp(); + stream.seekp(sizeof(SectionInfo), std::ofstream::cur); for (const auto &typeInfo : moduleLayoutDescInfos_) { if (task && task->IsTerminate()) { LOG_ECMA(DEBUG) << "ProcessProfile: task is already terminate"; return false; } - auto classType = PGOSampleType(typeInfo.GetClassType()); - size_t size = PGOHClassLayoutDescInner::CaculateSize(typeInfo); + auto profileType = PGOSampleType(typeInfo.GetProfileType()); + auto elementsKind = typeInfo.GetElementsKind(); + size_t size = PGOHClassLayoutDescInnerRef::CaculateSize(typeInfo); if (size == 0) { continue; } - auto superType = PGOSampleType(typeInfo.GetSuperClassType()); + auto superType = PGOSampleType(typeInfo.GetSuperProfileType()); + + PGOSampleTypeRef classRef = PGOSampleTypeRef::ConvertFrom(*this, profileType); + PGOSampleTypeRef superRef = PGOSampleTypeRef::ConvertFrom(*this, superType); void *addr = allocator->Allocate(size); - auto descInfos = new (addr) PGOHClassLayoutDescInner(size, classType, superType); + auto descInfos = new (addr) PGOHClassLayoutDescInnerRef(size, classRef, superRef, elementsKind); descInfos->Merge(typeInfo); - layoutDescStream.write(reinterpret_cast(descInfos), size); + stream.write(reinterpret_cast(descInfos), size); allocator->Delete(addr); secInfo.number_++; } secInfo.offset_ = sizeof(SectionInfo); - secInfo.size_ = static_cast(layoutDescStream.tellg()); - stream.write(reinterpret_cast(&secInfo), sizeof(SectionInfo)); - if (secInfo.number_ > 0) { - stream << layoutDescStream.rdbuf(); - } + secInfo.size_ = static_cast(stream.tellp()) - + static_cast(layoutBeginPosition) - sizeof(SectionInfo); + stream.seekp(layoutBeginPosition, std::ofstream::beg) + .write(reinterpret_cast(&secInfo), sizeof(SectionInfo)) + .seekp(0, std::ofstream::end); return true; } @@ -986,28 +834,31 @@ bool PGORecordDetailInfos::ParseFromText(std::ifstream &stream) if (details.empty()) { continue; } - size_t blockIndex = details.find(BLOCK_AND_ARRAY_START); + size_t blockIndex = details.find(DumpUtils::BLOCK_AND_ARRAY_START); if (blockIndex == std::string::npos) { return false; } CString recordName = ConvertToString(details.substr(0, blockIndex)); - size_t start = details.find_first_of(ARRAY_START); - size_t end = details.find_last_of(ARRAY_END); + size_t start = details.find_first_of(DumpUtils::ARRAY_START); + size_t end = details.find_last_of(DumpUtils::ARRAY_END); if (start == std::string::npos || end == std::string::npos || start > end) { return false; } auto content = details.substr(start + 1, end - (start + 1) - 1); - std::vector infoStrings = base::StringHelper::SplitString(content, BLOCK_SEPARATOR); + std::vector infoStrings = StringHelper::SplitString(content, DumpUtils::BLOCK_SEPARATOR); if (infoStrings.size() <= 0) { continue; } - auto methodInfosIter = recordInfos_.find(recordName.c_str()); + ApEntityId recordId(0); + recordPool_->TryAdd(recordName, recordId); + ProfileType profileType(0, recordId, ProfileType::Kind::LocalRecordId); + auto methodInfosIter = recordInfos_.find(profileType); PGOMethodInfoMap *methodInfos = nullptr; if (methodInfosIter == recordInfos_.end()) { methodInfos = nativeAreaAllocator_.New(); - recordInfos_.emplace(recordName.c_str(), methodInfos); + recordInfos_.emplace(profileType, methodInfos); } else { methodInfos = methodInfosIter->second; } @@ -1024,24 +875,48 @@ void PGORecordDetailInfos::ProcessToText(std::ofstream &stream) const bool isFirst = true; for (auto layoutInfoIter : moduleLayoutDescInfos_) { if (isFirst) { - profilerString += NEW_LINE; - profilerString += ARRAY_START + SPACE; + profilerString += DumpUtils::NEW_LINE; + profilerString += DumpUtils::ARRAY_START + DumpUtils::SPACE; isFirst = false; } else { - profilerString += BLOCK_SEPARATOR + SPACE; + profilerString += DumpUtils::BLOCK_SEPARATOR + DumpUtils::SPACE; } - isFirst = false; profilerString += PGOHClassLayoutDescInner::GetTypeString(layoutInfoIter); } if (!isFirst) { - profilerString += (SPACE + ARRAY_END + NEW_LINE); + profilerString += (DumpUtils::SPACE + DumpUtils::ARRAY_END + DumpUtils::NEW_LINE); stream << profilerString; } for (auto iter = recordInfos_.begin(); iter != recordInfos_.end(); iter++) { - auto recordName = iter->first; + auto recordId = ApEntityId(iter->first.GetId()); + auto recordName = recordPool_->GetEntry(recordId)->GetData(); auto methodInfos = iter->second; methodInfos->ProcessToText(hotnessThreshold_, recordName, stream); } + recordPool_->GetPool()->ProcessToText(stream); + profileTypePool_->GetPool()->ProcessToText(stream); +} + +void PGORecordDetailInfos::InitSections() +{ + recordPool_ = std::make_unique(); + apSectionList_.emplace_back(recordPool_->GetPool()); + profileTypePool_ = std::make_unique(); + apSectionList_.emplace_back(profileTypePool_->GetPool()); +} + +void PGORecordDetailInfos::Clear() +{ + for (auto iter : recordInfos_) { + iter.second->Clear(); + nativeAreaAllocator_.Delete(iter.second); + } + recordInfos_.clear(); + recordPool_->Clear(); + profileTypePool_->Clear(); + apSectionList_.clear(); + chunk_ = std::make_unique(&nativeAreaAllocator_); + InitSections(); } bool PGORecordSimpleInfos::Match(const CString &recordName, EntityId methodId) @@ -1055,12 +930,26 @@ bool PGORecordSimpleInfos::Match(const CString &recordName, EntityId methodId) void PGORecordSimpleInfos::ParseFromBinary(void *buffer, PGOProfilerHeader *const header) { + header_ = header; + ParseSectionsFromBinary(apSectionList_, buffer, header); SectionInfo *info = header->GetRecordInfoSection(); void *addr = reinterpret_cast(reinterpret_cast(buffer) + info->offset_); for (uint32_t i = 0; i < info->number_; i++) { - auto recordName = base::ReadBuffer(&addr); - PGOMethodIdSet *methodIds = nativeAreaAllocator_.New(); - if (methodIds->ParseFromBinary(&nativeAreaAllocator_, hotnessThreshold_, &addr, header)) { + CString recordName; + ProfileType recordType; + if (header->SupportProfileTypeWithAbcId()) { + auto recordTypeRef = ProfileTypeRef(base::ReadBuffer(&addr, sizeof(ApEntityId))); + recordType = ProfileType(*this, recordTypeRef); + auto recordId = recordType.GetId(); + recordName = recordPool_->GetEntry(recordId)->GetData(); + } else if (header->SupportRecordPool()) { + auto recordId = base::ReadBuffer(&addr, sizeof(ApEntityId)); + recordName = recordPool_->GetEntry(recordId)->GetData(); + } else { + recordName = base::ReadBuffer(&addr); + } + PGOMethodIdSet *methodIds = nativeAreaAllocator_.New(chunk_.get()); + if (methodIds->ParseFromBinary(*this, &addr)) { methodIds_.emplace(recordName, methodIds); } } @@ -1069,22 +958,80 @@ void PGORecordSimpleInfos::ParseFromBinary(void *buffer, PGOProfilerHeader *cons if (info == nullptr) { return; } - if (header->SupportType()) { + if (header->SupportTrackField()) { ParseFromBinaryForLayout(&addr); } } +void PGORecordSimpleInfos::Merge(const PGORecordSimpleInfos &simpleInfos) +{ + std::map idMapping; + recordPool_->Merge(*simpleInfos.recordPool_, idMapping); + for (const auto &method : simpleInfos.methodIds_) { + auto result = methodIds_.find(method.first); + if (result == methodIds_.end()) { + PGOMethodIdSet *methodIds = nativeAreaAllocator_.New(chunk_.get()); + auto ret = methodIds_.emplace(method.first, methodIds); + ASSERT(ret.second); + result = ret.first; + } + const_cast(*result->second).Merge(*method.second); + } + // Merge global layout desc infos to global method info map + for (const auto &moduleLayoutDescInfo : simpleInfos.moduleLayoutDescInfos_) { + auto result = moduleLayoutDescInfos_.find(moduleLayoutDescInfo); + if (result == moduleLayoutDescInfos_.end()) { + moduleLayoutDescInfos_.emplace(moduleLayoutDescInfo); + } else { + const_cast(*result).Merge(moduleLayoutDescInfo); + } + } +} + bool PGORecordSimpleInfos::ParseFromBinaryForLayout(void **buffer) { SectionInfo secInfo = base::ReadBuffer(buffer); for (uint32_t i = 0; i < secInfo.number_; i++) { - PGOHClassLayoutDescInner *info = base::ReadBufferInSize(buffer); + auto *info = base::ReadBufferInSize(buffer); if (info == nullptr) { LOG_ECMA(INFO) << "Binary format error!"; continue; } - moduleLayoutDescInfos_.emplace(info->Convert()); + moduleLayoutDescInfos_.emplace(info->Convert(*this)); } return true; } -} // namespace panda::ecmascript + +void PGORecordSimpleInfos::InitSections() +{ + recordPool_ = std::make_unique(); + apSectionList_.emplace_back(recordPool_->GetPool()); + profileTypePool_ = std::make_unique(); + apSectionList_.emplace_back(profileTypePool_->GetPool()); +} + +void PGORecordSimpleInfos::Clear() +{ + for (const auto &iter : methodIds_) { + iter.second->Clear(); + nativeAreaAllocator_.Delete(iter.second); + } + methodIds_.clear(); + recordPool_->Clear(); + profileTypePool_->Clear(); + apSectionList_.clear(); + chunk_ = std::make_unique(&nativeAreaAllocator_); + InitSections(); +} + +PGORecordSimpleInfos::PGORecordSimpleInfos(uint32_t threshold) : hotnessThreshold_(threshold) +{ + chunk_ = std::make_unique(&nativeAreaAllocator_); + InitSections(); +} + +PGORecordSimpleInfos::~PGORecordSimpleInfos() +{ + Clear(); +} +} // namespace panda::ecmascript::pgo diff --git a/ecmascript/pgo_profiler/pgo_profiler_info.h b/ecmascript/pgo_profiler/pgo_profiler_info.h index 5f47fe1b96d2a9151d978960c8db8ae6f88e61a9..b1dfa57fd1c8d8e5dfc1833cb7fddef2a4d88aa0 100644 --- a/ecmascript/pgo_profiler/pgo_profiler_info.h +++ b/ecmascript/pgo_profiler/pgo_profiler_info.h @@ -16,217 +16,77 @@ #ifndef ECMASCRIPT_PGO_PROFILER_INFO_H #define ECMASCRIPT_PGO_PROFILER_INFO_H +#include #include #include #include #include #include -#include "ecmascript/base/file_header.h" +#include "ecmascript/common.h" #include "ecmascript/jspandafile/method_literal.h" #include "ecmascript/log_wrapper.h" #include "ecmascript/mem/c_containers.h" #include "ecmascript/mem/c_string.h" +#include "ecmascript/mem/chunk_containers.h" #include "ecmascript/mem/native_area_allocator.h" #include "ecmascript/mem/slots.h" +#include "ecmascript/pgo_profiler/ap_file/pgo_file_info.h" +#include "ecmascript/pgo_profiler/ap_file/pgo_method_type_set.h" +#include "ecmascript/pgo_profiler/ap_file/pgo_profile_type_pool.h" +#include "ecmascript/pgo_profiler/ap_file/pgo_record_pool.h" +#include "ecmascript/pgo_profiler/pgo_context.h" #include "ecmascript/pgo_profiler/pgo_profiler_layout.h" +#include "ecmascript/pgo_profiler/pgo_utils.h" +#include "ecmascript/pgo_profiler/types/pgo_profiler_type.h" #include "ecmascript/property_attributes.h" +#include "macros.h" -#include "zlib.h" -namespace panda::ecmascript { +namespace panda::ecmascript::pgo { class SaveTask; - -enum class SampleMode : uint8_t { - HOTNESS_MODE, - CALL_MODE, -}; - -struct SectionInfo { - uint32_t offset_ {0}; - // reserve - uint32_t size_ {0}; - uint32_t number_ {0}; -}; -static constexpr size_t ALIGN_SIZE = 4; - -/** - * |----PGOProfilerHeader - * |--------MAGIC - * |--------VERSION - * |--------SECTION_NUMBER - * |--------PANDA_FILE_INFO_SECTION_INFO - * |------------offset - * |------------size (reserve) - * |------------number - * |--------RECORD_INFO_SECTION_INFO - * |------------offset - * |------------size (reserve) - * |------------number - * |----PGOPandaFileInfos - * |--------SIZE - * |--------CHECK_SUM - * |--------... - * |----PGORecordDetailInfos - * |--------PGOMethodInfoMap - * |------------PGOMethodInfo - * |----------------size - * |----------------id - * |----------------count - * |----------------mode - * |----------------methodName - * |----------------... - * |------------PGOMethodTypeSet - * |----------------ScalarOpTypeInfo - * |--------------------size - * |--------------------offset - * |--------------------type - * |----------------... - * |----------------PGOHClassLayoutDescInner - * |--------------------size - * |--------------------offset - * |--------------------type - * |--------------------count - * |--------------------PGOLayoutDescInfo - * |------------------------size - * |------------------------type - * |------------------------key - * |--------------------... - * |----------------... - * |----PGORecordSimpleInfos - * |--------PGOMethodIdSet - * |------------id - * |------------... - */ -class PGOProfilerHeader : public base::FileHeader { -public: - static constexpr VersionType TYPE_MINI_VERSION = {0, 0, 0, 2}; - static constexpr VersionType METHOD_CHECKSUM_MINI_VERSION = {0, 0, 0, 4}; - static constexpr std::array LAST_VERSION = {0, 0, 0, 4}; - static constexpr size_t SECTION_SIZE = 3; - static constexpr size_t PANDA_FILE_SECTION_INDEX = 0; - static constexpr size_t RECORD_INFO_SECTION_INDEX = 1; - static constexpr size_t LAYOUT_DESC_SECTION_INDEX = 2; - - PGOProfilerHeader() : base::FileHeader(LAST_VERSION), sectionNumber_(SECTION_SIZE) - { - GetPandaInfoSection()->offset_ = Size(); - } - - static size_t LastSize() - { - return sizeof(PGOProfilerHeader) + (SECTION_SIZE - 1) * sizeof(SectionInfo); - } - - size_t Size() const - { - return sizeof(PGOProfilerHeader) + (sectionNumber_ - 1) * sizeof(SectionInfo); - } - - bool Verify() const - { - return InternalVerify("apPath file", LAST_VERSION, false); - } - - static void Build(PGOProfilerHeader **header, size_t size) - { - *header = reinterpret_cast(malloc(size)); - new (*header) PGOProfilerHeader(); - } - - static void Destroy(PGOProfilerHeader **header) - { - if (*header != nullptr) { - free(*header); - *header = nullptr; - } - } - - // Copy Header. - static bool ParseFromBinary(void *buffer, PGOProfilerHeader **header); - void ProcessToBinary(std::ofstream &fileStream) const; - - bool ParseFromText(std::ifstream &stream); - bool ProcessToText(std::ofstream &stream) const; - - SectionInfo *GetPandaInfoSection() const - { - return GetSectionInfo(PANDA_FILE_SECTION_INDEX); - } - - SectionInfo *GetRecordInfoSection() const - { - return GetSectionInfo(RECORD_INFO_SECTION_INDEX); - } - - SectionInfo *GetLayoutDescSection() const - { - return GetSectionInfo(LAYOUT_DESC_SECTION_INDEX); - } - - bool SupportType() const - { - return InternalVerifyVersion(TYPE_MINI_VERSION); - } - - bool SupportMethodChecksum() const - { - return InternalVerifyVersion(METHOD_CHECKSUM_MINI_VERSION); - } - - NO_COPY_SEMANTIC(PGOProfilerHeader); - NO_MOVE_SEMANTIC(PGOProfilerHeader); - -private: - SectionInfo *GetSectionInfo(size_t index) const - { - if (index >= sectionNumber_) { - return nullptr; - } - return const_cast(§ionInfos_) + index; - } - - uint32_t sectionNumber_ {SECTION_SIZE}; - SectionInfo sectionInfos_; -}; +class PGOContext; class PGOPandaFileInfos { public: void Sample(uint32_t checksum) { - pandaFileInfos_.insert(checksum); + fileInfos_.emplace(checksum); } void Clear() { - pandaFileInfos_.clear(); + fileInfos_.clear(); } void ParseFromBinary(void *buffer, SectionInfo *const info); - void ProcessToBinary(std::ofstream &fileStream, SectionInfo *info) const; + void ProcessToBinary(std::fstream &fileStream, SectionInfo *info) const; + void Merge(const PGOPandaFileInfos &pandaFileInfos); + bool VerifyChecksum(const PGOPandaFileInfos &pandaFileInfos, const std::string &base, + const std::string &incoming) const; void ProcessToText(std::ofstream &stream) const; bool ParseFromText(std::ifstream &stream); - bool CheckSum(uint32_t checksum) const; + bool Checksum(uint32_t checksum) const; private: - class PandaFileInfo { + class FileInfo { public: - PandaFileInfo() = default; - PandaFileInfo(uint32_t checksum) : size_(LastSize()), checksum_(checksum) {} + FileInfo() = default; + FileInfo(uint32_t checksum) : size_(LastSize()), checksum_(checksum) {} static size_t LastSize() { - return sizeof(PandaFileInfo); + return sizeof(FileInfo); } - size_t Size() + size_t Size() const { return static_cast(size_); } - bool operator<(const PandaFileInfo &right) const + bool operator<(const FileInfo &right) const { return checksum_ < right.checksum_; } @@ -242,7 +102,7 @@ private: uint32_t checksum_; }; - std::set pandaFileInfos_; + std::set fileInfos_; }; class PGOMethodInfo { @@ -252,10 +112,11 @@ public: static constexpr int METHOD_COUNT_INDEX = 1; static constexpr int METHOD_MODE_INDEX = 2; static constexpr int METHOD_NAME_INDEX = 3; + static constexpr uint32_t METHOD_MAX_HIT_COUNT = 10000U; - explicit PGOMethodInfo(EntityId id) : id_(id) {} + explicit PGOMethodInfo(PGOMethodId id) : id_(id) {} - PGOMethodInfo(EntityId id, uint32_t count, SampleMode mode, const char *methodName) + PGOMethodInfo(PGOMethodId id, uint32_t count, SampleMode mode, const char *methodName) : id_(id), count_(count), mode_(mode) { size_t len = strlen(methodName); @@ -267,22 +128,13 @@ public: *(&methodName_ + len) = '\0'; } - static uint32_t CalcChecksum(const char *name, const uint8_t *byteCodeArray, uint32_t byteCodeLength) - { - uint32_t checksum = 0; - if (byteCodeArray != nullptr) { - checksum = adler32(checksum, byteCodeArray, byteCodeLength); - } + static uint32_t CalcChecksum(const char *name, const uint8_t *byteCodeArray, uint32_t byteCodeLength); - if (name != nullptr) { - checksum = adler32(checksum, reinterpret_cast(name), strlen(name)); - } - return checksum; - } + static uint32_t CalcOpCodeChecksum(const uint8_t *byteCodeArray, uint32_t byteCodeLength); static int32_t Size(uint32_t length) { - return sizeof(PGOMethodInfo) + AlignUp(length, ALIGN_SIZE); + return sizeof(PGOMethodInfo) + AlignUp(length, GetAlignmentInBytes(ALIGN_SIZE)); } int32_t Size() const @@ -302,9 +154,9 @@ public: return true; } - void IncreaseCount() + void IncreaseCount(int32_t inc) { - count_++; + count_ += static_cast(inc); } void ClearCount() @@ -318,11 +170,11 @@ public: LOG_ECMA(ERROR) << "The method id must same for merging"; return; } - count_ += info->GetCount(); + count_ = std::min(count_ + info->GetCount(), METHOD_MAX_HIT_COUNT); SetSampleMode(info->GetSampleMode()); } - EntityId GetMethodId() const + PGOMethodId GetMethodId() const { return id_; } @@ -385,322 +237,31 @@ public: private: uint32_t size_ {0}; - EntityId id_; + PGOMethodId id_; uint32_t count_ {0}; SampleMode mode_ {SampleMode::CALL_MODE}; char methodName_ {0}; }; -class PGOMethodTypeSet { +class PGODecodeMethodInfo { public: - static constexpr int METHOD_TYPE_INFO_INDEX = 4; - static constexpr int METHOD_TYPE_INFO_COUNT = 2; - static constexpr int METHOD_OFFSET_INDEX = 0; - static constexpr int METHOD_TYPE_INDEX = 1; + explicit PGODecodeMethodInfo(PGOMethodId id) : methodId_(id) {} - PGOMethodTypeSet() = default; - - void AddType(uint32_t offset, PGOSampleType type) + PGOMethodId GetMethodId() const { - if (type.IsClassType()) { - AddClassType(offset, type); - } else { - auto result = scalarOpTypeInfos_.find(ScalarOpTypeInfo(offset, type)); - if (result != scalarOpTypeInfos_.end()) { - auto combineType = result->GetType().CombineType(type); - const_cast(*result).SetType(combineType); - } else { - scalarOpTypeInfos_.emplace(offset, type); - } - } - } - - void AddClassType(uint32_t offset, PGOSampleType type) - { - auto result = rwScalarOpTypeInfos_.find(RWScalarOpTypeInfo(offset)); - if (result != rwScalarOpTypeInfos_.end()) { - const_cast(*result).AddClassType(type); - } else { - rwScalarOpTypeInfos_.emplace(offset, type); - } + return methodId_; } - void AddDefine(uint32_t offset, PGOSampleType type, PGOSampleType superType) + PGOMethodTypeSet &GetPGOMethodTypeSet() { - auto result = objDefOpTypeInfos_.find(ObjDefOpTypeInfo(offset, type, superType)); - if (result != objDefOpTypeInfos_.end()) { - return; - } - objDefOpTypeInfos_.emplace(offset, type, superType); + return pgoMethodTypeSet_; } - template - void GetTypeInfo(Callback callback) - { - for (const auto &typeInfo : scalarOpTypeInfos_) { - auto type = typeInfo.GetType(); - callback(typeInfo.GetOffset(), &type); - } - for (const auto &typeInfo : rwScalarOpTypeInfos_) { - auto type = typeInfo.GetType(); - callback(typeInfo.GetOffset(), &type); - } - for (const auto &typeInfo : objDefOpTypeInfos_) { - auto classType = typeInfo.GetType(); - callback(typeInfo.GetOffset(), &classType); - } - } - - void Merge(const PGOMethodTypeSet *info); - static void SkipFromBinary(void **buffer); - - bool ParseFromBinary(void **buffer); - bool ProcessToBinary(std::stringstream &stream) const; - - bool ParseFromText(const std::string &typeString); - void ProcessToText(std::string &text) const; - - NO_COPY_SEMANTIC(PGOMethodTypeSet); - NO_MOVE_SEMANTIC(PGOMethodTypeSet); + void Merge(const PGODecodeMethodInfo &from); private: - enum class InfoType : uint8_t { NONE, OP_TYPE, DEFINE_CLASS_TYPE = 3, USE_HCLASS_TYPE }; - - class TypeInfoHeader { - public: - TypeInfoHeader(InfoType type, uint32_t offset) : infoType_(type), offset_(offset) {} - TypeInfoHeader(uint32_t size, InfoType type, uint32_t offset) - : size_(size), infoType_(type), offset_(offset) {} - - InfoType GetInfoType() - { - return infoType_; - } - - int32_t Size() const - { - return size_; - } - - uint32_t GetOffset() const - { - return offset_; - } - - protected: - uint32_t size_ {0}; - InfoType infoType_ {InfoType::NONE}; - uint32_t offset_ {0}; - }; - - class RWScalarOpTypeInfo : public TypeInfoHeader { - public: - explicit RWScalarOpTypeInfo(uint32_t offset) - : TypeInfoHeader(InfoType::USE_HCLASS_TYPE, offset) {}; - RWScalarOpTypeInfo(uint32_t offset, PGOSampleType type) - : TypeInfoHeader(sizeof(RWScalarOpTypeInfo), InfoType::USE_HCLASS_TYPE, offset) - { - type_.AddClassType(type.GetClassType()); - } - - bool operator<(const RWScalarOpTypeInfo &right) const - { - return offset_ < right.offset_; - } - - int32_t GetCount() - { - return type_.GetCount(); - } - - void Merge(const RWScalarOpTypeInfo &type) - { - type_.Merge(type.type_); - } - - void AddClassType(const PGOSampleType &type) - { - ASSERT(type.IsClassType()); - type_.AddClassType(type.GetClassType()); - } - - PGORWOpType GetType() const - { - return type_; - } - - void ProcessToText(std::string &text) const; - - private: - PGORWOpType type_; - }; - - class ScalarOpTypeInfo : public TypeInfoHeader { - public: - ScalarOpTypeInfo(uint32_t offset, PGOSampleType type) - : TypeInfoHeader(sizeof(ScalarOpTypeInfo), InfoType::OP_TYPE, offset), type_(type) {} - - bool operator<(const ScalarOpTypeInfo &right) const - { - return offset_ < right.offset_; - } - - void SetType(PGOSampleType type) - { - if (type_ != type) { - type_ = type; - } - } - - void Merge(const ScalarOpTypeInfo &typeInfo) - { - PGOSampleType combineType = GetType().CombineType(typeInfo.GetType()); - SetType(combineType); - } - - PGOSampleType GetType() const - { - return type_; - } - - protected: - ScalarOpTypeInfo(uint32_t size, InfoType infoType, uint32_t offset, PGOSampleType type) - : TypeInfoHeader(size, infoType, offset), type_(type) {} - - private: - PGOSampleType type_; - }; - - class ObjDefOpTypeInfo : public ScalarOpTypeInfo { - public: - ObjDefOpTypeInfo(uint32_t offset, PGOSampleType type, PGOSampleType superType) - : ScalarOpTypeInfo(sizeof(ObjDefOpTypeInfo), InfoType::DEFINE_CLASS_TYPE, offset, type), - superType_(superType) {} - - PGOSampleType GetSuperType() const - { - return superType_; - } - - bool operator<(const ObjDefOpTypeInfo &right) const - { - return offset_ < right.GetOffset() || GetType() < right.GetType() || superType_ < right.superType_; - } - - void ProcessToText(std::string &text) const; - - protected: - ObjDefOpTypeInfo( - uint32_t size, InfoType infoType, uint32_t offset, PGOSampleType type, PGOSampleType superType) - : ScalarOpTypeInfo(size, infoType, offset, type), superType_(superType) {} - - private: - PGOSampleType superType_; - }; - - std::set scalarOpTypeInfos_; - std::set rwScalarOpTypeInfos_; - std::set objDefOpTypeInfos_; -}; - -class PGOHClassLayoutDescInner { -public: - PGOHClassLayoutDescInner(size_t size, PGOSampleType type, PGOSampleType superType) - : size_(size), type_(type), superType_(superType) {} - - static size_t CaculateSize(const PGOHClassLayoutDesc &desc); - static std::string GetTypeString(const PGOHClassLayoutDesc &desc); - - void Merge(const PGOHClassLayoutDesc &desc); - - int32_t Size() const - { - return size_; - } - - PGOSampleType GetType() const - { - return type_; - } - - PGOHClassLayoutDesc Convert() - { - PGOHClassLayoutDesc desc(GetType().GetClassType()); - desc.SetSuperClassType(superType_.GetClassType()); - auto descInfo = GetFirst(); - for (int32_t i = 0; i < count_; i++) { - desc.AddKeyAndDesc(descInfo->GetKey(), descInfo->GetHandler()); - descInfo = GetNext(descInfo); - } - for (int32_t i = 0; i < ptCount_; i++) { - desc.AddPtKeyAndDesc(descInfo->GetKey(), descInfo->GetHandler()); - descInfo = GetNext(descInfo); - } - for (int32_t i = 0; i < ctorCount_; i++) { - desc.AddCtorKeyAndDesc(descInfo->GetKey(), descInfo->GetHandler()); - descInfo = GetNext(descInfo); - } - return desc; - } - - class PGOLayoutDescInfo { - public: - PGOLayoutDescInfo() = default; - PGOLayoutDescInfo(const CString &key, PGOHandler handler) : handler_(handler) - { - size_t len = key.size(); - size_ = Size(len); - if (len > 0 && memcpy_s(&key_, len, key.c_str(), len) != EOK) { - LOG_ECMA(ERROR) << "SetMethodName memcpy_s failed" << key << ", len = " << len; - UNREACHABLE(); - } - *(&key_ + len) = '\0'; - } - - static int32_t Size(size_t len) - { - return sizeof(PGOLayoutDescInfo) + AlignUp(len, ALIGN_SIZE); - } - - int32_t Size() const - { - return size_; - } - - const char *GetKey() const - { - return &key_; - } - - PGOHandler GetHandler() const - { - return handler_; - } - - private: - int32_t size_ {0}; - PGOHandler handler_; - char key_ {'\0'}; - }; - -private: - const PGOLayoutDescInfo *GetFirst() const - { - return &descInfos_; - } - - const PGOLayoutDescInfo *GetNext(const PGOLayoutDescInfo *current) const - { - return reinterpret_cast(reinterpret_cast(current) + current->Size()); - } - - int32_t size_; - PGOSampleType type_; - PGOSampleType superType_; - int32_t count_ {0}; - int32_t ptCount_ {0}; - int32_t ctorCount_ {0}; - PGOLayoutDescInfo descInfos_; + PGOMethodId methodId_ {0}; + PGOMethodTypeSet pgoMethodTypeSet_ {}; }; class PGOMethodInfoMap { @@ -714,171 +275,266 @@ public: methodTypeInfos_.clear(); } - bool AddMethod(Chunk *chunk, EntityId methodId, uint32_t checksum, const CString &methodName, SampleMode mode); - bool AddType(Chunk *chunk, EntityId methodId, int32_t offset, PGOSampleType type); - bool AddDefine(Chunk *chunk, EntityId methodId, int32_t offset, PGOSampleType type, PGOSampleType superType); + bool AddMethod(NativeAreaAllocator *allocator, Method *jsMethod, SampleMode mode, int32_t incCount); + bool AddType(Chunk *chunk, PGOMethodId methodId, int32_t offset, PGOSampleType type); + bool AddCallTargetType(Chunk *chunk, PGOMethodId methodId, int32_t offset, PGOSampleType type); + bool AddObjectInfo(Chunk *chunk, PGOMethodId methodId, int32_t offset, const PGOObjectInfo &info); + bool AddDefine(Chunk *chunk, PGOMethodId methodId, int32_t offset, PGOSampleType type, PGOSampleType superType); void Merge(Chunk *chunk, PGOMethodInfoMap *methodInfos); - bool ParseFromBinary(Chunk *chunk, uint32_t threshold, void **buffer, PGOProfilerHeader *const header); - bool ProcessToBinary(uint32_t threshold, const CString &recordName, const SaveTask *task, std::ofstream &fileStream, - PGOProfilerHeader *const header) const; + bool ParseFromBinary(Chunk *chunk, PGOContext &context, void **buffer); + bool ProcessToBinary(PGOContext &context, ProfileTypeRef recordProfileRef, const SaveTask *task, + std::fstream &fileStream, PGOProfilerHeader *const header) const; bool ParseFromText(Chunk *chunk, uint32_t threshold, const std::vector &content); void ProcessToText(uint32_t threshold, const CString &recordName, std::ofstream &stream) const; + const CMap &GetMethodInfos() const + { + return methodInfos_; + } + NO_COPY_SEMANTIC(PGOMethodInfoMap); NO_MOVE_SEMANTIC(PGOMethodInfoMap); private: - PGOMethodTypeSet *GetOrInsertMethodTypeSet(Chunk *chunk, EntityId methodId); + PGOMethodTypeSet *GetOrInsertMethodTypeSet(Chunk *chunk, PGOMethodId methodId); - CMap methodInfos_; - CMap methodTypeInfos_; - CMap methodsChecksum_; + CMap methodInfos_; + CMap methodTypeInfos_; + CMap methodsChecksum_; CMap> globalLayoutDescInfos_; }; class PGOMethodIdSet { public: - PGOMethodIdSet() = default; + explicit PGOMethodIdSet(Chunk* chunk): chunk_(chunk), methodInfoMap_(chunk) {}; + ~PGOMethodIdSet() = default; - void Clear(NativeAreaAllocator *allocator) + void Clear() { - methodIdSet_.clear(); - for (auto iter : methodTypeSet_) { - allocator->Delete(iter.second); + candidateSet_.clear(); + for (auto &methodNameSet : methodInfoMap_) { + methodNameSet.second.Clear(); } - methodTypeSet_.clear(); + methodInfoMap_.clear(); } bool Match(EntityId methodId) { - return methodIdSet_.find(methodId) != methodIdSet_.end(); + return candidateSet_.find(methodId) != candidateSet_.end(); } template bool Update(const CString &recordName, Callback callback) { - std::unordered_set newIds = callback(recordName, methodIdSet_); + std::unordered_set newIds = callback(recordName, candidateSet_); if (!newIds.empty()) { - methodIdSet_.insert(newIds.begin(), newIds.end()); + candidateSet_.insert(newIds.begin(), newIds.end()); return true; } return false; } template - void GetTypeInfo(EntityId methodId, Callback callback) + void GetTypeInfo(const char *methodName, Callback callback) { - auto iter = methodTypeSet_.find(methodId); - if (iter != methodTypeSet_.end()) { - iter->second->GetTypeInfo(callback); + // for no function checksum in ap file + auto iter = methodInfoMap_.find(methodName); + if ((iter != methodInfoMap_.end()) && (iter->second.GetFirstMethodInfo() != nullptr)) { + iter->second.GetFirstMethodInfo()->GetPGOMethodTypeSet().GetTypeInfo(callback); } } - const std::map &GetMethodTypeSet() const + template + void GetTypeInfo(const char *methodName, uint32_t checksum, Callback callback) { - return methodTypeSet_; + auto iter = methodInfoMap_.find(methodName); + if ((iter != methodInfoMap_.end()) && (iter->second.GetMethodInfo(checksum) != nullptr)) { + return iter->second.GetMethodInfo(checksum)->GetPGOMethodTypeSet().GetTypeInfo(callback); + } + LOG_ECMA(DEBUG) << "Method checksum mismatched, name: " << methodName; } - bool GetMethodIdInPGO(uint32_t checksum, const char *methodName, EntityId &methodId) const + void MatchAndMarkMethod(const char *methodName, EntityId methodId) { - auto iter = methodsChecksumMapping_.find(checksum); - if (iter == methodsChecksumMapping_.end()) { - return false; + const auto &iter = methodInfoMap_.find(methodName); + if (iter == methodInfoMap_.end()) { + // no matching method in PGO file. + return; } - for (const auto &pgoMethodId : iter->second) { - auto methodNameIter = methodIdNameMapping_.find(pgoMethodId); - ASSERT(methodNameIter != methodIdNameMapping_.end()); - if (methodNameIter->second == methodName) { - methodId = pgoMethodId; - return true; + candidateSet_.emplace(methodId); + iter->second.SetMatch(); + } + + bool ParseFromBinary(PGOContext &context, void **buffer); + + void GetMismatchResult(const CString &recordName, uint32_t &totalMethodCount, uint32_t &mismatchMethodCount, + std::set> &mismatchMethodSet) const; + + void Merge(const PGOMethodIdSet &from); + + class PGOMethodNameSet { + public: + explicit PGOMethodNameSet(Chunk* chunk): methodMap_(chunk) {}; + void SetMatch() + { + methodNameMatch_ = true; + } + + bool IsMatch() const + { + return methodNameMatch_; + } + + PGODecodeMethodInfo& GetOrCreateMethodInfo(uint32_t checksum, PGOMethodId methodId) + { + auto methodIter = methodMap_.find(checksum); + if (methodIter == methodMap_.end()) { + auto ret = methodMap_.emplace(checksum, methodId); + ASSERT(ret.second); + methodIter = ret.first; + } + return methodIter->second; + } + + void Merge(const PGOMethodNameSet &from) + { + for (const auto &method : from.methodMap_) { + uint32_t checksum = method.first; + auto methodInfo = methodMap_.find(checksum); + if (methodInfo == methodMap_.end()) { + auto ret = methodMap_.emplace(checksum, method.second.GetMethodId()); + ASSERT(ret.second); + methodInfo = ret.first; + } + methodInfo->second.Merge(method.second); } } - return false; - } - bool ParseFromBinary( - NativeAreaAllocator *allocator, uint32_t threshold, void **buffer, PGOProfilerHeader *const header); + PGODecodeMethodInfo *GetFirstMethodInfo() + { + if (methodMap_.empty()) { + return nullptr; + } + return &(methodMap_.begin()->second); + } + + PGODecodeMethodInfo *GetMethodInfo(uint32_t checksum) + { + auto methodInfo = methodMap_.find(checksum); + if (methodInfo == methodMap_.end()) { + return nullptr; + } + return &(methodInfo->second); + } + + void Clear() + { + methodMap_.clear(); + } + + private: + bool methodNameMatch_ {false}; + ChunkUnorderedMap methodMap_; + }; NO_COPY_SEMANTIC(PGOMethodIdSet); NO_MOVE_SEMANTIC(PGOMethodIdSet); private: - std::unordered_set methodIdSet_; - std::map methodTypeSet_; - std::unordered_map methodIdNameMapping_; - std::unordered_map> methodsChecksumMapping_; + Chunk* chunk_; + std::unordered_set candidateSet_; // methodId in abc file, DO NOT for pgo internal use + ChunkUnorderedMap methodInfoMap_; }; -class PGORecordDetailInfos { +class PGORecordDetailInfos : public PGOContext { public: - explicit PGORecordDetailInfos(uint32_t hotnessThreshold) : hotnessThreshold_(hotnessThreshold) - { - chunk_ = std::make_unique(&nativeAreaAllocator_); - }; + explicit PGORecordDetailInfos(uint32_t hotnessThreshold); - ~PGORecordDetailInfos() - { - Clear(); - } + ~PGORecordDetailInfos() override; - void Clear() - { - for (auto iter : recordInfos_) { - iter.second->Clear(); - nativeAreaAllocator_.Delete(iter.second); - } - recordInfos_.clear(); - chunk_ = std::make_unique(&nativeAreaAllocator_); - } + void Clear(); + void InitSections(); // If it is a new method, return true. - bool AddMethod(const CString &recordName, EntityId methodId, uint32_t checksum, const CString &methodName, - SampleMode mode); - bool AddType(const CString &recordName, EntityId methodId, int32_t offset, PGOSampleType type); + bool AddMethod(ProfileType recordProfileType, Method *jsMethod, SampleMode mode, int32_t incCount); + bool AddType(ProfileType recordProfileType, PGOMethodId methodId, int32_t offset, PGOSampleType type); + bool AddCallTargetType(ProfileType recordProfileType, PGOMethodId methodId, int32_t offset, PGOSampleType type); + bool AddObjectInfo(ProfileType recordProfileType, PGOMethodId methodId, int32_t offset, const PGOObjectInfo &info); bool AddDefine( - const CString &recordName, EntityId methodId, int32_t offset, PGOSampleType type, PGOSampleType superType); - bool AddLayout(PGOSampleType type, JSTaggedType hclass, PGOObjLayoutKind kind); + ProfileType recordProfileType, PGOMethodId methodId, int32_t offset, PGOSampleType type, PGOSampleType superType); + bool AddLayout(PGOSampleType type, JSTaggedType hclass, PGOObjKind kind); + bool UpdateElementsKind(PGOSampleType type, ElementsKind kind); void Merge(const PGORecordDetailInfos &recordInfos); + void UpdateLayout(); + void ParseFromBinary(void *buffer, PGOProfilerHeader *const header); - void ProcessToBinary(const SaveTask *task, std::ofstream &fileStream, PGOProfilerHeader *const header) const; + void ProcessToBinary(const SaveTask *task, std::fstream &fileStream, PGOProfilerHeader *const header); bool ParseFromText(std::ifstream &stream); void ProcessToText(std::ofstream &stream) const; + const CMap &GetRecordInfos() const + { + return recordInfos_; + } + + std::shared_ptr GetRecordPool() const + { + return recordPool_; + } + + std::shared_ptr GetProfileTypePool() const override + { + return profileTypePool_; + } + + uint32_t GetHotnessThreshold() const override + { + return hotnessThreshold_; + } + + PGOProfilerHeader *GetHeader() const override + { + return header_; + } + + bool SupportElementsKind() const override + { + ASSERT(header_ != nullptr); + return header_->SupportElementsKind(); + } + NO_COPY_SEMANTIC(PGORecordDetailInfos); NO_MOVE_SEMANTIC(PGORecordDetailInfos); private: - PGOMethodInfoMap *GetMethodInfoMap(const CString &recordName); + PGOMethodInfoMap *GetMethodInfoMap(ProfileType recordProfileType); bool ParseFromBinaryForLayout(void **buffer); - bool ProcessToBinaryForLayout(NativeAreaAllocator *allocator, const SaveTask *task, std::ofstream &stream) const; + bool ProcessToBinaryForLayout(NativeAreaAllocator *allocator, const SaveTask *task, std::fstream &stream); uint32_t hotnessThreshold_ {2}; NativeAreaAllocator nativeAreaAllocator_; std::unique_ptr chunk_; - CMap recordInfos_; + CMap recordInfos_; std::set moduleLayoutDescInfos_; + PGOProfilerHeader *header_ {nullptr}; + std::list> apSectionList_; + std::shared_ptr recordPool_; + std::shared_ptr profileTypePool_; }; -class PGORecordSimpleInfos { +class PGORecordSimpleInfos : public PGOContext { public: - explicit PGORecordSimpleInfos(uint32_t threshold) : hotnessThreshold_(threshold) {} - ~PGORecordSimpleInfos() - { - Clear(); - } + explicit PGORecordSimpleInfos(uint32_t threshold); - void Clear() - { - for (auto iter : methodIds_) { - iter.second->Clear(&nativeAreaAllocator_); - nativeAreaAllocator_.Delete(iter.second); - } - methodIds_.clear(); - } + ~PGORecordSimpleInfos() override; + + void Clear(); + + void InitSections(); bool Match(const CString &recordName, EntityId methodId); @@ -899,7 +555,7 @@ public: if (iter != methodIds_.end()) { iter->second->Update(recordName, callback); } else { - PGOMethodIdSet *methodIds = nativeAreaAllocator_.New(); + PGOMethodIdSet *methodIds = nativeAreaAllocator_.New(chunk_.get()); if (methodIds->Update(recordName, callback)) { methodIds_.emplace(recordName, methodIds); } else { @@ -909,17 +565,26 @@ public: } template - void GetTypeInfo(const CString &recordName, EntityId methodId, Callback callback) + void GetTypeInfo(const CString &recordName, const char *methodName, Callback callback) { auto iter = methodIds_.find(recordName); if (iter != methodIds_.end()) { - iter->second->GetTypeInfo(methodId, callback); + iter->second->GetTypeInfo(methodName, callback); } } - bool GetHClassLayoutDesc(PGOSampleType classType, PGOHClassLayoutDesc **desc) const + template + void GetTypeInfo(const CString &recordName, const char *methodName, uint32_t checksum, Callback callback) { - auto iter = moduleLayoutDescInfos_.find(PGOHClassLayoutDesc(classType.GetClassType())); + auto iter = methodIds_.find(recordName); + if (iter != methodIds_.end()) { + iter->second->GetTypeInfo(methodName, checksum, callback); + } + } + + bool GetHClassLayoutDesc(PGOSampleType profileType, PGOHClassLayoutDesc **desc) const + { + auto iter = moduleLayoutDescInfos_.find(PGOHClassLayoutDesc(profileType.GetProfileType())); if (iter != moduleLayoutDescInfos_.end()) { *desc = &(const_cast(*iter)); return true; @@ -927,18 +592,48 @@ public: return false; } - bool GetMethodIdInPGO(const CString &recordName, uint32_t checksum, const char *methodName, - EntityId &methodId) const + void MatchAndMarkMethod(const CString &recordName, const char *methodName, EntityId methodId) { auto iter = methodIds_.find(recordName); if (iter != methodIds_.end()) { - return iter->second->GetMethodIdInPGO(checksum, methodName, methodId); + return iter->second->MatchAndMarkMethod(methodName, methodId); + } + } + + void GetMismatchResult(uint32_t &totalMethodCount, uint32_t &mismatchMethodCount, + std::set> &mismatchMethodSet) const + { + for (const auto &methodId : methodIds_) { + methodId.second->GetMismatchResult(methodId.first, totalMethodCount, mismatchMethodCount, + mismatchMethodSet); } - return false; } void ParseFromBinary(void *buffer, PGOProfilerHeader *const header); + void Merge(const PGORecordSimpleInfos &simpleInfos); + + std::shared_ptr GetProfileTypePool() const override + { + return profileTypePool_; + } + + uint32_t GetHotnessThreshold() const override + { + return hotnessThreshold_; + } + + PGOProfilerHeader *GetHeader() const override + { + return header_; + } + + bool SupportElementsKind() const override + { + ASSERT(header_ != nullptr); + return header_->SupportElementsKind(); + } + NO_COPY_SEMANTIC(PGORecordSimpleInfos); NO_MOVE_SEMANTIC(PGORecordSimpleInfos); @@ -947,8 +642,13 @@ private: uint32_t hotnessThreshold_ {2}; NativeAreaAllocator nativeAreaAllocator_; + std::unique_ptr chunk_; CUnorderedMap methodIds_; + PGOProfilerHeader *header_ {nullptr}; + std::list> apSectionList_; + std::shared_ptr recordPool_; + std::shared_ptr profileTypePool_; std::set moduleLayoutDescInfos_; }; -} // namespace panda::ecmascript +} // namespace panda::ecmascript::pgo #endif // ECMASCRIPT_PGO_PROFILER_INFO_H diff --git a/ecmascript/pgo_profiler/pgo_profiler_layout.cpp b/ecmascript/pgo_profiler/pgo_profiler_layout.cpp index 9cf761979d7269f3b963c3de7996ff4100635f58..0b9606752faea2e130adf0fdc1fb70cd1a1facd1 100644 --- a/ecmascript/pgo_profiler/pgo_profiler_layout.cpp +++ b/ecmascript/pgo_profiler/pgo_profiler_layout.cpp @@ -15,17 +15,23 @@ #include "ecmascript/pgo_profiler/pgo_profiler_layout.h" -namespace panda::ecmascript { -void PGOHClassLayoutDesc::UpdateKeyAndDesc(const CString &key, const PGOHandler &handler, PGOObjLayoutKind kind) +namespace panda::ecmascript::pgo { +void PGOHClassLayoutDesc::UpdateElementKind(const ElementsKind kind) +{ + kind_ = kind; +} + +void PGOHClassLayoutDesc::UpdateKeyAndDesc(const CString &key, const PGOHandler &handler, PGOObjKind kind) { switch (kind) { - case PGOObjLayoutKind::LOCAL: + case PGOObjKind::LOCAL: + case PGOObjKind::ELEMENT: UpdateKeyAndDesc(key, handler, layoutDesc_); break; - case PGOObjLayoutKind::PROTOTYPE: + case PGOObjKind::PROTOTYPE: UpdateKeyAndDesc(key, handler, ptLayoutDesc_); break; - case PGOObjLayoutKind::CONSTRUCTOR: + case PGOObjKind::CONSTRUCTOR: UpdateKeyAndDesc(key, handler, ctorLayoutDesc_); break; default: @@ -47,13 +53,13 @@ bool PGOHClassLayoutDesc::FindDescWithKey(const CString &key, PGOHandler &handle void PGOHClassLayoutDesc::Merge(const PGOHClassLayoutDesc &from) { for (const auto &iter : from.layoutDesc_) { - UpdateKeyAndDesc(iter.first, iter.second, PGOObjLayoutKind::LOCAL); + UpdateKeyAndDesc(iter.first, iter.second, PGOObjKind::LOCAL); } for (const auto &iter : from.ptLayoutDesc_) { - UpdateKeyAndDesc(iter.first, iter.second, PGOObjLayoutKind::PROTOTYPE); + UpdateKeyAndDesc(iter.first, iter.second, PGOObjKind::PROTOTYPE); } for (const auto &iter : from.ctorLayoutDesc_) { - UpdateKeyAndDesc(iter.first, iter.second, PGOObjLayoutKind::CONSTRUCTOR); + UpdateKeyAndDesc(iter.first, iter.second, PGOObjKind::CONSTRUCTOR); } } @@ -61,22 +67,36 @@ void PGOHClassLayoutDesc::UpdateKeyAndDesc(const CString &key, const PGOHandler { for (auto &iter : layoutDesc) { if (iter.first == key) { - PGOHandler oldType = iter.second; - if (oldType == handler) { + PGOHandler oldHandler = iter.second; + if (oldHandler == handler) { return; } - if (oldType.GetTrackType() == TrackType::NONE) { - iter.second = handler; - } else { - if (oldType.GetTrackType() != handler.GetTrackType()) { - iter.second = PGOHandler(TrackType::TAGGED, handler.IsAccessor()); - } else { + auto oldTrackType = oldHandler.GetTrackType(); + auto newTrackType = handler.GetTrackType(); + if (oldTrackType == newTrackType) { + iter.second.SetIsAccessor(handler.IsAccessor()); + return; + } + + switch (oldTrackType) { + case TrackType::TAGGED: iter.second.SetIsAccessor(handler.IsAccessor()); - } + break; + case TrackType::NONE: + case TrackType::INT: + case TrackType::DOUBLE: + if (newTrackType != TrackType::TAGGED) { + newTrackType = static_cast(static_cast(newTrackType) | + static_cast(oldTrackType)); + } + iter.second = PGOHandler(newTrackType, handler.IsAccessor()); + break; + default: + break; } return; } } layoutDesc.emplace_back(key, handler); } -} // namespace panda::ecmascript +} // namespace panda::ecmascript::pgo diff --git a/ecmascript/pgo_profiler/pgo_profiler_layout.h b/ecmascript/pgo_profiler/pgo_profiler_layout.h index 556001ae3058012a12ca96972393b0af7c2f591d..8b61f81f4c138092dd94db06028a6a9e69e068c7 100644 --- a/ecmascript/pgo_profiler/pgo_profiler_layout.h +++ b/ecmascript/pgo_profiler/pgo_profiler_layout.h @@ -16,17 +16,20 @@ #ifndef ECMASCRIPT_PGO_PROFILER_LAYOUT_H #define ECMASCRIPT_PGO_PROFILER_LAYOUT_H -#include +#include #include +#include "ecmascript/elements.h" #include "ecmascript/mem/c_containers.h" -#include "ecmascript/pgo_profiler/pgo_profiler_type.h" +#include "ecmascript/pgo_profiler/pgo_context.h" +#include "ecmascript/pgo_profiler/pgo_utils.h" +#include "ecmascript/pgo_profiler/types/pgo_profiler_type.h" #include "ecmascript/property_attributes.h" -namespace panda::ecmascript { +namespace panda::ecmascript::pgo { class PGOHandler { public: - using TrackTypeField = BitField; // 2 : two binary bits + using TrackTypeField = BitField; // 3 : three binary bits using IsAccessorField = TrackTypeField::NextFlag; PGOHandler() @@ -46,6 +49,28 @@ public: return value_; } + bool SetAttribute(PropertyAttributes &attr) const + { + bool ret = false; + switch (GetTrackType()) { + case TrackType::DOUBLE: + case TrackType::NUMBER: + attr.SetRepresentation(Representation::DOUBLE); + ret = true; + break; + case TrackType::INT: + attr.SetRepresentation(Representation::INT); + ret = true; + break; + case TrackType::TAGGED: + attr.SetRepresentation(Representation::TAGGED); + break; + default: + break; + } + return ret; + } + void SetTrackType(TrackType type) { TrackTypeField::Set(type, &value_); @@ -82,28 +107,23 @@ private: using PropertyDesc = std::pair; using LayoutDesc = CVector; -enum class PGOObjLayoutKind { - LOCAL, - PROTOTYPE, - CONSTRUCTOR, -}; class PGOHClassLayoutDesc { public: PGOHClassLayoutDesc() {}; - explicit PGOHClassLayoutDesc(ClassType type) : type_(type) {} + explicit PGOHClassLayoutDesc(ProfileType type) : type_(type) {} - void SetSuperClassType(ClassType superType) + void SetSuperProfileType(ProfileType superType) { superType_ = superType; } - ClassType GetSuperClassType() const + ProfileType GetSuperProfileType() const { return superType_; } - ClassType GetClassType() const + ProfileType GetProfileType() const { return type_; } @@ -128,7 +148,17 @@ public: return ctorLayoutDesc_; } - bool FindProperty(const CString &key, PropertyDesc desc) const + ElementsKind GetElementsKind() const + { + return kind_; + } + + void SetElementsKind(ElementsKind kind) + { + kind_ = kind; + } + + bool FindProperty(const CString &key, PropertyDesc &desc) const { for (const auto &iter : layoutDesc_) { if (iter.first == key) { @@ -154,7 +184,8 @@ public: ctorLayoutDesc_.emplace_back(key, handler); } - void UpdateKeyAndDesc(const CString &key, const PGOHandler &handler, PGOObjLayoutKind kind); + void UpdateElementKind(const ElementsKind kind); + void UpdateKeyAndDesc(const CString &key, const PGOHandler &handler, PGOObjKind kind); bool FindDescWithKey(const CString &key, PGOHandler &handler) const; @@ -168,11 +199,264 @@ public: private: void UpdateKeyAndDesc(const CString &key, const PGOHandler &handler, LayoutDesc &layoutDesc); - ClassType type_; - ClassType superType_; + ProfileType type_; + ProfileType superType_; + ElementsKind kind_; LayoutDesc layoutDesc_; LayoutDesc ptLayoutDesc_; LayoutDesc ctorLayoutDesc_; }; -} // namespace panda::ecmascript + +template +class PGOHClassLayoutTemplate { +public: + PGOHClassLayoutTemplate(size_t size, SampleType type, SampleType superType, ElementsKind kind) + : size_(size), type_(type), superType_(superType) + { + SetElementsKind(kind); + } + + static size_t CaculateSize(const PGOHClassLayoutDesc &desc) + { + if (desc.GetLayoutDesc().empty() && desc.GetPtLayoutDesc().empty() && desc.GetCtorLayoutDesc().empty()) { + return sizeof(PGOHClassLayoutTemplate); + } + size_t size = sizeof(PGOHClassLayoutTemplate) - sizeof(PGOLayoutDescInfo); + for (const auto &iter : desc.GetLayoutDesc()) { + auto key = iter.first; + if (key.size() > 0) { + size += static_cast(PGOLayoutDescInfo::Size(key.size())); + } + } + for (const auto &iter : desc.GetPtLayoutDesc()) { + auto key = iter.first; + if (key.size() > 0) { + size += static_cast(PGOLayoutDescInfo::Size(key.size())); + } + } + for (const auto &iter : desc.GetCtorLayoutDesc()) { + auto key = iter.first; + if (key.size() > 0) { + size += static_cast(PGOLayoutDescInfo::Size(key.size())); + } + } + size += sizeof(ElementsKind); + return size; + } + static std::string GetTypeString(const PGOHClassLayoutDesc &desc) + { + std::string text; + text += desc.GetProfileType().GetTypeString(); + if (!desc.GetSuperProfileType().IsNone()) { + text += DumpUtils::TYPE_SEPARATOR + DumpUtils::SPACE; + text += desc.GetSuperProfileType().GetTypeString(); + } + if (!Elements::IsNone(desc.GetElementsKind())) { + text += DumpUtils::TYPE_SEPARATOR + DumpUtils::SPACE; + text += Elements::GetString(desc.GetElementsKind()); + } + text += DumpUtils::BLOCK_AND_ARRAY_START; + bool isLayoutFirst = true; + for (const auto &layoutDesc : desc.GetLayoutDesc()) { + if (!isLayoutFirst) { + text += DumpUtils::TYPE_SEPARATOR + DumpUtils::SPACE; + } else { + text += DumpUtils::ARRAY_START; + } + isLayoutFirst = false; + text += layoutDesc.first; + text += DumpUtils::BLOCK_START; + text += std::to_string(layoutDesc.second.GetValue()); + } + if (!isLayoutFirst) { + text += DumpUtils::ARRAY_END; + } + bool isPtLayoutFirst = true; + for (const auto &layoutDesc : desc.GetPtLayoutDesc()) { + if (!isPtLayoutFirst) { + text += DumpUtils::TYPE_SEPARATOR + DumpUtils::SPACE; + } else { + if (!isLayoutFirst) { + text += DumpUtils::TYPE_SEPARATOR + DumpUtils::SPACE; + } + text += DumpUtils::ARRAY_START; + } + isPtLayoutFirst = false; + text += layoutDesc.first; + text += DumpUtils::BLOCK_START; + text += std::to_string(layoutDesc.second.GetValue()); + } + if (!isPtLayoutFirst) { + text += DumpUtils::ARRAY_END; + } + bool isCtorLayoutFirst = true; + for (const auto &layoutDesc : desc.GetCtorLayoutDesc()) { + if (!isCtorLayoutFirst) { + text += DumpUtils::TYPE_SEPARATOR + DumpUtils::SPACE; + } else { + if (!isLayoutFirst || !isPtLayoutFirst) { + text += DumpUtils::TYPE_SEPARATOR + DumpUtils::SPACE; + } + text += DumpUtils::ARRAY_START; + } + isCtorLayoutFirst = false; + text += layoutDesc.first; + text += DumpUtils::BLOCK_START; + text += std::to_string(layoutDesc.second.GetValue()); + } + if (!isCtorLayoutFirst) { + text += DumpUtils::ARRAY_END; + } + text += (DumpUtils::SPACE + DumpUtils::ARRAY_END); + return text; + } + + void Merge(const PGOHClassLayoutDesc &desc) + { + auto current = const_cast(GetFirst()); + for (const auto &iter : desc.GetLayoutDesc()) { + auto key = iter.first; + auto type = iter.second; + if (key.size() > 0) { + new (current) PGOLayoutDescInfo(key, type); + current = const_cast(GetNext(current)); + count_++; + } + } + for (const auto &iter : desc.GetPtLayoutDesc()) { + auto key = iter.first; + auto type = iter.second; + if (key.size() > 0) { + new (current) PGOLayoutDescInfo(key, type); + current = const_cast(GetNext(current)); + ptCount_++; + } + } + for (const auto &iter : desc.GetCtorLayoutDesc()) { + auto key = iter.first; + auto type = iter.second; + if (key.size() > 0) { + new (current) PGOLayoutDescInfo(key, type); + current = const_cast(GetNext(current)); + ctorCount_++; + } + } + } + + int32_t Size() const + { + return size_; + } + + SampleType GetType() const + { + return type_; + } + + SampleType GetSuperType() const + { + return superType_; + } + + PGOHClassLayoutDesc Convert(PGOContext& context) + { + PGOHClassLayoutDesc desc(ProfileType(context, GetType().GetProfileType())); + desc.SetSuperProfileType(ProfileType(context, superType_.GetProfileType())); + auto descInfo = GetFirst(); + for (int32_t i = 0; i < count_; i++) { + desc.AddKeyAndDesc(descInfo->GetKey(), descInfo->GetHandler()); + descInfo = GetNext(descInfo); + } + for (int32_t i = 0; i < ptCount_; i++) { + desc.AddPtKeyAndDesc(descInfo->GetKey(), descInfo->GetHandler()); + descInfo = GetNext(descInfo); + } + for (int32_t i = 0; i < ctorCount_; i++) { + desc.AddCtorKeyAndDesc(descInfo->GetKey(), descInfo->GetHandler()); + descInfo = GetNext(descInfo); + } + if (context.SupportElementsKind()) { + desc.SetElementsKind(GetElementsKind()); + } + return desc; + } + + class PGOLayoutDescInfo { + public: + PGOLayoutDescInfo() = default; + PGOLayoutDescInfo(const CString &key, PGOHandler handler) : handler_(handler) + { + size_t len = key.size(); + size_ = Size(len); + if (len > 0 && memcpy_s(&key_, len, key.c_str(), len) != EOK) { + LOG_ECMA(ERROR) << "SetMethodName memcpy_s failed" << key << ", len = " << len; + UNREACHABLE(); + } + *(&key_ + len) = '\0'; + } + + static int32_t Size(size_t len) + { + return sizeof(PGOLayoutDescInfo) + AlignUp(len, GetAlignmentInBytes(ALIGN_SIZE)); + } + + int32_t Size() const + { + return size_; + } + + const char *GetKey() const + { + return &key_; + } + + PGOHandler GetHandler() const + { + return handler_; + } + + private: + int32_t size_ {0}; + PGOHandler handler_; + char key_ {'\0'}; + }; + +private: + const PGOLayoutDescInfo *GetFirst() const + { + return &descInfos_; + } + + const PGOLayoutDescInfo *GetNext(const PGOLayoutDescInfo *current) const + { + return reinterpret_cast(reinterpret_cast(current) + current->Size()); + } + + void SetElementsKind(ElementsKind kind) + { + *reinterpret_cast(GetEnd() - sizeof(ElementsKind)) = kind; + } + + ElementsKind GetElementsKind() const + { + return *reinterpret_cast(GetEnd() - sizeof(ElementsKind)); + } + + uintptr_t GetEnd() const + { + return reinterpret_cast(this) + Size(); + } + + int32_t size_; + SampleType type_; + SampleType superType_; + int32_t count_ {0}; + int32_t ptCount_ {0}; + int32_t ctorCount_ {0}; + PGOLayoutDescInfo descInfos_; +}; + +using PGOHClassLayoutDescInner = PGOHClassLayoutTemplate; +using PGOHClassLayoutDescInnerRef = PGOHClassLayoutTemplate; +} // namespace panda::ecmascript::pgo #endif // ECMASCRIPT_PGO_PROFILER_LAYOUT_H diff --git a/ecmascript/pgo_profiler/pgo_profiler_manager.cpp b/ecmascript/pgo_profiler/pgo_profiler_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..41d81422b4ca9f0f165a2bcd7f676a7a26b5f3b3 --- /dev/null +++ b/ecmascript/pgo_profiler/pgo_profiler_manager.cpp @@ -0,0 +1,124 @@ +/* + * 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/pgo_profiler/pgo_profiler_manager.h" + +#include "ecmascript/log.h" +#include "ecmascript/log_wrapper.h" +#include "ecmascript/mem/mem.h" +#include "ecmascript/platform/file.h" +namespace panda::ecmascript::pgo { +namespace { + constexpr int32_t PGO_SAVING_SIGNAL = 50; +} // namespace + +bool PGOProfilerManager::MergeApFiles(const std::string &inFiles, const std::string &outPath, uint32_t hotnessThreshold, + ApGenMode mode) +{ + arg_list_t pandaFileNames = base::StringHelper::SplitString(inFiles, GetFileDelimiter()); + PGOProfilerEncoder merger(outPath, hotnessThreshold, mode); + if (!merger.InitializeData()) { + LOG_ECMA(ERROR) << "PGO Profiler encoder initialized failed. outPath: " << outPath + << " ,hotnessThreshold: " << hotnessThreshold; + return false; + } + bool isFirstFile = true; + std::string firstApFileName; + for (const auto &fileName : pandaFileNames) { + if (!base::StringHelper::EndsWith(fileName, ".ap")) { + LOG_ECMA(ERROR) << "The file path(" << fileName << ") does not end with .ap"; + continue; + } + PGOProfilerDecoder decoder(fileName, hotnessThreshold); + if (!decoder.LoadFull()) { + LOG_ECMA(ERROR) << "Fail to load file path(" << fileName << "), skip it."; + continue; + } + if (isFirstFile) { + firstApFileName = fileName; + } else { + if (!merger.VerifyPandaFileMatched(decoder.GetPandaFileInfos(), firstApFileName, fileName)) { + continue; + } + } + merger.Merge(decoder.GetRecordDetailInfos()); + merger.Merge(decoder.GetPandaFileInfos()); + isFirstFile = false; + } + if (isFirstFile) { + LOG_ECMA(ERROR) << "No input file processed. Input files: " << inFiles; + return false; + } + merger.Save(); + return true; +} + +bool PGOProfilerManager::MergeApFiles(uint32_t checksum, PGOProfilerDecoder &merger) +{ + uint32_t hotnessThreshold = merger.GetHotnessThreshold(); + std::string inFiles(merger.GetInPath()); + arg_list_t pandaFileNames = base::StringHelper::SplitString(inFiles, GetFileDelimiter()); + if (pandaFileNames.empty()) { + return true; + } + merger.InitMergeData(); + bool isFirstFile = true; + std::string firstApFileName; + for (const auto &fileName : pandaFileNames) { + PGOProfilerDecoder decoder(fileName, hotnessThreshold); + if (!decoder.LoadAndVerify(checksum)) { + LOG_ECMA(ERROR) << "Load and verify file(" << fileName << ") failed, skip it."; + continue; + } + if (isFirstFile) { + firstApFileName = fileName; + } else { + if (!merger.GetPandaFileInfos().VerifyChecksum(decoder.GetPandaFileInfos(), firstApFileName, fileName)) { + continue; + } + } + merger.Merge(decoder); + isFirstFile = false; + } + if (isFirstFile) { + LOG_ECMA(ERROR) << "No input file processed. Input files: " << inFiles; + return false; + } + return true; +} + +void PGOProfilerManager::RegisterSavingSignal() +{ + LOG_ECMA(INFO) << "Register Pgo Saving Signal"; + if (encoder_ == nullptr) { + LOG_ECMA(ERROR) << "Can not register pgo saving signal, because encoder is null."; + return; + } + if (!encoder_->IsInitialized()) { + LOG_ECMA(DEBUG) << "Can not register pgo saving signal, because encoder is initialized."; + return; + } + signal(PGO_SAVING_SIGNAL, SavingSignalHandler); + enableSignalSaving_ = true; +} + +void PGOProfilerManager::SavingSignalHandler(int signo) +{ + if (signo != PGO_SAVING_SIGNAL) { + return; + } + PGOProfilerManager::GetInstance()->AsynSave(); +} +} // namespace panda::ecmascript::pgo diff --git a/ecmascript/pgo_profiler/pgo_profiler_manager.h b/ecmascript/pgo_profiler/pgo_profiler_manager.h index c97dda70d7bf6833ef2c5675d222a014aa76dbe2..55845a0682bf033a4930e77848a92a85101bb815 100644 --- a/ecmascript/pgo_profiler/pgo_profiler_manager.h +++ b/ecmascript/pgo_profiler/pgo_profiler_manager.h @@ -16,21 +16,26 @@ #ifndef ECMASCRIPT_PGO_PROFILER_MANAGER_H #define ECMASCRIPT_PGO_PROFILER_MANAGER_H +#include +#include #include #include "ecmascript/pgo_profiler/pgo_profiler.h" #include "ecmascript/pgo_profiler/pgo_profiler_decoder.h" #include "ecmascript/pgo_profiler/pgo_profiler_encoder.h" -namespace panda::ecmascript { +namespace panda::ecmascript::pgo { class PGOProfilerManager { public: + using ApGenMode = PGOProfilerEncoder::ApGenMode; static PGOProfilerManager *GetInstance() { static PGOProfilerManager instance; return &instance; } + static void SavingSignalHandler(int signo); + PGOProfilerManager() = default; ~PGOProfilerManager() = default; @@ -39,7 +44,8 @@ public: void Initialize(const std::string &outDir, uint32_t hotnessThreshold) { - encoder_ = std::make_unique(outDir, hotnessThreshold); + // For FA jsvm, merge with existed output file + encoder_ = std::make_unique(outDir, hotnessThreshold, ApGenMode::OVERWRITE); } void Destroy() @@ -68,6 +74,8 @@ public: void Destroy(PGOProfiler *profiler) { if (profiler != nullptr) { + profiler->HandlePGOPreDump(); + profiler->WaitPGODumpFinish(); Merge(profiler); delete profiler; } @@ -83,10 +91,32 @@ public: } } - void SamplePandaFileInfo(uint32_t checksum) + void SamplePandaFileInfo(uint32_t checksum, const CString &abcName) + { + if (encoder_) { + encoder_->SamplePandaFileInfo(checksum, abcName); + } + } + + void SetModuleName(const std::string &moduleName) + { + if (encoder_) { + encoder_->ResetOutPathByModuleName(moduleName); + } + } + + bool GetPandaFileId(const CString &abcName, ApEntityId &entryId) const { if (encoder_) { - encoder_->SamplePandaFileInfo(checksum); + return encoder_->GetPandaFileId(abcName, entryId); + } + return false; + } + + void SetApGenMode(ApGenMode mode) + { + if (encoder_) { + encoder_->SetApGenMode(mode); } } @@ -98,6 +128,8 @@ public: } } + void RegisterSavingSignal(); + void AsynSave() { if (encoder_) { @@ -105,18 +137,26 @@ public: } } - bool PUBLIC_API TextToBinary(const std::string &inPath, const std::string &outPath, uint32_t hotnessThreshold) + bool PUBLIC_API TextToBinary(const std::string &inPath, const std::string &outPath, uint32_t hotnessThreshold, + ApGenMode mode) { - PGOProfilerEncoder encoder(outPath, hotnessThreshold); + PGOProfilerEncoder encoder(outPath, hotnessThreshold, mode); + PGOProfilerEncoder decoder(outPath, hotnessThreshold, mode); if (!encoder.InitializeData()) { LOG_ECMA(ERROR) << "PGO Profiler encoder initialized failed"; return false; } - bool ret = encoder.LoadAPTextFile(inPath); + if (!decoder.InitializeData()) { + LOG_ECMA(ERROR) << "PGO Profiler decoder initialized failed"; + return false; + } + bool ret = decoder.LoadAPTextFile(inPath); if (ret) { + encoder.Merge(decoder); ret = encoder.Save(); } encoder.Destroy(); + decoder.Destroy(); return ret; } @@ -131,16 +171,24 @@ public: return ret; } + static bool MergeApFiles(const std::string &inFiles, const std::string &outPath, uint32_t hotnessThreshold, + ApGenMode mode); + static bool MergeApFiles(uint32_t checksum, PGOProfilerDecoder &merger); + private: bool InitializeData() { if (!encoder_) { return false; } + if (!enableSignalSaving_) { + RegisterSavingSignal(); + } return encoder_->InitializeData(); } std::unique_ptr encoder_; + std::atomic_bool enableSignalSaving_ { false }; }; -} // namespace panda::ecmascript +} // namespace panda::ecmascript::pgo #endif // ECMASCRIPT_PGO_PROFILER_MANAGER_H diff --git a/ecmascript/pgo_profiler/pgo_utils.cpp b/ecmascript/pgo_profiler/pgo_utils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..534c00815992336ed079c162238376b5b79f98f4 --- /dev/null +++ b/ecmascript/pgo_profiler/pgo_utils.cpp @@ -0,0 +1,34 @@ +/* + * 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/pgo_profiler/pgo_utils.h" +#include + +namespace panda::ecmascript::pgo { + +const std::string DumpUtils::ELEMENT_SEPARATOR = "/"; +const std::string DumpUtils::BLOCK_SEPARATOR = ","; +const std::string DumpUtils::TYPE_SEPARATOR = "|"; +const std::string DumpUtils::BLOCK_START = ":"; +const std::string DumpUtils::ARRAY_START = "["; +const std::string DumpUtils::ARRAY_END = "]"; +const std::string DumpUtils::NEW_LINE = "\n"; +const std::string DumpUtils::SPACE = " "; +const std::string DumpUtils::BLOCK_AND_ARRAY_START = BLOCK_START + SPACE + ARRAY_START + SPACE; +const std::string DumpUtils::VERSION_HEADER = "Profiler Version" + BLOCK_START + SPACE; +const std::string DumpUtils::PANDA_FILE_INFO_HEADER = "Panda file sumcheck list" + BLOCK_AND_ARRAY_START; +const uint32_t DumpUtils::HEX_FORMAT_WIDTH_FOR_32BITS = 10; // for example, 0xffffffff is 10 characters + +} // namespace panda::ecmascript::pgo \ No newline at end of file diff --git a/ecmascript/pgo_profiler/pgo_utils.h b/ecmascript/pgo_profiler/pgo_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..0bbc426603720129b5af9b490447223e3e86d10b --- /dev/null +++ b/ecmascript/pgo_profiler/pgo_utils.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_PGO_PROFILER_PGO_UTILS_H +#define ECMASCRIPT_PGO_PROFILER_PGO_UTILS_H + +#include + +#include "mem/mem.h" +#include "libpandafile/file.h" + +namespace panda::ecmascript::pgo { +static constexpr Alignment ALIGN_SIZE = Alignment::LOG_ALIGN_4; +using PGOMethodId = panda_file::File::EntityId; +using ApEntityId = uint32_t; + +class DumpUtils { +public: + static const std::string ELEMENT_SEPARATOR; + static const std::string BLOCK_SEPARATOR; + static const std::string TYPE_SEPARATOR; + static const std::string BLOCK_START; + static const std::string ARRAY_START; + static const std::string ARRAY_END; + static const std::string NEW_LINE; + static const std::string SPACE; + static const std::string BLOCK_AND_ARRAY_START; + static const std::string VERSION_HEADER; + static const std::string PANDA_FILE_INFO_HEADER; + static const uint32_t HEX_FORMAT_WIDTH_FOR_32BITS; +}; +} // namespace panda::ecmascript::pgo +#endif // ECMASCRIPT_PGO_PROFILER_PGO_UTILS_H \ No newline at end of file diff --git a/ecmascript/pgo_profiler/prof_dump/main.cpp b/ecmascript/pgo_profiler/prof_dump/main.cpp index 2c9037d5d4d7c2c12246261b94b6db510dc7b783..d999b001700e17871580e2e4933f660666b8a990 100644 --- a/ecmascript/pgo_profiler/prof_dump/main.cpp +++ b/ecmascript/pgo_profiler/prof_dump/main.cpp @@ -19,10 +19,12 @@ #include "ecmascript/ecma_macros.h" #include "ecmascript/log_wrapper.h" #include "ecmascript/pgo_profiler/pgo_profiler_manager.h" +#include "ecmascript/platform/file.h" -namespace panda::ecmascript { +namespace panda::ecmascript::pgo { static const std::string VERSION = "0.0.0.1"; static const int MIN_PARAM_COUNT = 3; +using ApGenMode = PGOProfilerEncoder::ApGenMode; class Option { public: @@ -30,6 +32,7 @@ public: VERSION_QUERY, TO_BINARY, TO_TEXT, + MERGE, }; std::string GetProfInPath() const @@ -62,6 +65,7 @@ public: {"text", required_argument, nullptr, 't'}, {"binary", required_argument, nullptr, 'b'}, {"hotness-threshold", required_argument, nullptr, 's'}, + {"merge", no_argument, nullptr, 'm'}, {"help", no_argument, nullptr, 'h'}, {"version", no_argument, nullptr, 'v'}, {nullptr, 0, nullptr, 0}, @@ -83,6 +87,9 @@ public: return false; } break; + case 'm': + mode_ = Mode::MERGE; + break; case 'h': return false; case 'v': @@ -112,6 +119,7 @@ public: "-t, --text binary to text.\n" "-b, --binary text to binary.\n" "-s, --hotness-threshold set minimum number of calls to filter method. default: 2\n" + "-m, --merge merge multi binary ap files into one.\n" "-h, --help display this help and exit\n" "-v, --version output version information and exit\n"; return PROF_DUMP_HELP_HEAD_MSG + PROF_DUMP_HELP_OPTION_MSG; @@ -150,22 +158,30 @@ int Main(const int argc, const char **argv) break; } case Option::Mode::TO_BINARY: { - if (PGOProfilerManager::GetInstance()->TextToBinary(option.GetProfInPath(), - option.GetProfOutPath(), option.GetHotnessThreshold())) { + if (PGOProfilerManager::GetInstance()->TextToBinary(option.GetProfInPath(), option.GetProfOutPath(), + option.GetHotnessThreshold(), ApGenMode::OVERWRITE)) { LOG_NO_TAG(ERROR) << "profiler dump to binary success!"; } else { LOG_NO_TAG(ERROR) << "profiler dump to binary failed!"; } break; } + case Option::Mode::MERGE: { + // For CLI tools, do not merge with existed output file + if (PGOProfilerManager::MergeApFiles(option.GetProfInPath(), option.GetProfOutPath(), + option.GetHotnessThreshold(), ApGenMode::OVERWRITE)) { + LOG_NO_TAG(ERROR) << "profiler merge success!"; + } + break; + } default: break; } return 0; } -} // namespace panda::ecmascript +} // namespace panda::ecmascript::pgo int main(int argc, const char **argv) { - return panda::ecmascript::Main(argc, argv); + return panda::ecmascript::pgo::Main(argc, argv); } diff --git a/ecmascript/pgo_profiler/tests/BUILD.gn b/ecmascript/pgo_profiler/tests/BUILD.gn index dcd1868b1bb9fc11cd5e1b0d3b64972f1235c4ad..996965fa2cfe255ed9dd5d8639dbb6bbdfa46b7c 100644 --- a/ecmascript/pgo_profiler/tests/BUILD.gn +++ b/ecmascript/pgo_profiler/tests/BUILD.gn @@ -16,6 +16,35 @@ import("//arkcompiler/ets_runtime/test/test_helper.gni") module_output_path = "arkcompiler/ets_runtime" +test_js_path = + "//arkcompiler/ets_runtime/ecmascript/pgo_profiler/tests/pgo_test_case/" + +test_js_files = [ + "op_type_test", + "class_test", + "call_test", + "array_test", + "object_literal", +] + +foreach(file, test_js_files) { + es2abc_gen_abc("gen_${file}_abc") { + test_js = "${test_js_path}${file}.js" + test_abc = "$target_out_dir/${file}.abc" + + # Only targets in this file can depend on this. + extra_visibility = [ ":*" ] + src_js = rebase_path(test_js) + dst_file = rebase_path(test_abc) + extra_args = [] + extra_args += [ "--module" ] + extra_args += [ "--merge-abc" ] + extra_args += [ "--type-extractor" ] + in_puts = [ test_js ] + out_puts = [ test_abc ] + } +} + host_unittest_action("PGOProfilerTest") { module_out_path = module_output_path @@ -38,6 +67,17 @@ host_unittest_action("PGOProfilerTest") { sdk_libc_secshared_dep, ] + foreach(file, test_js_files) { + deps += [ ":gen_${file}_abc" ] + } + + if (is_ohos && is_standard_system) { + test_abc_dir = "/data/test" + } else { + test_abc_dir = rebase_path(target_out_dir) + } + defines = [ "TARGET_ABC_PATH=\"${test_abc_dir}/\"" ] + # hiviewdfx libraries external_deps = hiviewdfx_ext_deps deps += hiviewdfx_deps diff --git a/ecmascript/pgo_profiler/tests/pgo_context_mock.h b/ecmascript/pgo_profiler/tests/pgo_context_mock.h new file mode 100644 index 0000000000000000000000000000000000000000..da19583dd33294cfabe3e6f6428c9068fc713c0b --- /dev/null +++ b/ecmascript/pgo_profiler/tests/pgo_context_mock.h @@ -0,0 +1,68 @@ +/* + * 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_PGO_PROFILER_TESTS_PGO_CONTEXT_MOCK_H +#define ECMASCRIPT_PGO_PROFILER_TESTS_PGO_CONTEXT_MOCK_H + +#include + +#include "ecmascript/log_wrapper.h" +#include "ecmascript/pgo_profiler/pgo_context.h" +#include "ecmascript/base/file_header.h" +#include "ecmascript/pgo_profiler/pgo_profiler_info.h" +#include "ecmascript/pgo_profiler/pgo_utils.h" + +namespace panda::ecmascript::pgo { +class PGOContextMock : public PGOContext { +public: + explicit PGOContextMock(base::FileHeaderBase::VersionType version) + { + profileTypePool_ = std::make_shared(); + PGOProfilerHeader::Build(&header_, sizeof(PGOProfilerHeader)); + header_->SetVersion(version); + } + ~PGOContextMock() override + { + delete header_; + header_ = nullptr; + }; + + std::shared_ptr GetProfileTypePool() const override + { + return profileTypePool_; + } + uint32_t GetHotnessThreshold() const override + { + return threshold_; + } + PGOProfilerHeader *GetHeader() const override + { + return header_; + } + bool SupportElementsKind() const override + { + ASSERT(header_ != nullptr); + return header_->SupportElementsKind(); + } + +private: + NO_COPY_SEMANTIC(PGOContextMock); + NO_MOVE_SEMANTIC(PGOContextMock); + std::shared_ptr profileTypePool_; + PGOProfilerHeader *header_ {}; + uint32_t threshold_ {}; +}; +} // namespace panda::ecmascript::pgo +#endif \ No newline at end of file diff --git a/ecmascript/pgo_profiler/tests/pgo_profiler_test.cpp b/ecmascript/pgo_profiler/tests/pgo_profiler_test.cpp index f59c683ee49d1b50a7cbe4964d6141e4f46b054c..2b5e8ebea259aec60184b3a4e80002d1bf913984 100644 --- a/ecmascript/pgo_profiler/tests/pgo_profiler_test.cpp +++ b/ecmascript/pgo_profiler/tests/pgo_profiler_test.cpp @@ -14,8 +14,18 @@ */ #include +#include +#include #include #include + +#include "ecmascript/elements.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/pgo_profiler/ap_file/pgo_file_info.h" +#include "ecmascript/pgo_profiler/pgo_context.h" +#include "ecmascript/pgo_profiler/types/pgo_profile_type.h" +#include "ecmascript/pgo_profiler/types/pgo_profiler_type.h" +#include "ecmascript/pgo_profiler/pgo_utils.h" #include "gtest/gtest.h" #include "assembler/assembly-emitter.h" @@ -25,21 +35,24 @@ #include "ecmascript/jspandafile/js_pandafile.h" #include "ecmascript/jspandafile/js_pandafile_manager.h" #include "ecmascript/jspandafile/method_literal.h" +#include "ecmascript/jspandafile/program_object.h" #include "ecmascript/napi/include/jsnapi.h" #include "ecmascript/pgo_profiler/pgo_profiler_decoder.h" #include "ecmascript/pgo_profiler/pgo_profiler_info.h" #include "ecmascript/pgo_profiler/pgo_profiler_manager.h" +#include "ecmascript/pgo_profiler/tests/pgo_context_mock.h" #include "ecmascript/tests/test_helper.h" -#include "ecmascript/jspandafile/program_object.h" using namespace panda; using namespace panda::ecmascript; +using namespace panda::ecmascript::pgo; using namespace panda::panda_file; using namespace panda::pandasm; namespace panda::test { class PGOProfilerTest : public testing::Test { public: + using ApGenMode = PGOProfilerEncoder::ApGenMode; static void SetUpTestCase() { GTEST_LOG_(INFO) << "SetUpTestCase"; @@ -83,6 +96,26 @@ protected: return pf; } + std::shared_ptr ExecuteAndLoadJSPandaFile(std::string profDir, std::string recordName) + { + RuntimeOption option; + option.SetLogLevel(LOG_LEVEL::INFO); + option.SetEnableProfile(true); + option.SetProfileDir(profDir); + vm_ = JSNApi::CreateJSVM(option); + JSNApi::EnableUserUncaughtErrorHandler(vm_); + + std::string targetAbcPath = TARGET_ABC_PATH + recordName + ".abc"; + auto result = JSNApi::Execute(vm_, targetAbcPath, recordName, false); + EXPECT_TRUE(result); + + std::shared_ptr jsPandaFile = + JSPandaFileManager::GetInstance()->FindJSPandaFile(CString(targetAbcPath)); + + JSNApi::DestroyJSVM(vm_); + return jsPandaFile; + } + EcmaVM *vm_ = nullptr; }; @@ -104,14 +137,15 @@ HWTEST_F_L0(PGOProfilerTest, Sample) JSHandle constPool = vm_->GetFactory()->NewConstantPool(4); constPool->SetJSPandaFile(pf.get()); uint32_t checksum = 304293; - PGOProfilerManager::GetInstance()->SamplePandaFileInfo(checksum); + PGOProfilerManager::GetInstance()->SamplePandaFileInfo(checksum, "ark-profiler.abc"); ASSERT_TRUE(vm_ != nullptr) << "Cannot create Runtime"; JSHandle method = vm_->GetFactory()->NewMethod(methodLiterals[0]); method->SetConstantPool(vm_->GetJSThread(), constPool.GetTaggedValue()); JSHandle func = vm_->GetFactory()->NewJSFunction(vm_->GetGlobalEnv(), method); JSHandle recordName(vm_->GetFactory()->NewFromStdString("test")); - func->SetModule(vm_->GetJSThread(), recordName); + method->SetModule(vm_->GetJSThread(), recordName); + vm_->GetPGOProfiler()->SetSaveTimestamp(std::chrono::system_clock::now()); vm_->GetPGOProfiler()->ProfileCall(func.GetTaggedType()); JSNApi::DestroyJSVM(vm_); // Loader @@ -119,10 +153,10 @@ HWTEST_F_L0(PGOProfilerTest, Sample) CString expectRecordName = "test"; #if defined(SUPPORT_ENABLE_ASM_INTERP) ASSERT_TRUE(loader.LoadAndVerify(checksum)); - ASSERT_TRUE(!loader.Match(pf.get(), expectRecordName, methodLiterals[0])); + ASSERT_TRUE(!loader.Match(expectRecordName, methodLiterals[0]->GetMethodId())); #else ASSERT_TRUE(!loader.LoadAndVerify(checksum)); - ASSERT_TRUE(loader.Match(pf.get(), expectRecordName, methodLiterals[0])); + ASSERT_TRUE(loader.Match(expectRecordName, methodLiterals[0]->GetMethodId())); #endif unlink("ark-profiler/modules.ap"); rmdir("ark-profiler/"); @@ -151,7 +185,7 @@ HWTEST_F_L0(PGOProfilerTest, Sample1) JSHandle constPool = vm_->GetFactory()->NewConstantPool(4); constPool->SetJSPandaFile(pf.get()); uint32_t checksum = 304293; - PGOProfilerManager::GetInstance()->SamplePandaFileInfo(checksum); + PGOProfilerManager::GetInstance()->SamplePandaFileInfo(checksum, "ark-profiler1.abc"); ASSERT_TRUE(vm_ != nullptr) << "Cannot create Runtime"; JSHandle method = vm_->GetFactory()->NewMethod(methodLiterals[0]); @@ -165,9 +199,9 @@ HWTEST_F_L0(PGOProfilerTest, Sample1) JSHandle func1 = vm_->GetFactory()->NewJSFunction(vm_->GetGlobalEnv(), method1); JSHandle func2 = vm_->GetFactory()->NewJSFunction(vm_->GetGlobalEnv(), method2); JSHandle recordName(vm_->GetFactory()->NewFromStdString("test")); - func->SetModule(vm_->GetJSThread(), recordName); - func1->SetModule(vm_->GetJSThread(), recordName); - func2->SetModule(vm_->GetJSThread(), recordName); + method->SetModule(vm_->GetJSThread(), recordName); + method1->SetModule(vm_->GetJSThread(), recordName); + method2->SetModule(vm_->GetJSThread(), recordName); for (int i = 0; i < 5; i++) { vm_->GetPGOProfiler()->ProfileCall(func.GetTaggedType()); } @@ -182,12 +216,17 @@ HWTEST_F_L0(PGOProfilerTest, Sample1) CString expectRecordName = "test"; #if defined(SUPPORT_ENABLE_ASM_INTERP) ASSERT_TRUE(loader.LoadAndVerify(checksum)); - ASSERT_TRUE(loader.Match(pf.get(), expectRecordName, methodLiterals[0])); - ASSERT_TRUE(loader.Match(pf.get(), expectRecordName, methodLiterals[2])); - ASSERT_TRUE(!loader.Match(pf.get(), expectRecordName, methodLiterals[1])); + for (uint32_t idx = 0; idx < 3; idx++) { + loader.MatchAndMarkMethod(expectRecordName, + methodLiterals[idx]->GetMethodName(pf.get(), methodLiterals[idx]->GetMethodId()), + methodLiterals[idx]->GetMethodId()); + } + ASSERT_TRUE(loader.Match(expectRecordName, methodLiterals[0]->GetMethodId())); + ASSERT_TRUE(loader.Match(expectRecordName, methodLiterals[2]->GetMethodId())); + ASSERT_TRUE(!loader.Match(expectRecordName, methodLiterals[1]->GetMethodId())); #else ASSERT_TRUE(!loader.LoadAndVerify(checksum)); - ASSERT_TRUE(loader.Match(pf.get(), expectRecordName, methodLiterals[1])); + ASSERT_TRUE(loader.Match(expectRecordName, methodLiterals[1]->GetMethodId())); #endif unlink("ark-profiler1/modules.ap"); rmdir("ark-profiler1/"); @@ -213,7 +252,7 @@ HWTEST_F_L0(PGOProfilerTest, Sample2) constPool->SetJSPandaFile(pf.get()); ASSERT_TRUE(vm_ != nullptr) << "Cannot create Runtime"; uint32_t checksum = 304293; - PGOProfilerManager::GetInstance()->SamplePandaFileInfo(checksum); + PGOProfilerManager::GetInstance()->SamplePandaFileInfo(checksum, "ark-profiler2.abc"); JSHandle method = vm_->GetFactory()->NewMethod(methodLiterals[0]); JSHandle method1 = vm_->GetFactory()->NewMethod(methodLiterals[1]); @@ -221,11 +260,11 @@ HWTEST_F_L0(PGOProfilerTest, Sample2) method->SetConstantPool(vm_->GetJSThread(), constPool.GetTaggedValue()); method1->SetConstantPool(vm_->GetJSThread(), constPool.GetTaggedValue()); JSHandle func = vm_->GetFactory()->NewJSFunction(vm_->GetGlobalEnv(), method); - JSHandle recordName(vm_->GetFactory()->NewFromStdString("test")); - func->SetModule(vm_->GetJSThread(), recordName); JSHandle func1 = vm_->GetFactory()->NewJSFunction(vm_->GetGlobalEnv(), method1); + JSHandle recordName(vm_->GetFactory()->NewFromStdString("test")); + method->SetModule(vm_->GetJSThread(), recordName); JSHandle recordName1(vm_->GetFactory()->NewFromStdString("test1")); - func1->SetModule(vm_->GetJSThread(), recordName1); + method1->SetModule(vm_->GetJSThread(), recordName1); vm_->GetPGOProfiler()->ProfileCall(func.GetTaggedType()); for (int i = 0; i < 5; i++) { vm_->GetPGOProfiler()->ProfileCall(func1.GetTaggedType()); @@ -238,12 +277,20 @@ HWTEST_F_L0(PGOProfilerTest, Sample2) CString expectRecordName1 = "test1"; #if defined(SUPPORT_ENABLE_ASM_INTERP) ASSERT_TRUE(loader.LoadAndVerify(checksum)); - ASSERT_TRUE(!loader.Match(pf.get(), expectRecordName, methodLiterals[0])); + for (uint32_t idx = 0; idx < 2; idx++) { + loader.MatchAndMarkMethod(expectRecordName, + methodLiterals[idx]->GetMethodName(pf.get(), methodLiterals[idx]->GetMethodId()), + methodLiterals[idx]->GetMethodId()); + loader.MatchAndMarkMethod(expectRecordName1, + methodLiterals[idx]->GetMethodName(pf.get(), methodLiterals[idx]->GetMethodId()), + methodLiterals[idx]->GetMethodId()); + } + ASSERT_TRUE(!loader.Match(expectRecordName, methodLiterals[0]->GetMethodId())); #else ASSERT_TRUE(!loader.LoadAndVerify(checksum)); - ASSERT_TRUE(loader.Match(pf.get(), expectRecordName, methodLiterals[0])); + ASSERT_TRUE(loader.Match(expectRecordName, methodLiterals[0]->GetMethodId())); #endif - ASSERT_TRUE(loader.Match(pf.get(), expectRecordName1, methodLiterals[1])); + ASSERT_TRUE(loader.Match(expectRecordName1, methodLiterals[1]->GetMethodId())); unlink("ark-profiler2/modules.ap"); rmdir("ark-profiler2/"); } @@ -265,7 +312,7 @@ HWTEST_F_L0(PGOProfilerTest, DisEnableSample) JSHandle constPool = vm_->GetFactory()->NewConstantPool(4); constPool->SetJSPandaFile(pf.get()); uint32_t checksum = 304293; - PGOProfilerManager::GetInstance()->SamplePandaFileInfo(checksum); + PGOProfilerManager::GetInstance()->SamplePandaFileInfo(checksum, "ark-profiler3.abc"); ASSERT_TRUE(vm_ != nullptr) << "Cannot create Runtime"; JSHandle method = vm_->GetFactory()->NewMethod(methodLiterals[0]); @@ -273,7 +320,7 @@ HWTEST_F_L0(PGOProfilerTest, DisEnableSample) method->SetConstantPool(vm_->GetJSThread(), constPool.GetTaggedValue()); JSHandle func = vm_->GetFactory()->NewJSFunction(vm_->GetGlobalEnv(), method); JSHandle recordName(vm_->GetFactory()->NewFromStdString("test")); - func->SetModule(vm_->GetJSThread(), recordName); + method->SetModule(vm_->GetJSThread(), recordName); vm_->GetPGOProfiler()->ProfileCall(func.GetTaggedType()); JSNApi::DestroyJSVM(vm_); @@ -282,7 +329,7 @@ HWTEST_F_L0(PGOProfilerTest, DisEnableSample) // path is empty() ASSERT_TRUE(!loader.LoadAndVerify(checksum)); CString expectRecordName = "test"; - ASSERT_TRUE(loader.Match(pf.get(), expectRecordName, methodLiterals[0])); + ASSERT_TRUE(loader.Match(expectRecordName, methodLiterals[0]->GetMethodId())); rmdir("ark-profiler3/"); } @@ -320,7 +367,7 @@ HWTEST_F_L0(PGOProfilerTest, PGOProfilerManagerSample) option.SetProfileDir(currentPath); vm_ = JSNApi::CreateJSVM(option); uint32_t checksum = 304293; - PGOProfilerManager::GetInstance()->SamplePandaFileInfo(checksum); + PGOProfilerManager::GetInstance()->SamplePandaFileInfo(checksum, ""); ASSERT_TRUE(vm_ != nullptr) << "Cannot create Runtime"; JSHandle array = vm_->GetFactory()->NewJSArray(); @@ -330,7 +377,7 @@ HWTEST_F_L0(PGOProfilerTest, PGOProfilerManagerSample) MethodLiteral *methodLiteral = new MethodLiteral(EntityId(61)); JSHandle method = vm_->GetFactory()->NewMethod(methodLiteral); JSHandle func = vm_->GetFactory()->NewJSFunction(vm_->GetGlobalEnv(), method); - func->SetModule(vm_->GetJSThread(), JSTaggedValue::Hole()); + method->SetModule(vm_->GetJSThread(), JSTaggedValue::Hole()); vm_->GetPGOProfiler()->ProfileCall(func.GetTaggedType()); JSNApi::DestroyJSVM(vm_); @@ -362,21 +409,21 @@ HWTEST_F_L0(PGOProfilerTest, PGOProfilerDoubleVM) JSHandle constPool = vm_->GetFactory()->NewConstantPool(4); constPool->SetJSPandaFile(pf.get()); uint32_t checksum = 304293; - PGOProfilerManager::GetInstance()->SamplePandaFileInfo(checksum); + PGOProfilerManager::GetInstance()->SamplePandaFileInfo(checksum, "ark-profiler5.abc"); ASSERT_TRUE(vm_ != nullptr) << "Cannot create Runtime"; // worker vm read profile enable from PGOProfilerManager singleton option.SetEnableProfile(false); auto vm2 = JSNApi::CreateJSVM(option); JSHandle constPool2 = vm2->GetFactory()->NewConstantPool(4); constPool2->SetJSPandaFile(pf.get()); - PGOProfilerManager::GetInstance()->SamplePandaFileInfo(checksum); + PGOProfilerManager::GetInstance()->SamplePandaFileInfo(checksum, "ark-profiler5.abc"); ASSERT_TRUE(vm2 != nullptr) << "Cannot create Runtime"; JSHandle method = vm2->GetFactory()->NewMethod(methodLiterals[0]); method->SetConstantPool(vm2->GetJSThread(), constPool2.GetTaggedValue()); JSHandle func = vm2->GetFactory()->NewJSFunction(vm_->GetGlobalEnv(), method); JSHandle recordName(vm_->GetFactory()->NewFromStdString("test")); - func->SetModule(vm2->GetJSThread(), recordName); + method->SetModule(vm2->GetJSThread(), recordName); vm2->GetPGOProfiler()->ProfileCall(func.GetTaggedType()); JSHandle method1 = vm_->GetFactory()->NewMethod(methodLiterals[0]); @@ -386,8 +433,8 @@ HWTEST_F_L0(PGOProfilerTest, PGOProfilerDoubleVM) JSHandle func1 = vm_->GetFactory()->NewJSFunction(vm_->GetGlobalEnv(), method1); JSHandle func2 = vm_->GetFactory()->NewJSFunction(vm_->GetGlobalEnv(), method2); JSHandle recordName1(vm_->GetFactory()->NewFromStdString("test")); - func1->SetModule(vm_->GetJSThread(), recordName); - func2->SetModule(vm_->GetJSThread(), recordName); + method1->SetModule(vm_->GetJSThread(), recordName); + method2->SetModule(vm_->GetJSThread(), recordName); vm_->GetPGOProfiler()->ProfileCall(func1.GetTaggedType()); vm_->GetPGOProfiler()->ProfileCall(func2.GetTaggedType()); @@ -398,15 +445,15 @@ HWTEST_F_L0(PGOProfilerTest, PGOProfilerDoubleVM) mkdir("ark-profiler5/profiler", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); ASSERT_TRUE(!loader.LoadAndVerify(checksum)); CString expectRecordName = "test"; - ASSERT_TRUE(loader.Match(pf.get(), expectRecordName, methodLiterals[1])); + ASSERT_TRUE(loader.Match(expectRecordName, methodLiterals[1]->GetMethodId())); PGOProfilerDecoder loader1("ark-profiler5/modules.ap", 2); #if defined(SUPPORT_ENABLE_ASM_INTERP) ASSERT_TRUE(loader1.LoadAndVerify(checksum)); - ASSERT_TRUE(!loader1.Match(pf.get(), expectRecordName, methodLiterals[1])); + ASSERT_TRUE(!loader1.Match(expectRecordName, methodLiterals[1]->GetMethodId())); #else ASSERT_TRUE(!loader1.LoadAndVerify(checksum)); - ASSERT_TRUE(loader1.Match(pf.get(), expectRecordName, methodLiterals[1])); + ASSERT_TRUE(loader1.Match(expectRecordName, methodLiterals[1]->GetMethodId())); #endif unlink("ark-profiler5/modules.ap"); @@ -431,14 +478,14 @@ HWTEST_F_L0(PGOProfilerTest, PGOProfilerDecoderNoHotMethod) JSHandle constPool = vm_->GetFactory()->NewConstantPool(4); constPool->SetJSPandaFile(pf.get()); uint32_t checksum = 304293; - PGOProfilerManager::GetInstance()->SamplePandaFileInfo(checksum); + PGOProfilerManager::GetInstance()->SamplePandaFileInfo(checksum, "ark-profiler8.abc"); JSHandle method = vm_->GetFactory()->NewMethod(methodLiterals[0]); method->SetConstantPool(vm_->GetJSThread(), constPool.GetTaggedValue()); JSHandle func = vm_->GetFactory()->NewJSFunction(vm_->GetGlobalEnv(), method); JSHandle recordName(vm_->GetFactory()->NewFromStdString("test")); - func->SetModule(vm_->GetJSThread(), recordName); + method->SetModule(vm_->GetJSThread(), recordName); vm_->GetPGOProfiler()->ProfileCall(func.GetTaggedType()); JSNApi::DestroyJSVM(vm_); @@ -446,10 +493,10 @@ HWTEST_F_L0(PGOProfilerTest, PGOProfilerDecoderNoHotMethod) CString expectRecordName = "test"; #if defined(SUPPORT_ENABLE_ASM_INTERP) ASSERT_TRUE(loader.LoadAndVerify(checksum)); - ASSERT_TRUE(!loader.Match(pf.get(), expectRecordName, methodLiterals[0])); + ASSERT_TRUE(!loader.Match(expectRecordName, methodLiterals[0]->GetMethodId())); #else ASSERT_TRUE(!loader.LoadAndVerify(checksum)); - ASSERT_TRUE(loader.Match(pf.get(), expectRecordName, methodLiterals[0])); + ASSERT_TRUE(loader.Match(expectRecordName, methodLiterals[0]->GetMethodId())); #endif unlink("ark-profiler8/modules.ap"); @@ -475,14 +522,15 @@ HWTEST_F_L0(PGOProfilerTest, PGOProfilerPostTask) JSHandle constPool = vm_->GetFactory()->NewConstantPool(4); constPool->SetJSPandaFile(pf.get()); uint32_t checksum = 304293; - PGOProfilerManager::GetInstance()->SamplePandaFileInfo(checksum); + PGOProfilerManager::GetInstance()->SetApGenMode(ApGenMode::OVERWRITE); + PGOProfilerManager::GetInstance()->SamplePandaFileInfo(checksum, "ark-profiler9.abc"); JSHandle recordName(vm_->GetFactory()->NewFromStdString("test")); for (int i = 61; i < 91; i++) { JSHandle method = vm_->GetFactory()->NewMethod(methodLiterals[i]); method->SetConstantPool(vm_->GetJSThread(), constPool.GetTaggedValue()); JSHandle func = vm_->GetFactory()->NewJSFunction(vm_->GetGlobalEnv(), method); - func->SetModule(vm_->GetJSThread(), recordName); + method->SetModule(vm_->GetJSThread(), recordName); vm_->GetPGOProfiler()->ProfileCall(func.GetTaggedType()); if (i % 3 == 0) { vm_->GetPGOProfiler()->ProfileCall(func.GetTaggedType()); @@ -498,14 +546,18 @@ HWTEST_F_L0(PGOProfilerTest, PGOProfilerPostTask) ASSERT_TRUE(!loader.LoadAndVerify(checksum)); #endif CString expectRecordName = "test"; + for (int i = 0; i < 100; i++) { + EntityId methodId = methodLiterals[i]->GetMethodId(); + loader.MatchAndMarkMethod(expectRecordName, methodLiterals[i]->GetMethodName(pf.get(), methodId), methodId); + } for (int i = 61; i < 91; i++) { if (i % 3 == 0) { - ASSERT_TRUE(loader.Match(pf.get(), expectRecordName, methodLiterals[i])); + ASSERT_TRUE(loader.Match(expectRecordName, methodLiterals[i]->GetMethodId())); } else { #if defined(SUPPORT_ENABLE_ASM_INTERP) - ASSERT_TRUE(!loader.Match(pf.get(), expectRecordName, methodLiterals[i])); + ASSERT_TRUE(!loader.Match(expectRecordName, methodLiterals[i]->GetMethodId())); #else - ASSERT_TRUE(loader.Match(pf.get(), expectRecordName, methodLiterals[i])); + ASSERT_TRUE(loader.Match(expectRecordName, methodLiterals[i]->GetMethodId())); #endif } } @@ -518,25 +570,41 @@ HWTEST_F_L0(PGOProfilerTest, BinaryToText) { mkdir("ark-profiler7/", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); - std::ofstream file("ark-profiler7/modules.ap"); + std::fstream file("ark-profiler7/modules.ap", + std::fstream::binary | std::fstream::out | std::fstream::in | std::fstream::trunc); PGOProfilerHeader *header = nullptr; PGOProfilerHeader::Build(&header, PGOProfilerHeader::LastSize()); + std::unique_ptr abcFilePool = std::make_unique(); std::unique_ptr pandaFileInfos = std::make_unique(); std::unique_ptr recordInfos = std::make_unique(2); + + RuntimeOption option; + vm_ = JSNApi::CreateJSVM(option); + ASSERT_TRUE(vm_ != nullptr) << "Cannot create Runtime"; pandaFileInfos->Sample(0x34556738); - ASSERT_TRUE(recordInfos->AddMethod("test", EntityId(23), 0, "test", SampleMode::CALL_MODE)); - ASSERT_FALSE(recordInfos->AddMethod("test", EntityId(23), 0, "test", SampleMode::CALL_MODE)); - ASSERT_FALSE(recordInfos->AddMethod("test", EntityId(23), 0, "test", SampleMode::CALL_MODE)); + std::shared_ptr methodLiteral = std::make_shared(EntityId(61)); + auto *jsMethod = + Method::Cast(vm_->GetFactory()->NewMethod(methodLiteral.get(), MemSpaceType::NON_MOVABLE).GetTaggedValue()); + + ApEntityId recordId(0); + recordInfos->GetRecordPool()->TryAdd("test", recordId); + ProfileType recordType(0, recordId, ProfileType::Kind::LocalRecordId); + ASSERT_TRUE(recordInfos->AddMethod(recordType, jsMethod, SampleMode::CALL_MODE, 1)); + ASSERT_FALSE(recordInfos->AddMethod(recordType, jsMethod, SampleMode::CALL_MODE, 1)); + ASSERT_FALSE(recordInfos->AddMethod(recordType, jsMethod, SampleMode::CALL_MODE, 1)); pandaFileInfos->ProcessToBinary(file, header->GetPandaInfoSection()); recordInfos->ProcessToBinary(nullptr, file, header); + PGOFileSectionInterface::ProcessSectionToBinary(file, header, *abcFilePool->GetPool()); + header->SetFileSize(static_cast(file.tellp())); header->ProcessToBinary(file); + PGOProfilerEncoder::AddChecksum(file); file.close(); ASSERT_TRUE(PGOProfilerManager::GetInstance()->BinaryToText( "ark-profiler7/modules.ap", "ark-profiler7/modules.text", 2)); - + JSNApi::DestroyJSVM(vm_); unlink("ark-profiler7/modules.ap"); unlink("ark-profiler7/modules.text"); rmdir("ark-profiler7"); @@ -555,7 +623,8 @@ HWTEST_F_L0(PGOProfilerTest, TextToBinary) file.write(result.c_str(), result.size()); file.close(); - ASSERT_TRUE(PGOProfilerManager::GetInstance()->TextToBinary("ark-profiler10/modules.text", "ark-profiler10/", 2)); + ASSERT_TRUE(PGOProfilerManager::GetInstance()->TextToBinary("ark-profiler10/modules.text", "ark-profiler10/", 2, + ApGenMode::OVERWRITE)); PGOProfilerDecoder loader("ark-profiler10/modules.ap", 2); ASSERT_TRUE(loader.LoadAndVerify(413775942)); @@ -565,45 +634,6 @@ HWTEST_F_L0(PGOProfilerTest, TextToBinary) rmdir("ark-profiler10"); } -HWTEST_F_L0(PGOProfilerTest, TextRecover) -{ - mkdir("ark-profiler11/", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); - - std::ofstream file("ark-profiler11/modules.text"); - std::string result = "Profiler Version: 0.0.0.2\n"; - file.write(result.c_str(), result.size()); - result = "\nPanda file sumcheck list: [ 413775942 ]\n"; - file.write(result.c_str(), result.size()); - result = "\n_GLOBAL::funct_main_0: [ 1232/3/CALL_MODE/hello/ ]\n"; - file.write(result.c_str(), result.size()); - result = "\nrecordName: [ 234/100/HOTNESS_MODE/h#ello1/ ]\n"; - file.write(result.c_str(), result.size()); - file.close(); - - ASSERT_TRUE(PGOProfilerManager::GetInstance()->TextToBinary("ark-profiler11/modules.text", "ark-profiler11/", 2)); - - ASSERT_TRUE(PGOProfilerManager::GetInstance()->BinaryToText( - "ark-profiler11/modules.ap", "ark-profiler11/modules_recover.text", 2)); - - std::ifstream fileOrigin("ark-profiler11/modules.text"); - std::ifstream fileRecover("ark-profiler11/modules_recover.text"); - - std::string lineOrigin; - std::string lineRecover; - // check content from origin and recovered profile line by line. - while (std::getline(fileOrigin, lineOrigin)) { - std::getline(fileRecover, lineRecover); - ASSERT_EQ(lineOrigin, lineRecover); - } - - fileOrigin.close(); - fileRecover.close(); - unlink("ark-profiler11/modules.ap"); - unlink("ark-profiler11/modules.text"); - unlink("ark-profiler11/modules_recover.text"); - rmdir("ark-profiler11"); -} - HWTEST_F_L0(PGOProfilerTest, FailResetProfilerInWorker) { const char *source = R"( @@ -622,7 +652,7 @@ HWTEST_F_L0(PGOProfilerTest, FailResetProfilerInWorker) // PgoProfiler is disabled as default. vm_ = JSNApi::CreateJSVM(option); uint32_t checksum = 304293; - PGOProfilerManager::GetInstance()->SamplePandaFileInfo(checksum); + PGOProfilerManager::GetInstance()->SamplePandaFileInfo(checksum, "ark-profiler12.abc"); ASSERT_TRUE(vm_ != nullptr) << "Cannot create Runtime"; JSHandle method = vm_->GetFactory()->NewMethod(methodLiterals[0]); @@ -632,7 +662,7 @@ HWTEST_F_L0(PGOProfilerTest, FailResetProfilerInWorker) method->SetConstantPool(vm_->GetJSThread(), constPool.GetTaggedValue()); JSHandle func = vm_->GetFactory()->NewJSFunction(vm_->GetGlobalEnv(), method); JSHandle recordName(vm_->GetFactory()->NewFromStdString("test")); - func->SetModule(vm_->GetJSThread(), recordName); + method->SetModule(vm_->GetJSThread(), recordName); vm_->GetPGOProfiler()->ProfileCall(func.GetTaggedType()); JSNApi::DestroyJSVM(vm_); @@ -641,7 +671,492 @@ HWTEST_F_L0(PGOProfilerTest, FailResetProfilerInWorker) // path is empty() ASSERT_TRUE(!loader.LoadAndVerify(checksum)); CString expectRecordName = "test"; - ASSERT_TRUE(loader.Match(pf.get(), expectRecordName, methodLiterals[0])); + ASSERT_TRUE(loader.Match(expectRecordName, methodLiterals[0]->GetMethodId())); rmdir("ark-profiler12/"); } + +#if defined(SUPPORT_ENABLE_ASM_INTERP) +HWTEST_F_L0(PGOProfilerTest, ProfileCallTest) +{ + mkdir("ark-profiler13/", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + const char *targetRecordName = "call_test"; + std::shared_ptr jsPandaFile = ExecuteAndLoadJSPandaFile("ark-profiler13/", targetRecordName); + ASSERT_NE(jsPandaFile, nullptr); + uint32_t checksum = jsPandaFile->GetChecksum(); + + // Loader + PGOProfilerDecoder decoder("ark-profiler13/modules.ap", 1); + PGOProfilerDecoder decoder1("ark-profiler13/modules.ap", 10); + PGOProfilerDecoder decoder2("ark-profiler13/modules.ap", 11000); + ASSERT_TRUE(decoder.LoadAndVerify(checksum)); + ASSERT_TRUE(decoder1.LoadAndVerify(checksum)); + ASSERT_TRUE(decoder2.LoadAndVerify(checksum)); + auto methodLiterals = jsPandaFile->GetMethodLiteralMap(); + for (auto iter : methodLiterals) { + auto methodLiteral = iter.second; + auto methodId = methodLiteral->GetMethodId(); + auto methodName = methodLiteral->GetMethodName(jsPandaFile.get(), methodId); + decoder.MatchAndMarkMethod(targetRecordName, methodName, methodId); + decoder1.MatchAndMarkMethod(targetRecordName, methodName, methodId); + decoder2.MatchAndMarkMethod(targetRecordName, methodName, methodId); + ASSERT_TRUE(decoder.Match(targetRecordName, methodId)); + if (std::string(methodName) == "Test") { + ASSERT_TRUE(decoder1.Match(targetRecordName, methodId)); + ASSERT_TRUE(decoder2.Match(targetRecordName, methodId)); + } else { + ASSERT_TRUE(decoder1.Match(targetRecordName, methodId)); + ASSERT_TRUE(decoder2.Match(targetRecordName, methodId)); + } + } + unlink("ark-profiler13/modules.ap"); + rmdir("ark-profiler13/"); +} + +HWTEST_F_L0(PGOProfilerTest, UseClassTypeTest) +{ + mkdir("ark-profiler14/", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + const char *targetRecordName = "class_test"; + std::shared_ptr jsPandaFile = ExecuteAndLoadJSPandaFile("ark-profiler14/", targetRecordName); + ASSERT_NE(jsPandaFile, nullptr); + uint32_t checksum = jsPandaFile->GetChecksum(); + + // Loader + PGOProfilerDecoder decoder("ark-profiler14/modules.ap", 1); + ASSERT_TRUE(decoder.LoadAndVerify(checksum)); + auto methodLiterals = jsPandaFile->GetMethodLiteralMap(); + for (auto iter : methodLiterals) { + auto methodLiteral = iter.second; + auto methodId = methodLiteral->GetMethodId(); + auto methodName = methodLiteral->GetMethodName(jsPandaFile.get(), methodId); + decoder.MatchAndMarkMethod(targetRecordName, methodName, methodId); + ASSERT_TRUE(decoder.Match(targetRecordName, methodId)); + auto callback = [methodName, methodId](uint32_t offset, PGOType *type) { + ASSERT_NE(offset, 0); + if (type->IsScalarOpType()) { + } else if (type->IsRwOpType()) { + auto pgoRWOpType = *reinterpret_cast(type); + if (std::string(methodName) == "Foot" || std::string(methodName) == "Arm") { + ASSERT_TRUE(pgoRWOpType.GetCount() == 1); + ASSERT_EQ(pgoRWOpType.GetObjectInfo(0).GetProfileType(), + ProfileType(ApEntityId(PGORecordPool::RESERVED_COUNT), methodId.GetOffset())); + } else if (std::string(methodName) == "foo" || std::string(methodName) == "Body") { + ASSERT_TRUE(pgoRWOpType.GetCount() == 3); + } + } else { + ASSERT_TRUE(false); + } + }; + decoder.GetTypeInfo(jsPandaFile.get(), targetRecordName, methodLiteral, callback); + } + unlink("ark-profiler14/modules.ap"); + rmdir("ark-profiler14/"); +} + +HWTEST_F_L0(PGOProfilerTest, DefineClassTypeTest) +{ + mkdir("ark-profiler15/", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + const char *targetRecordName = "class_test"; + std::shared_ptr jsPandaFile = ExecuteAndLoadJSPandaFile("ark-profiler15/", targetRecordName); + ASSERT_NE(jsPandaFile, nullptr); + uint32_t checksum = jsPandaFile->GetChecksum(); + + // Loader + PGOProfilerDecoder decoder("ark-profiler15/modules.ap", 1); + ASSERT_TRUE(decoder.LoadAndVerify(checksum)); + auto methodLiterals = jsPandaFile->GetMethodLiteralMap(); + for (auto iter : methodLiterals) { + auto methodLiteral = iter.second; + auto methodId = methodLiteral->GetMethodId(); + auto methodName = methodLiteral->GetMethodName(jsPandaFile.get(), methodId); + decoder.MatchAndMarkMethod(targetRecordName, methodName, methodId); + ASSERT_TRUE(decoder.Match(targetRecordName, methodId)); + auto callback = [methodName, &decoder, jsPandaFile](uint32_t offset, PGOType *type) { + ASSERT_NE(offset, 0); + if (type->IsScalarOpType()) { + auto sampleType = *reinterpret_cast(type); + if (sampleType.IsProfileType()) { + ASSERT_EQ(std::string(methodName), "func_main_0"); + PGOHClassLayoutDesc *desc; + if (!decoder.GetHClassLayoutDesc(sampleType, &desc)) { + return; + } + ASSERT_EQ(desc->GetCtorLayoutDesc().size(), 3); + ASSERT_EQ(desc->GetPtLayoutDesc().size(), 1); + auto classId = EntityId(sampleType.GetProfileType().GetId()); + auto className = MethodLiteral::GetMethodName(jsPandaFile.get(), classId); + if (std::string(className) == "Arm") { + auto superClassId = EntityId(desc->GetSuperProfileType().GetId()); + auto superClassName = MethodLiteral::GetMethodName(jsPandaFile.get(), superClassId); + ASSERT_EQ(std::string(superClassName), "Body"); + ASSERT_EQ(desc->GetLayoutDesc().size(), 0); + } else if (std::string(className) == "Foot") { + auto superClassId = EntityId(desc->GetSuperProfileType().GetId()); + auto superClassName = MethodLiteral::GetMethodName(jsPandaFile.get(), superClassId); + ASSERT_EQ(std::string(superClassName), "Body"); + ASSERT_EQ(desc->GetLayoutDesc().size(), 0); + } else { + ASSERT_EQ(desc->GetSuperProfileType().GetRaw(), 0); + ASSERT_EQ(desc->GetLayoutDesc().size(), 0); + } + } + } + }; + decoder.GetTypeInfo(jsPandaFile.get(), targetRecordName, methodLiteral, callback); + } + unlink("ark-profiler15/modules.ap"); + rmdir("ark-profiler15/"); +} + +HWTEST_F_L0(PGOProfilerTest, OpTypeTest) +{ + mkdir("ark-profiler16/", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + const char *targetRecordName = "op_type_test"; + std::shared_ptr jsPandaFile = ExecuteAndLoadJSPandaFile("ark-profiler16/", targetRecordName); + ASSERT_NE(jsPandaFile, nullptr); + uint32_t checksum = jsPandaFile->GetChecksum(); + + // Loader + PGOProfilerDecoder decoder("ark-profiler16/modules.ap", 1); + ASSERT_TRUE(decoder.LoadAndVerify(checksum)); + std::string types[17] = + { "1", "5", "4", "4", "4", "4", "4", "4", "5", "4", "4", "1", "4", "5", "1" }; + int index = 0; + auto methodLiterals = jsPandaFile->GetMethodLiteralMap(); + for (auto iter : methodLiterals) { + auto methodLiteral = iter.second; + auto methodId = methodLiteral->GetMethodId(); + auto methodName = methodLiteral->GetMethodName(jsPandaFile.get(), methodId); + decoder.MatchAndMarkMethod(targetRecordName, methodName, methodId); + ASSERT_TRUE(decoder.Match(targetRecordName, methodId)); + auto callback = [methodName, types, &index](uint32_t offset, PGOType *type) { + ASSERT_NE(offset, 0); + if (type->IsScalarOpType()) { + auto sampleType = *reinterpret_cast(type); + if (sampleType.IsProfileType()) { + return; + } + if (std::string(methodName) == "advance") { + if (sampleType.GetWeight() > 0) { + auto trueWeight = sampleType.GetWeight() >> 10; + auto falseWeight = sampleType.GetWeight() & 0x7FF; + auto primitiveType = sampleType.GetPrimitiveType(); + ASSERT_GT(trueWeight, falseWeight); + ASSERT_EQ(static_cast(primitiveType), PGOSampleType::IntType()); + } else { + ASSERT_EQ(sampleType.GetTypeString(), types[index++]); + } + } + } + }; + decoder.GetTypeInfo(jsPandaFile.get(), targetRecordName, methodLiteral, callback); + } + unlink("ark-profiler16/modules.ap"); + rmdir("ark-profiler16/"); +} + +HWTEST_F_L0(PGOProfilerTest, ArrayProfileTest) +{ + mkdir("ark-profiler18/", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + const char *targetRecordName = "array_test"; + std::shared_ptr jsPandaFile = ExecuteAndLoadJSPandaFile("ark-profiler18/", targetRecordName); + ASSERT_NE(jsPandaFile, nullptr); + uint32_t checksum = jsPandaFile->GetChecksum(); + + // Loader + PGOProfilerDecoder decoder("ark-profiler18/modules.ap", 1); + ASSERT_TRUE(decoder.LoadAndVerify(checksum)); + auto methodLiterals = jsPandaFile->GetMethodLiteralMap(); + for (auto iter : methodLiterals) { + auto methodLiteral = iter.second; + auto methodId = methodLiteral->GetMethodId(); + auto methodName = methodLiteral->GetMethodName(jsPandaFile.get(), methodId); + decoder.MatchAndMarkMethod(targetRecordName, methodName, methodId); + ASSERT_TRUE(decoder.Match(targetRecordName, methodId)); + auto callback = [methodName, &decoder, jsPandaFile](uint32_t offset, PGOType *type) { + if (type->IsScalarOpType()) { + auto sampleType = *reinterpret_cast(type); + if (sampleType.IsProfileType()) { + ASSERT_EQ(std::string(methodName), "func_main_0"); + PGOHClassLayoutDesc *desc; + if (!decoder.GetHClassLayoutDesc(sampleType, &desc)) { + return; + } + ASSERT_EQ(desc->GetCtorLayoutDesc().size(), 0); + ASSERT_EQ(desc->GetPtLayoutDesc().size(), 0); + ASSERT_EQ(desc->GetLayoutDesc().size(), 1); + } + } else if (type->IsRwOpType()) { + auto pgoRWOpType = *reinterpret_cast(type); + if (std::string(methodName) == "foo") { + ASSERT_TRUE(pgoRWOpType.GetCount() == 3); + auto classType = pgoRWOpType.GetObjectInfo(0).GetProfileType(); + ASSERT_TRUE(classType.IsElementType()); + ASSERT_EQ(classType.GetId(), static_cast(ElementsKind::NUMBER)); + + classType = pgoRWOpType.GetObjectInfo(1).GetProfileType(); + ASSERT_TRUE(classType.IsElementType()); + ASSERT_EQ(classType.GetId(), static_cast(ElementsKind::HOLE_INT)); + + classType = pgoRWOpType.GetObjectInfo(2).GetProfileType(); + ASSERT_TRUE(classType.IsElementType()); + ASSERT_EQ(classType.GetId(), static_cast(ElementsKind::TAGGED)); + } else if (std::string(methodName) == "foo1") { + ASSERT_TRUE(pgoRWOpType.GetCount() == 1); + auto classType = pgoRWOpType.GetObjectInfo(0).GetProfileType(); + ASSERT_TRUE(classType.IsElementType()); + ASSERT_EQ(classType.GetId(), static_cast(ElementsKind::TAGGED)); + } + } + }; + decoder.GetTypeInfo(jsPandaFile.get(), targetRecordName, methodLiteral, callback); + } + unlink("ark-profiler18/modules.ap"); + rmdir("ark-profiler18/"); +} + +HWTEST_F_L0(PGOProfilerTest, ObjectLiteralProfileTest) +{ + mkdir("ark-profiler20/", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + const char *targetRecordName = "object_literal"; + std::shared_ptr jsPandaFile = ExecuteAndLoadJSPandaFile("ark-profiler20/", targetRecordName); + ASSERT_NE(jsPandaFile, nullptr); + uint32_t checksum = jsPandaFile->GetChecksum(); + + // Loader + PGOProfilerDecoder decoder("ark-profiler20/modules.ap", 1); + ASSERT_TRUE(decoder.LoadAndVerify(checksum)); + auto methodLiterals = jsPandaFile->GetMethodLiteralMap(); + for (auto iter : methodLiterals) { + auto methodLiteral = iter.second; + auto methodId = methodLiteral->GetMethodId(); + auto methodName = methodLiteral->GetMethodName(jsPandaFile.get(), methodId); + decoder.MatchAndMarkMethod(targetRecordName, methodName, methodId); + ASSERT_TRUE(decoder.Match(targetRecordName, methodId)); + auto callback = [methodName, &decoder, jsPandaFile](uint32_t offset, PGOType *type) { + if (type->IsScalarOpType()) { + auto sampleType = *reinterpret_cast(type); + if (sampleType.IsProfileType()) { + ASSERT_EQ(std::string(methodName), "func_main_0"); + PGOHClassLayoutDesc *desc; + if (!decoder.GetHClassLayoutDesc(sampleType, &desc)) { + return; + } + ASSERT_EQ(desc->GetCtorLayoutDesc().size(), 0); + ASSERT_EQ(desc->GetPtLayoutDesc().size(), 0); + ASSERT_EQ(desc->GetLayoutDesc().size(), 3); + } + } else if (type->IsRwOpType()) { + auto pgoRWOpType = *reinterpret_cast(type); + if (std::string(methodName) == "foo") { + ASSERT_TRUE(pgoRWOpType.GetCount() == 2); + auto classType = PGOSampleType(pgoRWOpType.GetObjectInfo(0).GetProfileType()); + PGOHClassLayoutDesc *desc; + ASSERT_TRUE(decoder.GetHClassLayoutDesc(classType, &desc)); + ASSERT_EQ(desc->GetLayoutDesc()[0].first, "x"); + ASSERT_EQ(desc->GetLayoutDesc()[0].second.GetTrackType(), TrackType::INT); + ASSERT_EQ(desc->GetLayoutDesc()[1].first, "y"); + ASSERT_EQ(desc->GetLayoutDesc()[1].second.GetTrackType(), TrackType::INT); + ASSERT_EQ(desc->GetLayoutDesc()[2].first, "z"); + ASSERT_EQ(desc->GetLayoutDesc()[2].second.GetTrackType(), TrackType::INT); + + classType = PGOSampleType(pgoRWOpType.GetObjectInfo(1).GetProfileType()); + ASSERT_TRUE(decoder.GetHClassLayoutDesc(classType, &desc)); + ASSERT_EQ(desc->GetLayoutDesc()[0].first, "u"); + ASSERT_EQ(desc->GetLayoutDesc()[0].second.GetTrackType(), TrackType::DOUBLE); + ASSERT_EQ(desc->GetLayoutDesc()[1].first, "y"); + ASSERT_EQ(desc->GetLayoutDesc()[1].second.GetTrackType(), TrackType::NUMBER); + ASSERT_EQ(desc->GetLayoutDesc()[2].first, "t"); + ASSERT_EQ(desc->GetLayoutDesc()[2].second.GetTrackType(), TrackType::TAGGED); + } + } + }; + decoder.GetTypeInfo(jsPandaFile.get(), targetRecordName, methodLiteral, callback); + } + unlink("ark-profiler20/modules.ap"); + rmdir("ark-profiler20/"); +} +#endif + +HWTEST_F_L0(PGOProfilerTest, FileConsistencyCheck) +{ + const char *source = R"( + .language ECMAScript + .function void foo1(any a0, any a1, any a2) {} + )"; + std::vector methodLiterals {}; + std::shared_ptr pf = CreateJSPandaFile(source, "ark-profiler.abc", methodLiterals); + EXPECT_EQ(methodLiterals.size(), 1); // number of methods + + mkdir("ark-profiler17/", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + RuntimeOption option; + option.SetEnableProfile(true); + option.SetProfileDir("ark-profiler17/"); + vm_ = JSNApi::CreateJSVM(option); + JSHandle constPool = vm_->GetFactory()->NewConstantPool(4); + constPool->SetJSPandaFile(pf.get()); + uint32_t checksum = 304293; + PGOProfilerManager::GetInstance()->SamplePandaFileInfo(checksum, "ark-profiler.abc"); + ASSERT_TRUE(vm_ != nullptr) << "Cannot create Runtime"; + + JSHandle method = vm_->GetFactory()->NewMethod(methodLiterals[0]); + method->SetConstantPool(vm_->GetJSThread(), constPool.GetTaggedValue()); + JSHandle func = vm_->GetFactory()->NewJSFunction(vm_->GetGlobalEnv(), method); + JSHandle recordName(vm_->GetFactory()->NewFromStdString("test")); + method->SetModule(vm_->GetJSThread(), recordName); + vm_->GetPGOProfiler()->SetSaveTimestamp(std::chrono::system_clock::now()); + vm_->GetPGOProfiler()->ProfileCall(func.GetTaggedType()); + JSNApi::DestroyJSVM(vm_); + + // write to corrupt the ap file's consistency + std::ofstream fWriter("ark-profiler17/modules.ap", std::fstream::app); + + fWriter.write(reinterpret_cast(&checksum), sizeof(checksum)); + fWriter.seekp(100); + fWriter.write(reinterpret_cast(&checksum), sizeof(checksum)); + fWriter.close(); + + // Loader + PGOProfilerDecoder loader("ark-profiler17/modules.ap", 2); + CString expectRecordName = "test"; +#if defined(SUPPORT_ENABLE_ASM_INTERP) + ASSERT_FALSE(loader.LoadAndVerify(checksum)); +#else + ASSERT_TRUE(!loader.LoadAndVerify(checksum)); + ASSERT_TRUE(loader.Match(expectRecordName, methodLiterals[0]->GetMethodId())); +#endif + unlink("ark-profiler17/modules.ap"); + rmdir("ark-profiler17/"); +} + +#if defined(SUPPORT_ENABLE_ASM_INTERP) +HWTEST_F_L0(PGOProfilerTest, MergeApSelfTwice) +{ + mkdir("ark-profiler18/", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + const char *targetRecordName = "op_type_test"; + std::shared_ptr jsPandaFile = ExecuteAndLoadJSPandaFile("ark-profiler18/", targetRecordName); + ASSERT_NE(jsPandaFile, nullptr); + + // Loader + PGOProfilerDecoder decoder("ark-profiler18/modules_merge.ap", 1); + PGOProfilerDecoder decoderSingle("ark-profiler18/modules.ap", 1); + ASSERT_TRUE(PGOProfilerManager::MergeApFiles("ark-profiler18/modules.ap:ark-profiler18/modules.ap", + "ark-profiler18/modules_merge.ap", 1, ApGenMode::OVERWRITE)); + ASSERT_TRUE(decoder.LoadFull()); + ASSERT_TRUE(decoderSingle.LoadFull()); + + auto doubleCount = + decoder.GetRecordDetailInfos().GetRecordInfos().begin()->second->GetMethodInfos().begin()->second->GetCount(); + auto singleCount = decoderSingle.GetRecordDetailInfos() + .GetRecordInfos() + .begin() + ->second->GetMethodInfos() + .begin() + ->second->GetCount(); + ASSERT_EQ(doubleCount, singleCount + singleCount); + + unlink("ark-profiler18/modules.ap"); + unlink("ark-profiler18/modules_merge.ap"); + rmdir("ark-profiler18/"); +} +#endif + +HWTEST_F_L0(PGOProfilerTest, ClassTypeLegacyCheckForWideClassType) +{ + PGOContextMock context(PGOProfilerHeader::RECORD_POOL_MINI_VERSION); + ProfileTypeLegacy classTypeLegacy(0xafe, ProfileType::Kind::ElementId); + auto &profileTypeRef = *(static_cast(static_cast(&classTypeLegacy))); + ProfileType classType(context, profileTypeRef); + ASSERT_EQ(classTypeLegacy.GetId(), 0xafe); + ASSERT_EQ(classTypeLegacy.GetKind(), ProfileType::Kind::ElementId); + ASSERT_EQ(classType.GetId(), 0xafe); + ASSERT_EQ(classType.GetKind(), ProfileType::Kind::ElementId); +} + +HWTEST_F_L0(PGOProfilerTest, PGOSampleTypeLegacyCheckForWideClassType) +{ + PGOContextMock context(PGOProfilerHeader::RECORD_POOL_MINI_VERSION); + PGOSampleTypeRef sampleTypeLegacyType(PGOSampleTypeRef::Type::NUMBER); + PGOSampleType sampleType = PGOSampleType::ConvertFrom(context, sampleTypeLegacyType); + ASSERT_TRUE(sampleTypeLegacyType.IsNumber()); + ASSERT_TRUE(sampleType.IsNumber()); + ASSERT_FALSE(sampleType.IsProfileType()); + + ProfileTypeLegacy classTypeLegacy(0xafe, ProfileType::Kind::ElementId); + auto &profileTypeRef = *(static_cast(static_cast(&classTypeLegacy))); + PGOSampleTypeRef sampleTypeLegacyClass(profileTypeRef); + ASSERT_TRUE(sampleTypeLegacyClass.IsProfileType()); + + sampleType = PGOSampleType::ConvertFrom(context, sampleTypeLegacyClass); + ASSERT_FALSE(sampleType.IsNumber()); + ASSERT_TRUE(sampleType.IsProfileType()); + ASSERT_EQ(sampleType.GetProfileType().GetId(), classTypeLegacy.GetId()); + ASSERT_EQ(sampleType.GetProfileType().GetKind(), classTypeLegacy.GetKind()); +} + +HWTEST_F_L0(PGOProfilerTest, PGOObjectInfoLegacyCheckForWideClassType) +{ + PGOContextMock context(PGOProfilerHeader::RECORD_POOL_MINI_VERSION); + ProfileTypeLegacy classTypeLegacy(0xafe, ProfileType::Kind::ElementId); + auto &profileTypeRef = *(static_cast(static_cast(&classTypeLegacy))); + PGOObjectInfoRef objInfoLegacy(profileTypeRef, PGOObjKind::CONSTRUCTOR); + PGOObjectInfo objInfo; + objInfo.ConvertFrom(context, objInfoLegacy); + ASSERT_EQ(objInfo.GetObjKind(), objInfoLegacy.GetObjKind()); + ASSERT_EQ(objInfo.GetProfileType().GetId(), 0xafe); + ASSERT_EQ(objInfo.GetProfileType().GetKind(), ProfileType::Kind::ElementId); +} + +HWTEST_F_L0(PGOProfilerTest, PGORWOpTypeLegacyCheckForWideClassType) +{ + PGOContextMock context(PGOProfilerHeader::RECORD_POOL_MINI_VERSION); + PGORWOpTypeRef rwOpLegacy; + // add item1 + ProfileTypeLegacy classTypeLegacy1(0xafe, ProfileType::Kind::ElementId); + auto &profileTypeRef1 = *(static_cast(static_cast(&classTypeLegacy1))); + PGOObjectInfoRef infoLegacy1(profileTypeRef1, PGOObjKind::CONSTRUCTOR); + rwOpLegacy.AddObjectInfo(infoLegacy1); + + // add item2 + ProfileTypeLegacy classTypeLegacy2(0xaff, ProfileType::Kind::BuiltinsId); + auto &profileTypeRef2 = *(static_cast(static_cast(&classTypeLegacy2))); + PGOObjectInfoRef infoLegacy2(profileTypeRef2, PGOObjKind::ELEMENT); + rwOpLegacy.AddObjectInfo(infoLegacy2); + + PGORWOpType rwOp; + rwOp.ConvertFrom(context, rwOpLegacy); + ASSERT_EQ(rwOp.GetCount(), rwOpLegacy.GetCount()); + // get item + PGOObjectInfo info1 = rwOp.GetObjectInfo(0); + PGOObjectInfo info2 = rwOp.GetObjectInfo(1); + + ASSERT_EQ(info1.GetObjKind(), PGOObjKind::CONSTRUCTOR); + ASSERT_EQ(info2.GetProfileType().GetId(), 0xaff); + ASSERT_EQ(info2.GetProfileType().GetKind(), ProfileType::Kind::BuiltinsId); +} + +HWTEST_F_L0(PGOProfilerTest, PGOHClassLayoutDescInnerLegacyCheckForWideClassType) +{ + PGOContextMock context(PGOProfilerHeader::RECORD_POOL_MINI_VERSION); + // create legacy + ProfileTypeLegacy classTypeLegacy(0xafe, ProfileType::Kind::ClassId); + auto &profileTypeRef = *(static_cast(static_cast(&classTypeLegacy))); + PGOSampleTypeRef sampleTypeLegacyClass(profileTypeRef); + + ProfileTypeLegacy superClassTypeLegacy(0xaff, ProfileType::Kind::ElementId); + auto &superProfileTypeRef = *(static_cast(static_cast(&superClassTypeLegacy))); + PGOSampleTypeRef superSampleTypeLegacyClass(superProfileTypeRef); + auto elementsKind = ElementsKind::HOLE_NUMBER; + PGOHClassLayoutDesc desc; + desc.SetElementsKind(elementsKind); + size_t size = PGOHClassLayoutDescInnerRef::CaculateSize(desc); + PGOHClassLayoutDescInnerRef layoutLegacy(size, sampleTypeLegacyClass, superSampleTypeLegacyClass, elementsKind); + + PGOHClassLayoutDesc descRecover = layoutLegacy.Convert(context); + + ASSERT_EQ(descRecover.GetElementsKind(), elementsKind); + ASSERT_EQ(descRecover.GetProfileType().GetId(), 0xafe); + ASSERT_EQ(descRecover.GetProfileType().GetKind(), ProfileType::Kind::ClassId); + ASSERT_EQ(descRecover.GetSuperProfileType().GetId(), 0xaff); + ASSERT_EQ(descRecover.GetSuperProfileType().GetKind(), ProfileType::Kind::ElementId); +} } // namespace panda::test diff --git a/ecmascript/pgo_profiler/tests/pgo_test_case/array_test.js b/ecmascript/pgo_profiler/tests/pgo_test_case/array_test.js new file mode 100644 index 0000000000000000000000000000000000000000..4f2aca3f72e4ffc58ee6265e5761d0fac771ab22 --- /dev/null +++ b/ecmascript/pgo_profiler/tests/pgo_test_case/array_test.js @@ -0,0 +1,33 @@ +/* + * 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. + */ + +let a = [1, 2, 3]; +let b = [1, , 3]; +let c = [1, 2, 'test', 1]; +let d = [1, 2, 3, 4.23]; + +function foo(p) { + p[1] = 1.1; +} + +function foo1(p) { + p[2] = 'test'; +} + +foo(a); +foo(b); +foo(c); +foo1(d); +c[6] = 1; diff --git a/ecmascript/pgo_profiler/tests/pgo_test_case/call_test.js b/ecmascript/pgo_profiler/tests/pgo_test_case/call_test.js new file mode 100644 index 0000000000000000000000000000000000000000..d24d7912f3915fa08725dc3e9a133f52e7be30b3 --- /dev/null +++ b/ecmascript/pgo_profiler/tests/pgo_test_case/call_test.js @@ -0,0 +1,33 @@ +/* + * 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. + */ + +class Test { + x; + y; + constructor(x, y) { + this.x = x; + this.y = y; + } +} + +function foo(p) +{ + return p.x; +} + +let a = new Test(1, 23); +for (let i = 0; i < 1000000; i++) { + foo(a); +} diff --git a/ecmascript/pgo_profiler/tests/pgo_test_case/class_test.js b/ecmascript/pgo_profiler/tests/pgo_test_case/class_test.js new file mode 100644 index 0000000000000000000000000000000000000000..39675c64130bde4689fabd1cb6860a751f0a7026 --- /dev/null +++ b/ecmascript/pgo_profiler/tests/pgo_test_case/class_test.js @@ -0,0 +1,51 @@ +/* + * 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. + */ + +class Body { + constructor(x, y) { + this.x = x; + this.y = y; + } +} + +class Foot extends Body { + constructor(x, y, u, v) { + super(x, y); + this.u = u; + this.v = v; + } +} + +class Arm extends Body { + constructor(x, y, t) { + super(x, y); + this.t = t; + } +} + +function foo(p) +{ + return p.x; +} + +let a = new Body(1, 23); +let b = new Foot(3, 3.2, 2, 32.3); +let c = new Arm(1.3, 23.2, 23); + +for (let i = 0; i < 1000000; i++) { + foo(a); + foo(b); + foo(c); +} diff --git a/ecmascript/pgo_profiler/tests/pgo_test_case/object_literal.js b/ecmascript/pgo_profiler/tests/pgo_test_case/object_literal.js new file mode 100644 index 0000000000000000000000000000000000000000..2f98f5f38cc017c1bf3807ee564a7519d6a1ef43 --- /dev/null +++ b/ecmascript/pgo_profiler/tests/pgo_test_case/object_literal.js @@ -0,0 +1,26 @@ +/* + * 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. + */ + +let a = {x: 1, y: 2, z: 1}; +let b = {u: 1.3, y: 2, t: 'test'}; + +b.y = 1.1; + +function foo(p) { + return p.x; +} + +foo(a); +foo(b); diff --git a/ecmascript/pgo_profiler/tests/pgo_test_case/op_type_test.js b/ecmascript/pgo_profiler/tests/pgo_test_case/op_type_test.js new file mode 100644 index 0000000000000000000000000000000000000000..96120cd7c710055afef3e6dfbc057b658f08b9e4 --- /dev/null +++ b/ecmascript/pgo_profiler/tests/pgo_test_case/op_type_test.js @@ -0,0 +1,69 @@ +/* + * 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. + */ + +class Body { + constructor(x, vx, mass) { + this.x = x; + this.vx = vx; + this.mass = mass; + } +} + +function advance(bodies, dt) { + const sqrt = Math.sqrt; + const size = bodies.length; + + for (let i = 0; i < size; i++) { + const bodyi = bodies[i]; + let vxi = bodyi.vx; + const xi = bodyi.x; + const massi = bodyi.mass; + + for (let j = i + 1; j < size; j++) { + const bodyj = bodies[j]; + const dx = xi - bodyj.x; + + const d2 = dx * dx; + const mag = dt / (d2 * sqrt(d2)); + const massiMag = massi * mag; + + const massj = bodyj.mass; + const massjMag = massj * mag; + vxi -= dx * massjMag; + + bodyj.vx += dx * massiMag; + } + bodyi.vx = vxi; + + bodyi.x += dt * vxi; + } +} + +const PI = Math.PI; +const SOLAR_MASS = 4 * PI * PI; + +function sun() { + return new Body(0.0, 0.0, SOLAR_MASS); +} + +function sun1() { + return new Body(1.2, 3.1, PI); +} + +const bodies = [sun(), sun1()]; +const n = 100000; +for (let i = 0; i < n; i++) { + advance(bodies, 0.01); +} diff --git a/ecmascript/pgo_profiler/types/pgo_profile_type.cpp b/ecmascript/pgo_profiler/types/pgo_profile_type.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f9ee74b2a4ab7c4a97b44dbe5439f193a57333fb --- /dev/null +++ b/ecmascript/pgo_profiler/types/pgo_profile_type.cpp @@ -0,0 +1,48 @@ +/* + * 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/pgo_profiler/types/pgo_profile_type.h" +#include "ecmascript/log.h" +#include "ecmascript/log_wrapper.h" +#include "ecmascript/pgo_profiler/pgo_profiler_info.h" +#include "ecmascript/pgo_profiler/pgo_utils.h" +#include "macros.h" + +namespace panda::ecmascript::pgo { +const ProfileType ProfileType::PROFILE_TYPE_NONE = ProfileType(0, 0); + +ProfileTypeRef::ProfileTypeRef(PGOContext &context, const ProfileType &type) +{ + ApEntityId apId(0); + context.GetProfileTypePool()->TryAdd(type, apId); + UpdateId(apId); +} + +ProfileType::ProfileType(PGOContext &context, ProfileTypeRef typeRef) +{ + if (!context.GetHeader()->SupportWideProfileType()) { + ProfileTypeLegacy legacy(typeRef); + UpdateId(legacy.GetId()); + UpdateKind(legacy.GetKind()); + } else { + const auto *typeEntry = context.GetProfileTypePool()->GetEntry(typeRef.GetId()); + if (typeEntry == nullptr) { + LOG_ECMA(ERROR) << "Profile type ref: " << typeRef.GetTypeString() << " not found in ap file."; + } else { + type_ = typeEntry->GetProfileType().GetRaw(); + } + } +} +} // namespace panda::ecmascript::pgo \ No newline at end of file diff --git a/ecmascript/pgo_profiler/types/pgo_profile_type.h b/ecmascript/pgo_profiler/types/pgo_profile_type.h new file mode 100644 index 0000000000000000000000000000000000000000..01f8235557659d4956abbed46d09d4e04216f57b --- /dev/null +++ b/ecmascript/pgo_profiler/types/pgo_profile_type.h @@ -0,0 +1,262 @@ +/* + * 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_PGO_PROFILER_TYPES_PGO_PROFILE_TYPE_H +#define ECMASCRIPT_PGO_PROFILER_TYPES_PGO_PROFILE_TYPE_H + +#include +#include +#include + +#include "ecmascript/log.h" +#include "ecmascript/log_wrapper.h" +#include "ecmascript/pgo_profiler/pgo_context.h" +#include "ecmascript/pgo_profiler/pgo_utils.h" +#include "libpandabase/utils/bit_field.h" +#include "macros.h" + +namespace panda::ecmascript::pgo { +class ProfileTypeRef; +class PGOContext; + +using ApEntityId = pgo::ApEntityId; + +class ProfileType { +public: + enum class Kind : uint8_t { + ClassId, + LiteralId, + ElementId, + BuiltinsId, + LegacyKind = BuiltinsId, + LocalRecordId, + TotalKinds, + UnknowId + }; + + static const ProfileType PROFILE_TYPE_NONE; + + static constexpr uint32_t ID_BITFIELD_NUM = 29; + static constexpr uint32_t ABC_ID_BITFIELD_NUM = 20; + static constexpr uint32_t KIND_BITFIELD_NUM = 15; + using IdBits = BitField; + using AbcIdBits = IdBits::NextField; + using KindBits = AbcIdBits::NextField; + + static_assert(KindBits::IsValid(Kind::TotalKinds)); + + ProfileType() = default; + ProfileType(PGOContext &context, ProfileTypeRef typeRef); + ProfileType(ApEntityId abcId, uint32_t type, Kind kind = Kind::ClassId) + { + if (UNLIKELY(!IdBits::IsValid(type))) { + type_ = 0; + } else { + UpdateAbcId(abcId); + UpdateId(type); + UpdateKind(kind); + } + } + + bool IsNone() const + { + return type_ == PROFILE_TYPE_NONE.type_; + } + + uint64_t GetRaw() const + { + return type_; + } + + bool IsBuiltinsType() const + { + return GetKind() == Kind::BuiltinsId; + } + + bool IsClassType() const + { + return GetKind() == Kind::ClassId; + } + + bool IsElementType() const + { + return GetKind() == Kind::ElementId; + } + + uint32_t GetId() const + { + return IdBits::Decode(type_); + } + + Kind GetKind() const + { + return KindBits::Decode(type_); + } + + ApEntityId GetAbcId() const + { + return AbcIdBits::Decode(type_); + } + + void UpdateAbcId(ApEntityId abcId) + { + type_ = AbcIdBits::Update(type_, abcId); + } + + bool operator<(const ProfileType &right) const + { + return type_ < right.type_; + } + + bool operator!=(const ProfileType &right) const + { + return type_ != right.type_; + } + + bool operator==(const ProfileType &right) const + { + return type_ == right.type_; + } + + std::string GetTypeString() const + { + std::stringstream stream; + stream << "type: " << std::showbase << std::hex << type_ << + "(kind: " << std::showbase << std::dec << static_cast(GetKind()) << + ", abcId: " << GetAbcId() << + ", id: " << GetId() << ")"; + return stream.str(); + } + + void UpdateId(uint32_t id) + { + type_ = IdBits::Update(type_, id); + } + + void UpdateKind(Kind kind) + { + type_ = KindBits::Update(type_, kind); + } + +private: + void UpdateId(uint64_t type) + { + type_ = IdBits::Update(type_, type); + } + + uint64_t type_ {0}; +}; + +class ProfileTypeRef { +public: + ProfileTypeRef() = default; + explicit ProfileTypeRef(ApEntityId type) + { + UpdateId(type); + } + ProfileTypeRef(PGOContext &context, const ProfileType &type); + + bool IsNone() const + { + return typeId_ == 0; + } + + ApEntityId GetId() const + { + return typeId_; + } + + bool operator<(const ProfileTypeRef &right) const + { + return typeId_ < right.typeId_; + } + + bool operator==(const ProfileTypeRef &right) const + { + return typeId_ == right.typeId_; + } + + std::string GetTypeString() const + { + return std::to_string(typeId_); + } + + void UpdateId(ApEntityId typeId) + { + typeId_ = typeId; + } + +private: + ApEntityId typeId_ {0}; +}; +static_assert(sizeof(ProfileTypeRef) == sizeof(uint32_t)); + +class ProfileTypeLegacy { +public: + static constexpr uint32_t ID_BITFIELD_NUM = 29; + static constexpr uint32_t KIND_BITFIELD_NUM = 3; + using IdBits = BitField; + using KindBits = IdBits::NextField; + + // legacy size check. for version lower than WIDE_CLASS_TYPE_MINI_VERSION, we should consider the legacy scenario. + static_assert(ID_BITFIELD_NUM == ProfileType::ID_BITFIELD_NUM); + static_assert(KindBits::IsValid(ProfileType::Kind::LegacyKind)); + + explicit ProfileTypeLegacy(uint32_t type, ProfileType::Kind kind = ProfileType::Kind::ClassId) + { + if (!IdBits::IsValid(type)) { + type_ = 0; + } else { + UpdateId(type); + UpdateKind(kind); + } + } + + explicit ProfileTypeLegacy(ProfileTypeRef profileTypeRef) : type_(profileTypeRef.GetId()) {} + + bool IsNone() const + { + return type_ == 0; + } + + uint32_t GetRaw() const + { + return type_; + } + + uint32_t GetId() const + { + return IdBits::Decode(type_); + } + + ProfileType::Kind GetKind() const + { + return KindBits::Decode(type_); + } + +private: + void UpdateId(uint32_t type) + { + type_ = IdBits::Update(type_, type); + } + + void UpdateKind(ProfileType::Kind kind) + { + type_ = KindBits::Update(type_, kind); + } + uint32_t type_ {0}; +}; +} // namespace panda::ecmascript::pgo +#endif // ECMASCRIPT_PGO_PROFILER_TYPES_PGO_PROFILE_TYPE_H diff --git a/ecmascript/pgo_profiler/pgo_profiler_type.h b/ecmascript/pgo_profiler/types/pgo_profiler_type.h similarity index 38% rename from ecmascript/pgo_profiler/pgo_profiler_type.h rename to ecmascript/pgo_profiler/types/pgo_profiler_type.h index 30a3e9c7e0402503ca56dde8fb57a1c805dd7ecd..09d952bfbd31789d7bc68c6c28db61567f4bc695 100644 --- a/ecmascript/pgo_profiler/pgo_profiler_type.h +++ b/ecmascript/pgo_profiler/types/pgo_profiler_type.h @@ -13,55 +13,21 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_PGO_PROFILER_TYPE_H -#define ECMASCRIPT_PGO_PROFILER_TYPE_H +#ifndef ECMASCRIPT_PGO_PROFILER_TYPES_PGO_PROFILER_TYPE_H +#define ECMASCRIPT_PGO_PROFILER_TYPES_PGO_PROFILER_TYPE_H #include #include #include +#include "ecmascript/log_wrapper.h" +#include "ecmascript/pgo_profiler/pgo_utils.h" +#include "ecmascript/pgo_profiler/types/pgo_profile_type.h" +#include "libpandabase/utils/bit_field.h" #include "macros.h" -namespace panda::ecmascript { -class ClassType { -public: - ClassType() = default; - explicit ClassType(int32_t type) : type_(type) {} - - bool IsNone() const - { - return type_ == 0; - } - - int32_t GetClassType() const - { - return type_; - } - - bool operator<(const ClassType &right) const - { - return type_ < right.type_; - } - - bool operator!=(const ClassType &right) const - { - return type_ != right.type_; - } - - bool operator==(const ClassType &right) const - { - return type_ == right.type_; - } - - std::string GetTypeString() const - { - return std::to_string(type_); - } - -private: - int32_t type_ { 0 }; -}; - +namespace panda::ecmascript::pgo { +class PGOContext; class PGOType { public: enum class TypeKind : uint8_t { @@ -82,7 +48,7 @@ public: } private: - TypeKind kind_ { TypeKind::SCALAR_OP_TYPE }; + TypeKind kind_ {TypeKind::SCALAR_OP_TYPE}; }; /** @@ -90,15 +56,21 @@ private: * | NUMBER NUMBER_HETEROE1 * | DOUBLE / / */ -class PGOSampleType : public PGOType { +template +class PGOSampleTemplate : public PGOType { public: + static constexpr int WEIGHT_BITS = 11; + static constexpr int WEIGHT_START_BIT = 10; + static constexpr int WEIGHT_TRUE_START_BIT = WEIGHT_START_BIT + WEIGHT_BITS; + static constexpr int WEIGHT_MASK = (1 << WEIGHT_BITS) - 1; + enum class Type : uint32_t { NONE = 0x0ULL, - INT = 0x1ULL, // 00000001 - INT_OVERFLOW = (0x1ULL << 1) | INT, // 00000011 - DOUBLE = 0x1ULL << 2, // 00000100 - NUMBER = INT | DOUBLE, // 00000101 - NUMBER1 = INT_OVERFLOW | DOUBLE, // 00000111 + INT = 0x1ULL, // 00000001 + INT_OVERFLOW = (0x1ULL << 1) | INT, // 00000011 + DOUBLE = 0x1ULL << 2, // 00000100 + NUMBER = INT | DOUBLE, // 00000101 + NUMBER1 = INT_OVERFLOW | DOUBLE, // 00000111 BOOLEAN = 0x1ULL << 3, UNDEFINED_OR_NULL = 0x1ULL << 4, SPECIAL = 0x1ULL << 5, @@ -107,23 +79,33 @@ public: BIG_INT = 0x1ULL << 7, HEAP_OBJECT = 0x1ULL << 8, HEAP_OR_UNDEFINED_OR_NULL = HEAP_OBJECT | UNDEFINED_OR_NULL, - ANY = 0x3FFULL, + ANY = (0x1ULL << WEIGHT_START_BIT) - 1, }; - PGOSampleType() : type_(Type::NONE) {}; + PGOSampleTemplate() : type_(Type::NONE) {}; + + explicit PGOSampleTemplate(Type type) : type_(type) {}; + explicit PGOSampleTemplate(uint32_t type) : type_(Type(type)) {}; + explicit PGOSampleTemplate(PGOProfileType type) : type_(type) {} - explicit PGOSampleType(Type type) : type_(type) {}; - explicit PGOSampleType(uint32_t type) : type_(Type(type)) {}; - explicit PGOSampleType(ClassType type) : type_(type) {} + template + static PGOSampleTemplate ConvertFrom(PGOContext &context, const FromType &from) + { + if (from.IsProfileType()) { + return PGOSampleTemplate(PGOProfileType(context, from.GetProfileType())); + } + return PGOSampleTemplate(static_cast(from.GetType())); + } - static PGOSampleType CreateClassType(int32_t classType) + static PGOSampleTemplate CreateProfileType(ApEntityId recordId, int32_t profileType, + typename ProfileType::Kind kind = ProfileType::Kind::ClassId) { - return PGOSampleType(ClassType(classType)); + return PGOSampleTemplate(PGOProfileType(recordId, profileType, kind)); } - static PGOSampleType NoneType() + static PGOSampleTemplate NoneType() { - return PGOSampleType(Type::NONE); + return PGOSampleTemplate(Type::NONE); } static int32_t None() @@ -191,23 +173,37 @@ public: return static_cast(static_cast(curType) | static_cast(newType)); } - static PGOSampleType NoneClassType() + static PGOSampleTemplate NoneProfileType() { - return PGOSampleType(ClassType()); + return PGOSampleTemplate(PGOProfileType()); } - PGOSampleType CombineType(PGOSampleType type) + PGOSampleTemplate CombineType(PGOSampleTemplate type) { if (type_.index() == 0) { + auto oldType = static_cast(std::get(type_)); + oldType = oldType & static_cast(AnyType()); type_ = - Type(static_cast(std::get(type_)) | static_cast(std::get(type.type_))); + Type(oldType | static_cast(std::get(type.type_))); } else { - SetType(type); + this->SetType(type); } return *this; } - void SetType(PGOSampleType type) + PGOSampleTemplate CombineCallTargetType(PGOSampleTemplate type) + { + ASSERT(type_.index() == 1); + uint32_t oldMethodId = GetProfileType().GetId(); + uint32_t newMethodId = type.GetProfileType().GetId(); + // If we have recorded a valid method if before, invalidate it. + if ((oldMethodId != newMethodId) && (oldMethodId != 0)) { + type_ = ProfileType::PROFILE_TYPE_NONE; + } + return *this; + } + + void SetType(PGOSampleTemplate type) { type_ = type.type_; } @@ -217,44 +213,57 @@ public: if (type_.index() == 0) { return std::to_string(static_cast(std::get(type_))); } else { - return std::get(type_).GetTypeString(); + return std::get(type_).GetTypeString(); } } - bool IsClassType() const + bool IsProfileType() const { return type_.index() == 1; } - ClassType GetClassType() const + PGOProfileType GetProfileType() const + { + ASSERT(IsProfileType()); + return std::get(type_); + } + + Type GetPrimitiveType() const + { + auto type = static_cast(std::get(type_)); + return Type(type & static_cast(AnyType())); + } + + uint32_t GetWeight() const { - ASSERT(IsClassType()); - return std::get(type_); + ASSERT(type_.index() == 0); + auto type = static_cast(std::get(type_)); + return type >> WEIGHT_START_BIT; } bool IsAny() const { - return type_.index() == 0 && std::get(type_) == Type::ANY; + return type_.index() == 0 && GetPrimitiveType() == Type::ANY; } bool IsNone() const { - return type_.index() == 0 && std::get(type_) == Type::NONE; + return type_.index() == 0 && GetPrimitiveType() == Type::NONE; } bool IsInt() const { - return type_.index() == 0 && std::get(type_) == Type::INT; + return type_.index() == 0 && GetPrimitiveType() == Type::INT; } bool IsIntOverFlow() const { - return type_.index() == 0 && std::get(type_) == Type::INT_OVERFLOW; + return type_.index() == 0 && GetPrimitiveType() == Type::INT_OVERFLOW; } bool IsDouble() const { - return type_.index() == 0 && std::get(type_) == Type::DOUBLE; + return type_.index() == 0 && GetPrimitiveType() == Type::DOUBLE; } bool IsNumber() const @@ -262,7 +271,8 @@ public: if (type_.index() != 0) { return false; } - switch (std::get(type_)) { + auto primType = GetPrimitiveType(); + switch (primType) { case Type::INT: case Type::INT_OVERFLOW: case Type::DOUBLE: @@ -274,74 +284,171 @@ public: } } - bool operator<(const PGOSampleType &right) const + bool operator<(const PGOSampleTemplate &right) const { return type_ < right.type_; } - bool operator!=(const PGOSampleType &right) const + bool operator!=(const PGOSampleTemplate &right) const { return type_ != right.type_; } - bool operator==(const PGOSampleType &right) const + bool operator==(const PGOSampleTemplate &right) const { return type_ == right.type_; } + Type GetType() const + { + ASSERT(!IsProfileType()); + return std::get(type_); + } + private: - std::variant type_; + std::variant type_; +}; + +using PGOSampleType = PGOSampleTemplate; +using PGOSampleTypeRef = PGOSampleTemplate; + +enum class PGOObjKind { + LOCAL, + PROTOTYPE, + CONSTRUCTOR, + ELEMENT, }; -class PGORWOpType : public PGOType { +template +class PGOObjectTemplate { public: - PGORWOpType() : PGOType(TypeKind::RW_OP_TYPE) {}; - explicit PGORWOpType(const PGOSampleType &type) : PGOType(TypeKind::RW_OP_TYPE), count_(0) + PGOObjectTemplate() = default; + PGOObjectTemplate(PGOProfileType type, PGOObjKind kind) : type_(type), objKind_(kind) {} + + template + void ConvertFrom(PGOContext &context, const FromType &from) { - ASSERT(type.IsClassType()); - AddClassType(type.GetClassType()); + objKind_ = from.GetObjKind(); + type_ = PGOProfileType(context, from.GetProfileType()); } - void Merge(const PGORWOpType &type) + std::string GetInfoString() const { - for (int i = 0; i < type.count_; i++) { - AddClassType(type.type_[i]); + std::string result = type_.GetTypeString(); + result += "("; + if (objKind_ == PGOObjKind::CONSTRUCTOR) { + result += "c"; + } else if (objKind_ == PGOObjKind::PROTOTYPE) { + result += "p"; + } else if (objKind_ == PGOObjKind::ELEMENT) { + result += "e"; + } else { + result += "l"; } + result += ")"; + return result; + } + + PGOProfileType GetProfileType() const + { + return type_; } - void AddClassType(const ClassType &type) + PGOObjKind GetObjKind() const { - if (type.IsNone()) { + return objKind_; + } + + bool IsNone() const + { + return type_.IsNone(); + } + + bool InConstructor() const + { + return objKind_ == PGOObjKind::CONSTRUCTOR; + } + + bool InElement() const + { + return objKind_ == PGOObjKind::ELEMENT; + } + + bool operator<(const PGOObjectTemplate &right) const + { + return type_ < right.type_ || objKind_ < right.objKind_; + } + + bool operator==(const PGOObjectTemplate &right) const + { + return type_ == right.type_ && objKind_ == right.objKind_; + } + +private: + PGOProfileType type_ {PGOProfileType()}; + PGOObjKind objKind_ {PGOObjKind::LOCAL}; +}; +using PGOObjectInfo = PGOObjectTemplate; +using PGOObjectInfoRef = PGOObjectTemplate; + +template +class PGORWOpTemplate : public PGOType { +public: + PGORWOpTemplate() : PGOType(TypeKind::RW_OP_TYPE) {}; + + template + void ConvertFrom(PGOContext &context, const FromType &from) + { + count_ = std::min(from.GetCount(), static_cast(POLY_CASE_NUM)); + for (uint32_t index = 0; index < count_; index++) { + infos_[index].ConvertFrom(context, from.GetObjectInfo(index)); + } + } + + void Merge(const PGORWOpTemplate &type) + { + for (uint32_t i = 0; i < type.count_; i++) { + AddObjectInfo(type.infos_[i]); + } + } + + void AddObjectInfo(const PGOObjectInfoType &info) + { + if (info.IsNone()) { return; } - int32_t count = 0; + uint32_t count = 0; for (; count < count_; count++) { - if (type_[count] == type) { + if (infos_[count] == info) { return; } } - if (count < 4) { - type_[count] = type; + if (count < static_cast(POLY_CASE_NUM)) { + infos_[count] = info; count_++; } else { LOG_ECMA(DEBUG) << "Class type exceeds 4, discard"; } } - ClassType GetType(int32_t index) const + PGOObjectInfoType GetObjectInfo(uint32_t index) const { ASSERT(index < count_); - return type_[index]; + return infos_[index]; } - int32_t GetCount() const + uint32_t GetCount() const { return count_; } private: - int count_ = 0; - ClassType type_[4]; + static constexpr int POLY_CASE_NUM = 4; + uint32_t count_ = 0; + PGOObjectInfoType infos_[POLY_CASE_NUM]; }; -} // namespace panda::ecmascript -#endif // ECMASCRIPT_PGO_PROFILER_TYPE_H + +using PGORWOpType = PGORWOpTemplate; +using PGORWOpTypeRef = PGORWOpTemplate; +} // namespace panda::ecmascript::pgo +#endif // ECMASCRIPT_PGO_PROFILER_TYPES_PGO_PROFILER_TYPE_H diff --git a/ecmascript/platform/common/mutex.cpp b/ecmascript/platform/common/mutex.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3d5621c339c57ba4801062f3fff5bafc597d4eca --- /dev/null +++ b/ecmascript/platform/common/mutex.cpp @@ -0,0 +1,199 @@ +/* + * 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/platform/mutex.h" + +#include "ecmascript/log_wrapper.h" + +#include +#include + +namespace panda::ecmascript { +inline void FatalIfError(const char *f, int rc) +{ + if (rc != 0) { + LOG_ECMA(FATAL)<< f << " failed: " << rc; + } +} + +Mutex::Mutex(bool is_init) : mutex_() +{ + if (is_init) { + Init(nullptr); + } +} + +Mutex::~Mutex() +{ + int rc = pthread_mutex_destroy(&mutex_); + FatalIfError("pthread_mutex_destroy", rc); +} + +void Mutex::Init(pthread_mutexattr_t *attrs) +{ + int rc = pthread_mutex_init(&mutex_, attrs); + FatalIfError("pthread_mutex_init", rc); +} + +void Mutex::Lock() +{ + int rc = pthread_mutex_lock(&mutex_); + FatalIfError("pthread_mutex_lock", rc); +} + +bool Mutex::TryLock() +{ + int rc = pthread_mutex_trylock(&mutex_); + if (rc == EBUSY) { + return false; + } + + FatalIfError("pthread_mutex_trylock", rc); + + return true; +} + +void Mutex::Unlock() +{ + int rc = pthread_mutex_unlock(&mutex_); + FatalIfError("pthread_mutex_unlock", rc); +} + +RecursiveMutex::RecursiveMutex() : Mutex(false) +{ + pthread_mutexattr_t attrs; + pthread_mutexattr_init(&attrs); + pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_RECURSIVE); + Init(&attrs); +} + +RWLock::RWLock() : rwlock_() +{ + int rc = pthread_rwlock_init(&rwlock_, nullptr); + FatalIfError("pthread_rwlock_init", rc); +} + +RWLock::~RWLock() +{ + int rc = pthread_rwlock_destroy(&rwlock_); + FatalIfError("pthread_rwlock_destroy", rc); +} + +void RWLock::ReadLock() +{ + int rc = pthread_rwlock_rdlock(&rwlock_); + FatalIfError("pthread_rwlock_rdlock", rc); +} + +void RWLock::WriteLock() +{ + int rc = pthread_rwlock_wrlock(&rwlock_); + FatalIfError("pthread_rwlock_wrlock", rc); +} + +bool RWLock::TryReadLock() +{ + int rc = pthread_rwlock_tryrdlock(&rwlock_); + if (rc == EBUSY) { + return false; + } + + FatalIfError("pthread_rwlock_tryrdlock", rc); + + return true; +} + +bool RWLock::TryWriteLock() +{ + int rc = pthread_rwlock_trywrlock(&rwlock_); + if (rc == EBUSY) { + return false; + } + + FatalIfError("pthread_rwlock_trywrlock", rc); + + return true; +} + +void RWLock::Unlock() +{ + int rc = pthread_rwlock_unlock(&rwlock_); + FatalIfError("pthread_rwlock_unlock", rc); +} + +ConditionVariable::ConditionVariable() : cond_() +{ + int rc = pthread_cond_init(&cond_, nullptr); + FatalIfError("pthread_cond_init", rc); +} + +ConditionVariable::~ConditionVariable() +{ + int rc = pthread_cond_destroy(&cond_); + FatalIfError("pthread_cond_destroy", rc); +} + +void ConditionVariable::Signal() +{ + int rc = pthread_cond_signal(&cond_); + FatalIfError("pthread_cond_signal", rc); +} + +void ConditionVariable::SignalAll() +{ + int rc = pthread_cond_broadcast(&cond_); + FatalIfError("pthread_cond_broadcast", rc); +} + +void ConditionVariable::Wait(Mutex *mutex) +{ + int rc = pthread_cond_wait(&cond_, &mutex->mutex_); + FatalIfError("pthread_cond_wait", rc); +} + +struct timespec ConvertTime(uint64_t ms, uint64_t ns, bool is_absolute) +{ + struct timespec abs_time = {0, 0}; + if (!is_absolute) { + clock_gettime(CLOCK_REALTIME, &abs_time); + } + const int64_t MILLISECONDS_PER_SEC = 1000; + const int64_t NANOSECONDS_PER_MILLISEC = 1000000; + const int64_t NANOSECONDS_PER_SEC = 1000000000; + time_t seconds = ms / MILLISECONDS_PER_SEC; + time_t nanoseconds = (ms % MILLISECONDS_PER_SEC) * NANOSECONDS_PER_MILLISEC + ns; + abs_time.tv_sec += seconds; + abs_time.tv_nsec += nanoseconds; + if (abs_time.tv_nsec >= NANOSECONDS_PER_SEC) { + abs_time.tv_nsec -= NANOSECONDS_PER_SEC; + abs_time.tv_sec++; + } + return abs_time; +} + +bool ConditionVariable::TimedWait(Mutex *mutex, uint64_t ms, uint64_t ns, bool is_absolute) +{ + struct timespec abs_time = ConvertTime(ms, ns, is_absolute); + int rc = pthread_cond_timedwait(&cond_, &mutex->mutex_, &abs_time); + if (rc != 0) { + if (rc == ETIMEDOUT) { + // interrupted + return true; + } + } + FatalIfError("pthread_cond_timedwait", rc); + return false; +} +} // namespace panda::ecmascript \ No newline at end of file diff --git a/ecmascript/platform/file.h b/ecmascript/platform/file.h index 03592f062b3d59d7590b31c6ca68730d84c622bc..e014f4158fece8a60d26532a9de91d98992f4688 100644 --- a/ecmascript/platform/file.h +++ b/ecmascript/platform/file.h @@ -30,6 +30,7 @@ #include "ecmascript/ecma_string.h" #include "ecmascript/js_tagged_value.h" namespace panda::ecmascript { +class SourceTextModule; #ifdef PANDA_TARGET_WINDOWS using fd_t = HANDLE; #define INVALID_FD INVALID_HANDLE_VALUE @@ -62,6 +63,7 @@ using fd_t = int; #define FILE_FAILED 0 std::string GetFileDelimiter(); +std::string GetPathSeparator(); bool RealPath(const std::string &path, std::string &realPath, bool readOnly = true); void DPrintf(fd_t fd, const std::string &buffer); void Close(fd_t fd); @@ -72,5 +74,6 @@ JSHandle ResolveFilenameFromNative(JSThread *thread, JSTaggedValue d JSTaggedValue request); bool FileExist(const char *filename); int Unlink(const char *filename); +bool TryToRemoveSO(JSThread *thread, JSHandle module); } // namespace panda::ecmascript #endif // ECMASCRIPT_PLATFORM_FILE_H diff --git a/ecmascript/platform/mutex.h b/ecmascript/platform/mutex.h new file mode 100644 index 0000000000000000000000000000000000000000..4fe7fc9ad601766a51a6f352497fd5a7d6351ad9 --- /dev/null +++ b/ecmascript/platform/mutex.h @@ -0,0 +1,160 @@ +/* + * 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_PLATFORM_MUTEX_H +#define ECMASCRIPT_PLATFORM_MUTEX_H + +#include + +#include "ecmascript/common.h" + +namespace panda::ecmascript { + +class PUBLIC_API Mutex { +public: + explicit Mutex(bool is_init = true); + + ~Mutex(); + + void Lock(); + + bool TryLock(); + + void Unlock(); + +protected: + void Init(pthread_mutexattr_t *attrs); + +private: + pthread_mutex_t mutex_; + + NO_COPY_SEMANTIC(Mutex); + NO_MOVE_SEMANTIC(Mutex); + + friend class ConditionVariable; +}; + +class RecursiveMutex : public Mutex { +public: + RecursiveMutex(); + + ~RecursiveMutex() = default; + + NO_COPY_SEMANTIC(RecursiveMutex); + NO_MOVE_SEMANTIC(RecursiveMutex); +}; + +class RWLock { +public: + RWLock(); + + ~RWLock(); + + void ReadLock(); + + void WriteLock(); + + bool TryReadLock(); + + bool TryWriteLock(); + + void Unlock(); + +private: + pthread_rwlock_t rwlock_; + + NO_COPY_SEMANTIC(RWLock); + NO_MOVE_SEMANTIC(RWLock); +}; + +class PUBLIC_API ConditionVariable { +public: + ConditionVariable(); + + ~ConditionVariable(); + + void Signal(); + + void SignalAll(); + + void Wait(Mutex *mutex); + + bool TimedWait(Mutex *mutex, uint64_t ms, uint64_t ns = 0, bool is_absolute = false); + +private: + pthread_cond_t cond_; + + NO_COPY_SEMANTIC(ConditionVariable); + NO_MOVE_SEMANTIC(ConditionVariable); +}; + +class LockHolder { +public: + explicit LockHolder(Mutex &mtx) : lock_(mtx) + { + lock_.Lock(); + } + + ~LockHolder() + { + lock_.Unlock(); + } + +private: + Mutex &lock_; + + NO_COPY_SEMANTIC(LockHolder); + NO_MOVE_SEMANTIC(LockHolder); +}; + +class ReadLockHolder { +public: + explicit ReadLockHolder(RWLock &lock) : lock_(lock) + { + lock_.ReadLock(); + } + + ~ReadLockHolder() + { + lock_.Unlock(); + } + +private: + RWLock &lock_; + + NO_COPY_SEMANTIC(ReadLockHolder); + NO_MOVE_SEMANTIC(ReadLockHolder); +}; + +class WriteLockHolder { +public: + explicit WriteLockHolder(RWLock &lock) : lock_(lock) + { + lock_.WriteLock(); + } + + ~WriteLockHolder() + { + lock_.Unlock(); + } + +private: + RWLock &lock_; + + NO_COPY_SEMANTIC(WriteLockHolder); + NO_MOVE_SEMANTIC(WriteLockHolder); +}; +} // namespace panda::ecmascript +#endif // ECMASCRIPT_PLATFORM_MUTEX_H diff --git a/ecmascript/platform/unix/file.cpp b/ecmascript/platform/unix/file.cpp index 359cb65d42dca0e78712de1f2098f4429eea240d..f39d45008ff572f4c50dddd17bd654c3a10e7a22 100644 --- a/ecmascript/platform/unix/file.cpp +++ b/ecmascript/platform/unix/file.cpp @@ -20,9 +20,11 @@ #include #include -#include "ecmascript/js_tagged_value-inl.h" +#include "ecmascript/base/path_helper.h" #include "ecmascript/ecma_macros.h" +#include "ecmascript/js_tagged_value-inl.h" #include "ecmascript/log_wrapper.h" +#include "ecmascript/module/js_module_source_text.h" #include "ecmascript/platform/map.h" namespace panda::ecmascript { @@ -31,6 +33,11 @@ std::string GetFileDelimiter() return ":"; } +std::string GetPathSeparator() +{ + return "/"; +} + bool RealPath(const std::string &path, std::string &realPath, bool readOnly) { if (path.empty() || path.size() > PATH_MAX) { @@ -80,7 +87,7 @@ MemMap FileMap(const char *fileName, int flag, int prot, int64_t offset) return MemMap(); } - size_t size = static_cast(lseek(fd, 0, SEEK_END)); + off_t size = lseek(fd, 0, SEEK_END); if (size <= 0) { close(fd); LOG_ECMA(ERROR) << fileName << " file is empty"; @@ -142,4 +149,16 @@ int Unlink(const char *filename) { return unlink(filename); } + +bool TryToRemoveSO(JSThread *thread, JSHandle module) +{ + UnloadNativeModuleCallback unloadNativeModuleCallback = thread->GetEcmaVM()->GetUnloadNativeModuleCallback(); + if (unloadNativeModuleCallback == nullptr) { + LOG_ECMA(ERROR) << "unloadNativeModuleCallback is nullptr"; + return false; + } + + CString soName = base::PathHelper::GetStrippedModuleName(ConvertToString(module->GetEcmaModuleRecordName())); + return unloadNativeModuleCallback(soName.c_str()); +} } // namespace panda::ecmascript diff --git a/ecmascript/platform/windows/file.cpp b/ecmascript/platform/windows/file.cpp index da90cdd85cf6301761a246ff090eb1b713c2a89a..3d7346eae874475014ecd35c8621df39b48589b2 100644 --- a/ecmascript/platform/windows/file.cpp +++ b/ecmascript/platform/windows/file.cpp @@ -45,6 +45,11 @@ std::string GetFileDelimiter() return ";"; } +std::string GetPathSeparator() +{ + return "\\"; +} + bool RealPath(const std::string &path, std::string &realPath, [[maybe_unused]] bool readOnly) { realPath = ""; @@ -78,6 +83,9 @@ void Close(fd_t fd) MemMap FileMap(const char *fileName, int flag, int prot, int64_t offset) { + if (prot == PAGE_PROT_READWRITE) { + flag |= FILE_RDONLY | FILE_WRONLY; + } fd_t fd = CreateFile(fileName, flag, 0, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); if (fd == INVALID_FD) { LOG_ECMA(ERROR) << fileName << " file open failed"; @@ -163,4 +171,9 @@ int Unlink(const char *filename) { return _unlink(filename); } + +bool TryToRemoveSO([[maybe_unused]] JSThread *thread, [[maybe_unused]] JSHandle module) +{ + return false; +} } // namespace panda::ecmascript diff --git a/ecmascript/platform/windows/time.cpp b/ecmascript/platform/windows/time.cpp index 5e4733af77e40e96bc828e9475cb9cb0bbfd2863..8c67158ee3cf58efe99816df011d76af31ba6f8d 100644 --- a/ecmascript/platform/windows/time.cpp +++ b/ecmascript/platform/windows/time.cpp @@ -17,6 +17,7 @@ #include #include +#include namespace panda::ecmascript { static constexpr uint16_t THOUSAND = 1000; @@ -37,6 +38,29 @@ bool IsDst(int64_t timeMs) time_t tv = timeMs; struct tm nowtm; localtime_s(&nowtm, &tv); - return nowtm.tm_isdst; + + int month = nowtm.tm_mon + 1; + int day = nowtm.tm_mday; + int hour = nowtm.tm_hour; + + TIME_ZONE_INFORMATION tzi; + GetTimeZoneInformation(&tzi); + + SYSTEMTIME stDSTStart = tzi.DaylightDate; + SYSTEMTIME stDSTEnd = tzi.StandardDate; + + if (month > stDSTStart.wMonth && month < stDSTEnd.wMonth) { + return true; + } else if (month == stDSTStart.wMonth) { + if (day > stDSTStart.wDay || (day == stDSTStart.wDay && hour >= tzi.DaylightBias)) { + return true; + } + } else if (month == stDSTEnd.wMonth) { + if (day < stDSTEnd.wDay || (day == stDSTEnd.wDay && hour < tzi.DaylightBias)) { + return true; + } + } + + return false; } } // namespace panda::ecmascript diff --git a/ecmascript/property_attributes.h b/ecmascript/property_attributes.h index 31a84cbc14edd6e301319300b3094e65af7da776..ad8bfda38f2c305f28c7b8b9df1f8dae33cdd674 100644 --- a/ecmascript/property_attributes.h +++ b/ecmascript/property_attributes.h @@ -27,16 +27,15 @@ enum class Representation { NONE, INT, DOUBLE, - NUMBER, - OBJECT, - MIXED, + TAGGED, }; -enum class TrackType { - NONE, - INT, - DOUBLE, - TAGGED +enum class TrackType : uint8_t { + NONE = 0x0ULL, + INT = 0x1ULL, + DOUBLE = 0x1ULL << 1, + NUMBER = INT | DOUBLE, + TAGGED = 0x1ULL << 2 }; enum class PropertyBoxType { @@ -51,6 +50,24 @@ enum class PropertyBoxType { INVALIDATED = CONSTANT, // Cell has been deleted, invalidated or never existed. }; +/** + * [bitfield] + * Common | WritableField (bit 1) + * | EnumerableField (bit 2) + * | ConfigurableField (bit 3) + * | IsAccessorField (bit 4) + * | IsInlinedPropsField(bit 5) + * | RepresentationField(bit 6...7) + * -------------------------------- + * Fast | OffsetField(bit 8...17) + * | TrackTypeField(bit 18...20) + * | SortedIndexField(bit 21...30) + * | IsConstPropsField(bit 31) + * | IsNotHoleField(bit 32) + * ----------------------------- + * Slow | PropertyBoxTypeField(bit 8...9) + * | DictionaryOrderField(bit 10...29) + */ class PropertyAttributes { public: PropertyAttributes() = default; @@ -66,30 +83,51 @@ public: static constexpr uint32_t DICTIONARY_ORDER_NUM = 20; static constexpr uint32_t OFFSET_BITFIELD_NUM = 10; - static constexpr uint32_t MAX_CAPACITY_OF_PROPERTIES = (1U << OFFSET_BITFIELD_NUM) - 1; - - using PropertyMetaDataField = BitField; // 4: property metaData field occupies 4 bits - using AttributesField = BitField; // 4: attributes field occupies 4 bits - using DefaultAttributesField = BitField; // 3: default attributes field occupies 3 bits - using WritableField = BitField; // 1: writable field occupies 1 bits + static constexpr uint32_t REPRESENTATION_NUM = 2; + static constexpr uint32_t TRACK_TYPE_NUM = 3; + static constexpr uint32_t MAX_FAST_PROPS_CAPACITY = (1U << OFFSET_BITFIELD_NUM) - 1; + static constexpr unsigned BITS_PER_BYTE = 8; + + using PropertyMetaDataField = BitField; // 4: property metaData field occupies 4 bits + using AttributesField = BitField; // 4: attributes field occupies 4 bits + using DefaultAttributesField = BitField; // 3: default attributes field occupies 3 bits + using WritableField = BitField; // 1: writable field occupies 1 bits using EnumerableField = WritableField::NextFlag; using ConfigurableField = EnumerableField::NextFlag; - using IsAccessorField = ConfigurableField::NextFlag; // 4 + using IsAccessorField = ConfigurableField::NextFlag; // 4 + + using IsInlinedPropsField = PropertyMetaDataField::NextFlag; // 5 + using RepresentationField = IsInlinedPropsField::NextField; // 2: 2 bits, 6-7 + using CommonLastBitField = RepresentationField; + // For flags required for both fast mode and slow mode, need to be added before CommonLastBitField - // fast mode - using IsInlinedPropsField = PropertyMetaDataField::NextFlag; // 5 - using RepresentationField = IsInlinedPropsField::NextField; // 3: 3 bits, 6-8 - using OffsetField = RepresentationField::NextField; // 18 - using TrackTypeField = OffsetField::NextField; // 2: 2 bits + // --------------------------------------------------------------------------------------------- + // only for fast mode + using FastModeStartField = CommonLastBitField; + static_assert(FastModeStartField::START_BIT == CommonLastBitField::START_BIT); + static_assert(FastModeStartField::SIZE == CommonLastBitField::SIZE); + using OffsetField = FastModeStartField::NextField; // 17 + using TrackTypeField = OffsetField::NextField; // 20: 3 bits static constexpr uint32_t NORMAL_ATTR_BITS = 20; using NormalAttrField = BitField; - using SortedIndexField = TrackTypeField::NextField; // 30 - using IsConstPropsField = SortedIndexField::NextFlag; // 31 - using IsNotHoleField = IsConstPropsField::NextFlag; // 32 - // dictionary mode, include global - using PropertyBoxTypeField = PropertyMetaDataField::NextField; // 2: 2 bits, 5-6 - using DictionaryOrderField = PropertyBoxTypeField::NextField; // 26 + using SortedIndexField = TrackTypeField::NextField; // 30 + using IsConstPropsField = SortedIndexField::NextFlag; // 31 + using IsNotHoleField = IsConstPropsField::NextFlag; // 32 + using FastModeLastField = IsNotHoleField; + static_assert( + FastModeLastField::START_BIT + FastModeLastField::SIZE <= sizeof(uint32_t) * BITS_PER_BYTE, "Invalid"); + + // --------------------------------------------------------------------------------------------- + // only for dictionary mode, include global + using DictModeStartField = CommonLastBitField; + static_assert(DictModeStartField::START_BIT == CommonLastBitField::START_BIT); + static_assert(DictModeStartField::SIZE == CommonLastBitField::SIZE); + using PropertyBoxTypeField = DictModeStartField::NextField; // 2: 2 bits, 8-9 + using DictionaryOrderField = PropertyBoxTypeField::NextField; // 29 + using DictModeLastField = DictionaryOrderField; + static_assert( + DictModeLastField::START_BIT + DictModeLastField::SIZE <= sizeof(uint32_t) * BITS_PER_BYTE, "Invalid"); static constexpr uint32_t BIT_SIZE = 28; static constexpr int INITIAL_PROPERTY_INDEX = 0; @@ -128,69 +166,23 @@ public: return DefaultAttributesField::Mask(); } - static inline Representation TaggedToRepresentation(JSTaggedValue value) - { - if (value.IsInt()) { - return Representation::INT; - } - if (value.IsDouble()) { - return Representation::DOUBLE; - } - - return Representation::OBJECT; - } - - static Representation UpdateRepresentation(Representation oldRep, JSTaggedValue value) - { - if (oldRep == Representation::MIXED) { - return oldRep; - } - - Representation newRep = TaggedToRepresentation(value); - if (oldRep == Representation::NONE) { - return newRep; - } - if (oldRep == newRep) { - return oldRep; - } - - switch (oldRep) { - case Representation::INT: - case Representation::DOUBLE: - case Representation::NUMBER: - if (newRep != Representation::OBJECT) { - return Representation::NUMBER; - } - return Representation::MIXED; - case Representation::OBJECT: - return Representation::MIXED; - default: - LOG_ECMA(FATAL) << "this branch is unreachable"; - UNREACHABLE(); - } - } - bool UpdateTrackType(JSTaggedValue value) { TrackType oldType = GetTrackType(); if (oldType == TrackType::TAGGED) { return false; } - TrackType newType = TrackType::DOUBLE; + + TrackType newType = TrackType::TAGGED; if (value.IsInt()) { - newType = TrackType::INT; - } else if (value.IsObject()) { - newType = TrackType::TAGGED; + newType = static_cast(static_cast(TrackType::INT) | static_cast(oldType)); + } else if (value.IsDouble()) { + newType = static_cast(static_cast(TrackType::DOUBLE) | static_cast(oldType)); } - if (oldType == TrackType::NONE) { + if (oldType != newType) { SetTrackType(newType); return true; - } else { - if (oldType != newType) { - SetTrackType(TrackType::TAGGED); - return true; - } } return false; } @@ -275,6 +267,23 @@ public: return IsNotHoleField::Get(value_); } + inline bool IsTaggedRep() const + { + return !IsDoubleRep() && !IsIntRep(); + } + + inline bool IsDoubleRep() const + { + auto rep = GetRepresentation(); + return rep == Representation::DOUBLE; + } + + inline bool IsIntRep() const + { + auto rep = GetRepresentation(); + return rep == Representation::INT; + } + inline void SetRepresentation(Representation representation) { RepresentationField::Set(representation, &value_); diff --git a/ecmascript/property_detector-inl.h b/ecmascript/property_detector-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..f9793b0673664385bd07a6374d8214a9d991f054 --- /dev/null +++ b/ecmascript/property_detector-inl.h @@ -0,0 +1,40 @@ +/* + * 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/property_detector.h" + +#include "ecmascript/global_env.h" +#include "ecmascript/marker_cell.h" + +namespace panda::ecmascript { +#define DETECTOR_DEFINITION(type, name, index) \ + inline void PropertyDetector::Invalidate##name(JSHandle env) \ + { \ + ASSERT(Is##name##Valid(env)); \ + JSTaggedValue value = env->GetTagged##name(); \ + MarkerCell *cell = MarkerCell::Cast(value.GetTaggedObject()); \ + cell->InvalidatePropertyDetector(); \ + } \ + \ + inline bool PropertyDetector::Is##name##Valid(JSHandle env) \ + { \ + JSTaggedValue value = env->GetTagged##name(); \ + MarkerCell *cell = MarkerCell::Cast(value.GetTaggedObject()); \ + return !cell->GetIsDetectorInvalid(); \ + } +GLOBAL_ENV_DETECTOR_FIELDS(DETECTOR_DEFINITION) +#undef DETECTOR_DEFINITION + +} // namespace panda::ecmascript diff --git a/ecmascript/property_detector.h b/ecmascript/property_detector.h new file mode 100644 index 0000000000000000000000000000000000000000..5479a591832c40e105fcd2a01523c1c9161bd60d --- /dev/null +++ b/ecmascript/property_detector.h @@ -0,0 +1,46 @@ +/* + * 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_PROPERTY_DETECTOR_H +#define ECMASCRIPT_PROPERTY_DETECTOR_H + +#include "ecmascript/ecma_macros.h" +#include "ecmascript/js_handle.h" +#include "ecmascript/js_tagged_value.h" + +namespace panda { +namespace ecmascript { +class GlobalEnv; +class PropertyDetector { +public: +#define GLOBAL_ENV_DETECTOR_FIELDS(V) \ + V(JSTaggedValue, RegExpReplaceDetector, REGEXP_REPLACE_DETECTOR_INDEX) \ + V(JSTaggedValue, RegExpSplitDetector, REGEXP_SPLIT_DETECTOR_INDEX) + +#define DECLARE_DETECTOR(type, name, index) \ + static inline bool Is##name##Valid(JSHandle env); \ + static inline void Invalidate##name(JSHandle env); + GLOBAL_ENV_DETECTOR_FIELDS(DECLARE_DETECTOR) +#undef DECLARE_DETECTOR + +#define DETECTOR_SYMBOL_LIST(V) \ + V(ReplaceSymbol, "Symbol.replace", replace) \ + V(SplitSymbol, "Symbol.split", split ) +}; + +} // namespace ecmascript +} // namespace panda + +#endif // ECMASCRIPT_PROPERTY_DETECTOR_H diff --git a/ecmascript/regexp/regexp_executor.cpp b/ecmascript/regexp/regexp_executor.cpp index 3617ef976666e7dcf63fb7acf44bdc5edd042a05..43c04bce738ae5d413973f17655460adf41a975d 100644 --- a/ecmascript/regexp/regexp_executor.cpp +++ b/ecmascript/regexp/regexp_executor.cpp @@ -252,7 +252,7 @@ MatchResult RegExpExecutor::GetResult(const JSThread *thread, bool isSuccess) co { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); MatchResult result; - std::vector>> captures; + std::vector> captures; result.isSuccess_ = isSuccess; if (isSuccess) { for (uint32_t i = 0; i < nCapture_; i++) { @@ -265,25 +265,22 @@ MatchResult RegExpExecutor::GetResult(const JSThread *thread, bool isSuccess) co } } int32_t len = captureState->captureEnd - captureState->captureStart; - std::pair> pair; + std::pair pair; if ((captureState->captureStart != nullptr && captureState->captureEnd != nullptr) && (len >= 0)) { pair.first = false; + Capture capture; if (isWideChar_) { + pair.second.startIndex = (captureState->captureStart - input_) / WIDE_CHAR_SIZE; + pair.second.endIndex = (captureState->captureEnd - input_) / WIDE_CHAR_SIZE; // create utf-16 string - pair.second = factory->NewFromUtf16( + pair.second.capturedValue = factory->NewFromUtf16( reinterpret_cast(captureState->captureStart), len / 2); } else { // create utf-8 string - CVector buffer(len + 1); - uint8_t *dest = buffer.data(); - if (memcpy_s(dest, len + 1, reinterpret_cast(captureState->captureStart), len) != - EOK) { - LOG_FULL(FATAL) << "memcpy_s failed"; - UNREACHABLE(); - } - dest[len] = '\0'; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) - pair.second = - factory->NewFromUtf8(reinterpret_cast(buffer.data()), len); + pair.second.capturedValue = + factory->NewFromUtf8(reinterpret_cast(captureState->captureStart), len); + pair.second.startIndex = captureState->captureStart - input_; + pair.second.endIndex = captureState->captureEnd - input_; } } else { // undefined diff --git a/ecmascript/regexp/regexp_executor.h b/ecmascript/regexp/regexp_executor.h index ac21a2a950930b2b5ba1ee564100983f09b61394..39e14c13ba999a5318d1353ba830e62da4c03080 100644 --- a/ecmascript/regexp/regexp_executor.h +++ b/ecmascript/regexp/regexp_executor.h @@ -17,7 +17,8 @@ #define ECMASCRIPT_REGEXP_REGEXP_EXECUTOR_H #include "ecmascript/regexp/regexp_parser.h" -#include "ecmascript/mem/chunk.h" +#include "ecmascript/mem/regexp_cached_chunk.h" +#include "ecmascript/js_handle.h" namespace panda::ecmascript { class RegExpExecutor { @@ -41,15 +42,21 @@ public: __extension__ CaptureState *captureResultList_[0]; // NOLINT(modernize-avoid-c-arrays) }; + struct Capture { + uint32_t startIndex = 0; + uint32_t endIndex = 0; + JSHandle capturedValue; + }; + struct MatchResult { uint32_t endIndex_ = 0; uint32_t index_ = 0; // first value is true if result is undefined - std::vector>> captures_; + std::vector> captures_; bool isSuccess_ = false; }; - explicit RegExpExecutor(Chunk *chunk) : chunk_(chunk) + explicit RegExpExecutor(RegExpCachedChunk *chunk) : chunk_(chunk) { ASSERT(chunk_ != nullptr); }; @@ -576,8 +583,8 @@ public: int32_t idxMax = static_cast(rangeCount - 1); int32_t idx = 0; uint32_t low = 0; - uint32_t high = - byteCode.GetU16(currentPc + RANGE32_HEAD_OFFSET + idxMax * RANGE32_MAX_HALF_OFFSET + RANGE32_OFFSET); + uint32_t high = byteCode.GetU16(currentPc + RANGE32_HEAD_OFFSET + + static_cast(idxMax) * RANGE32_MAX_HALF_OFFSET + RANGE32_OFFSET); if (nowChar <= high) { while (idxMin <= idxMax) { idx = (idxMin + idxMax) / RANGE32_OFFSET; @@ -597,7 +604,7 @@ public: } return isFound; } - + uint32_t GetCurrentPC() const { return currentPc_; @@ -719,7 +726,7 @@ private: uint32_t stateStackSize_ = 0; uint32_t stateSize_ = 0; uint8_t *stateStack_ = nullptr; - Chunk *chunk_ = nullptr; + RegExpCachedChunk *chunk_ = nullptr; }; } // namespace panda::ecmascript #endif // ECMASCRIPT_REGEXP_REGEXP_EXECUTOR_H diff --git a/ecmascript/regexp/regexp_opcode.cpp b/ecmascript/regexp/regexp_opcode.cpp index 763d2f69c107371af21dfaef2aab059c20e35b87..4319fa460dcaf808e145b6dcbd8d4df5b0bc5fbe 100644 --- a/ecmascript/regexp/regexp_opcode.cpp +++ b/ecmascript/regexp/regexp_opcode.cpp @@ -582,6 +582,7 @@ void RangeSet::Insert(uint32_t start, uint32_t end) IsAdjacent(start, end, iter->first, iter->second)) { iter->first = std::min(iter->first, start); iter->second = std::max(iter->second, end); + Compress(); return; } if (iter->first > end) { @@ -592,7 +593,7 @@ void RangeSet::Insert(uint32_t start, uint32_t end) rangeSet_.emplace_back(pairElement); } } -// if RangeResult cross-intersects with [a, z], +// if RangeResult cross-intersects with [a, z] and [A, Z], // we capitalize the intersection part and insert into RangeResult. void RangeSet::Inter(RangeSet &cr, const RangeSet &s1) { @@ -603,22 +604,29 @@ void RangeSet::Inter(RangeSet &cr, const RangeSet &s1) if (rangeSet_.empty()) { return; } - uint32_t firstMax = 0; - uint32_t secondMin = 0; - for (const auto &range : rangeSet_) { - if (range.first >= s1.rangeSet_.front().first) { - firstMax = range.first; - } else { - firstMax = s1.rangeSet_.front().first; - } - if (range.second >= s1.rangeSet_.front().second) { - secondMin = s1.rangeSet_.front().second; - } else { - secondMin = range.second; - } - if (secondMin >= firstMax) { - cr.Insert(firstMax, secondMin); - cr.Insert(firstMax + 'A' - 'a', secondMin + 'A' - 'a'); + for (const auto &interItem : s1.rangeSet_) { + uint32_t firstMax = 0; + uint32_t secondMin = 0; + for (const auto &range : rangeSet_) { + if (range.first >= interItem.first) { + firstMax = range.first; + } else { + firstMax = interItem.first; + } + if (range.second >= interItem.second) { + secondMin = interItem.second; + } else { + secondMin = range.second; + } + if (secondMin < firstMax) { + continue; + } + if (firstMax >= 'a' && firstMax <= 'z') { + cr.Insert(firstMax + 'A' - 'a', secondMin + 'A' - 'a'); + } + if (firstMax >= 'A' && firstMax <= 'Z') { + cr.Insert(firstMax - 'A' + 'a', secondMin - 'A' + 'a'); + } } } } diff --git a/ecmascript/regexp/regexp_parser.cpp b/ecmascript/regexp/regexp_parser.cpp index 298a34b838afb86160c99f9c74c32602b1e09793..689f07b2a5175d8a0604d8e644fe97364be3e463 100644 --- a/ecmascript/regexp/regexp_parser.cpp +++ b/ecmascript/regexp/regexp_parser.cpp @@ -292,10 +292,10 @@ void RegExpParser::ParseAlternative(bool isBackward) icu::UnicodeSet set(atomValue, atomValue); set.closeOver(USET_CASE_INSENSITIVE); set.removeAllStrings(); - int32_t size = set.size(); + uint32_t size = static_cast(set.size()); RangeOpCode rangeOp; RangeSet rangeResult; - for (int32_t idx = 0; idx < size; idx++) { + for (uint32_t idx = 0; idx < size; idx++) { int32_t uc = set.charAt(idx); RangeSet curRange(uc); rangeResult.Insert(curRange); @@ -1227,12 +1227,13 @@ bool RegExpParser::ParseClassRanges(RangeSet *result) PrintF("Parse ClassRanges------\n"); while (c0_ != ']') { RangeSet s1; + bool needInter = false; uint32_t c1 = ParseClassAtom(&s1); if (c1 == UINT32_MAX) { ParseError("invalid class range"); return false; } - + needInter = NeedIntersection(c1); int next_c0 = *pc_; if (c0_ == '-' && next_c0 != ']') { if (c1 == CLASS_RANGE_BASE) { @@ -1264,12 +1265,21 @@ bool RegExpParser::ParseClassRanges(RangeSet *result) return false; } } + needInter = NeedIntersection(c2); result->Insert(c1, c2); + if (IsIgnoreCase() && needInter) { + ProcessIntersection(result); + } } else { result->Insert(s1); - } - if (IsIgnoreCase()) { - ProcessIntersection(result); + if (!(IsIgnoreCase() && needInter)) { + continue; + } + if (c1 <= 'z' && c1 >= 'a') { + result->Insert(RangeSet(c1 - 'a' + 'A')); + } else { + result->Insert(RangeSet(c1 - 'A' + 'a')); + } } } Advance(); @@ -1484,4 +1494,9 @@ int RegExpParser::Canonicalize(int c, bool isUnicode) } return c; } + +bool RegExpParser::NeedIntersection(uint32_t c) +{ + return (c <= 'z' && c >= 'a') || (c <= 'Z' && c >= 'A'); +} } // namespace panda::ecmascript diff --git a/ecmascript/regexp/regexp_parser.h b/ecmascript/regexp/regexp_parser.h index a1c171c6000034d18f9a4ad68234fc15bbe9399b..588b3f5e88013166ea730a7e26914cf2b1e130df 100644 --- a/ecmascript/regexp/regexp_parser.h +++ b/ecmascript/regexp/regexp_parser.h @@ -40,6 +40,7 @@ public: static constexpr auto FLAG_DOTALL = (1U << 3U); static constexpr auto FLAG_UTF16 = (1U << 4U); static constexpr auto FLAG_STICKY = (1U << 5U); + static constexpr auto FLAG_HASINDICES = (1U << 6U); static const uint32_t KEY_EOF = UINT32_MAX; static constexpr int CLASS_RANGE_BASE = 0x40000000; static constexpr uint32_t NUM_CAPTURE__OFFSET = 4; @@ -47,7 +48,7 @@ public: static constexpr uint32_t OCTAL_VALUE = 8; static constexpr uint32_t OCTAL_VALUE_RANGE = 32; static constexpr uint32_t HEX_VALUE = 16; - static constexpr int32_t DECIMAL_DIGITS_ADVANCE = 10; + static constexpr uint32_t DECIMAL_DIGITS_ADVANCE = 10; static constexpr uint32_t FLAGS_OFFSET = 12; static constexpr uint32_t OP_START_OFFSET = 16; static constexpr uint32_t UNICODE_HEX_VALUE = 4; @@ -113,6 +114,7 @@ public: int ParseEscape(const uint8_t **pp, int isUtf16); int RecountCaptures(); int IsIdentFirst(uint32_t c); + bool NeedIntersection(uint32_t c); inline CVector GetGroupNames() const { @@ -184,7 +186,8 @@ public: if (c == cur) { c = u_toupper(static_cast(c)); } - if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) { + if (((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) && + !((cur >= 'A' && cur <= 'Z') || (cur >= 'a' && cur <= 'z'))) { c = cur; } return c; @@ -193,12 +196,14 @@ public: { RangeSet cr; RangeSet cr1; - uint32_t pt[2]; // 2: Range values for a and z + 1 const uint32_t MINLOWERCHAR = 'a'; const uint32_t MAXLOWERCHAR = 'z' + 1; - pt[0] = MINLOWERCHAR; - pt[1] = MAXLOWERCHAR; - cr.Insert(pt[0], pt[1]); + const uint32_t MINUPPERCHAR = 'A'; + const uint32_t MAXUPPERCHAR = 'Z' + 1; + // Range values for a and z + 1 + cr.Insert(MINLOWERCHAR, MAXLOWERCHAR); + // Range values for A and Z + 1 + cr.Insert(MINUPPERCHAR, MAXUPPERCHAR); result->Inter(cr1, cr); result->Insert(cr1); } diff --git a/ecmascript/regexp/tests/regexp_test.cpp b/ecmascript/regexp/tests/regexp_test.cpp index 3a312973e59e9fe5745f13ff2ec850cbf1346761..11d4e90b4ddb5e425e0123b4618d00d832e852fc 100644 --- a/ecmascript/regexp/tests/regexp_test.cpp +++ b/ecmascript/regexp/tests/regexp_test.cpp @@ -40,11 +40,13 @@ public: { TestHelper::CreateEcmaVMWithScope(instance, thread, scope); chunk_ = thread->GetEcmaVM()->GetChunk(); + regExpCachedChunk_ = new RegExpCachedChunk(thread); } void TearDown() override { TestHelper::DestroyEcmaVMWithScope(instance, scope); + delete regExpCachedChunk_; } bool IsValidAlphaEscapeInAtom(char s) const @@ -102,6 +104,7 @@ protected: EcmaHandleScope *scope {nullptr}; JSThread *thread {nullptr}; Chunk *chunk_ {nullptr}; + RegExpCachedChunk *regExpCachedChunk_ {nullptr}; }; HWTEST_F_L0(RegExpTest, ParseError1) @@ -644,7 +647,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec1) bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("abc"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); @@ -653,7 +656,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec1) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromASCII("ab"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec2) @@ -666,7 +669,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec2) bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("cabd"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); @@ -675,10 +678,10 @@ HWTEST_F_L0(RegExpTest, ParseAndExec2) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 10U); JSHandle str = factory->NewFromASCII("ab"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second, str) == 0); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[2].second, str) == 0); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[3].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second.capturedValue, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[2].second.capturedValue, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[3].second.capturedValue, str) == 0); ASSERT_TRUE(result.captures_[4].first); ASSERT_TRUE(result.captures_[5].first); ASSERT_TRUE(result.captures_[6].first); @@ -697,7 +700,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec3) bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("aabaac"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); @@ -707,8 +710,8 @@ HWTEST_F_L0(RegExpTest, ParseAndExec3) ASSERT_EQ(result.captures_.size(), 2U); JSHandle str1 = factory->NewFromASCII("aaba"); JSHandle str2 = factory->NewFromASCII("ba"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str1) == 0); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second, str2) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str1) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second.capturedValue, str2) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec4) @@ -721,7 +724,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec4) bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("aabaac"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); @@ -730,7 +733,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec4) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromASCII("aa"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec5) @@ -743,7 +746,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec5) bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("b"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); @@ -752,7 +755,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec5) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromASCII(""); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec6) @@ -765,7 +768,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec6) bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("zaacbbbcac"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); @@ -778,12 +781,12 @@ HWTEST_F_L0(RegExpTest, ParseAndExec6) JSHandle str3 = factory->NewFromASCII("ac"); JSHandle str4 = factory->NewFromASCII("a"); JSHandle str5 = factory->NewFromASCII("c"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str1) == 0); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second, str2) == 0); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[2].second, str3) == 0); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[3].second, str4) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str1) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second.capturedValue, str2) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[2].second.capturedValue, str3) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[3].second.capturedValue, str4) == 0); ASSERT_TRUE(result.captures_[4].first); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[5].second, str5) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[5].second.capturedValue, str5) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec7) @@ -796,7 +799,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec7) bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("ab\nabc"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); @@ -805,7 +808,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec7) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromASCII("abc"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec8) @@ -818,7 +821,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec8) bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("ab\nabc"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); @@ -827,7 +830,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec8) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromASCII("abc"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec9) @@ -840,7 +843,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec9) bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("erv"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); @@ -849,7 +852,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec9) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromASCII("er"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec10) @@ -862,7 +865,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec10) bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("bad good"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); @@ -871,7 +874,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec10) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromASCII("d"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec11) @@ -884,7 +887,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec11) bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("\na"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); @@ -893,7 +896,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec11) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromASCII("a"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec12) @@ -906,7 +909,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec12) bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("\n"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); @@ -915,7 +918,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec12) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromASCII("\n"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec13) @@ -928,7 +931,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec13) bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("\naabc"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); @@ -937,7 +940,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec13) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromASCII("abc"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec14) @@ -950,7 +953,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec14) bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("\nbbabc"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); @@ -959,7 +962,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec14) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromASCII("abc"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec15) @@ -972,7 +975,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec15) bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("aabc"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); @@ -981,7 +984,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec15) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromASCII("a"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec16) @@ -994,7 +997,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec16) bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("ABC"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); @@ -1003,7 +1006,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec16) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromASCII("ABC"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec17) @@ -1016,7 +1019,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec17) bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("a\n"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); @@ -1025,7 +1028,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec17) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromASCII("a\n"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec18) @@ -1037,7 +1040,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec18) bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("ababc"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); @@ -1054,7 +1057,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec19) bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("ababc"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); @@ -1063,7 +1066,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec19) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromASCII("a"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec20) @@ -1076,7 +1079,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec20) bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("baaabac"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); @@ -1086,8 +1089,8 @@ HWTEST_F_L0(RegExpTest, ParseAndExec20) ASSERT_EQ(result.captures_.size(), 2U); JSHandle str1 = factory->NewFromASCII(""); JSHandle str2 = factory->NewFromASCII("aaa"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str1) == 0); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second, str2) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str1) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second.capturedValue, str2) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec21) @@ -1100,7 +1103,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec21) bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("caab"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); @@ -1109,7 +1112,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec21) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromASCII("a"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec22) @@ -1122,7 +1125,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec22) bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("aaaa:aa"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); @@ -1131,7 +1134,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec22) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromASCII("aaaa:"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec23) @@ -1144,7 +1147,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec23) bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("caab"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); @@ -1153,7 +1156,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec23) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromASCII("a"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec24) @@ -1165,7 +1168,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec24) bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("caab"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); @@ -1182,7 +1185,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec25) bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("cabab"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); @@ -1192,8 +1195,8 @@ HWTEST_F_L0(RegExpTest, ParseAndExec25) ASSERT_EQ(result.captures_.size(), 2U); JSHandle str1 = factory->NewFromASCII(""); JSHandle str2 = factory->NewFromASCII("ab"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str1) == 0); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second, str2) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str1) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second.capturedValue, str2) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec26) @@ -1206,7 +1209,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec26) bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("A"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); @@ -1215,7 +1218,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec26) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromASCII("A"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec27) @@ -1228,7 +1231,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec27) bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("Z"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); @@ -1237,7 +1240,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec27) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromASCII("Z"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec28) @@ -1250,7 +1253,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec28) bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("\n"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); @@ -1259,7 +1262,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec28) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromASCII("\n"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec29) @@ -1272,7 +1275,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec29) bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input(""); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); @@ -1281,8 +1284,8 @@ HWTEST_F_L0(RegExpTest, ParseAndExec29) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 2U); JSHandle str = factory->NewFromASCII(""); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec30) @@ -1295,7 +1298,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec30) bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input(""); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); @@ -1304,7 +1307,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec30) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 2U); JSHandle str = factory->NewFromASCII(""); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); ASSERT_TRUE(result.captures_[1].first); } @@ -1317,7 +1320,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec31) parser.Parse(); bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("aabb"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); @@ -1326,8 +1329,8 @@ HWTEST_F_L0(RegExpTest, ParseAndExec31) ASSERT_EQ(result.captures_.size(), 2U); JSHandle str1 = factory->NewFromASCII("abb"); JSHandle str2 = factory->NewFromASCII("b"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str1) == 0); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second, str2) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str1) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second.capturedValue, str2) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec32) @@ -1339,7 +1342,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec32) parser.Parse(); bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("aabb"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); @@ -1349,9 +1352,9 @@ HWTEST_F_L0(RegExpTest, ParseAndExec32) JSHandle str1 = factory->NewFromASCII("abb"); JSHandle str2 = factory->NewFromASCII("ab"); JSHandle str3 = factory->NewFromASCII("b"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str1) == 0); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second, str2) == 0); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[2].second, str3) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str1) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second.capturedValue, str2) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[2].second.capturedValue, str3) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec33) @@ -1363,7 +1366,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec33) parser.Parse(); bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("qyqya"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); @@ -1371,7 +1374,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec33) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromASCII("qya"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec34) @@ -1383,7 +1386,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec34) parser.Parse(); bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("qyqy "); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); @@ -1391,7 +1394,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec34) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromASCII("qy"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec35) @@ -1403,7 +1406,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec35) parser.Parse(); bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("xx2021-01-09"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); @@ -1414,10 +1417,10 @@ HWTEST_F_L0(RegExpTest, ParseAndExec35) JSHandle str2 = factory->NewFromASCII("2021"); JSHandle str3 = factory->NewFromASCII("01"); JSHandle str4 = factory->NewFromASCII("09"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str1) == 0); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second, str2) == 0); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[2].second, str3) == 0); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[3].second, str4) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str1) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second.capturedValue, str2) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[2].second.capturedValue, str3) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[3].second.capturedValue, str4) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec36) @@ -1429,7 +1432,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec36) parser.Parse(); bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("The Quick Brown Fox Jumps Over The Lazy Dog"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); @@ -1439,9 +1442,9 @@ HWTEST_F_L0(RegExpTest, ParseAndExec36) JSHandle str1 = factory->NewFromASCII("Quick Brown Fox Jumps"); JSHandle str2 = factory->NewFromASCII("Brown"); JSHandle str3 = factory->NewFromASCII("Jumps"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str1) == 0); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second, str2) == 0); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[2].second, str3) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str1) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second.capturedValue, str2) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[2].second.capturedValue, str3) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec37) @@ -1453,7 +1456,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec37) parser.Parse(); bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("abABc"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); @@ -1462,8 +1465,8 @@ HWTEST_F_L0(RegExpTest, ParseAndExec37) ASSERT_EQ(result.captures_.size(), 2U); JSHandle str1 = factory->NewFromASCII("abABc"); JSHandle str2 = factory->NewFromASCII("AB"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str1) == 0); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second, str2) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str1) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second.capturedValue, str2) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec38) @@ -1475,7 +1478,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec38) parser.Parse(); bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("www.netscape.com"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); @@ -1485,9 +1488,9 @@ HWTEST_F_L0(RegExpTest, ParseAndExec38) JSHandle str1 = factory->NewFromASCII("www.netscape.com"); JSHandle str2 = factory->NewFromASCII("netscape."); JSHandle str3 = factory->NewFromASCII("netscap"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str1) == 0); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second, str2) == 0); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[2].second, str3) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str1) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second.capturedValue, str2) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[2].second.capturedValue, str3) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec39) @@ -1499,7 +1502,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec39) parser.Parse(); bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("baaaac"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); @@ -1508,8 +1511,8 @@ HWTEST_F_L0(RegExpTest, ParseAndExec39) ASSERT_EQ(result.captures_.size(), 2U); JSHandle str1 = factory->NewFromASCII("b"); JSHandle str2 = factory->NewFromASCII(""); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str1) == 0); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second, str2) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str1) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second.capturedValue, str2) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec40) @@ -1521,7 +1524,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec40) parser.Parse(); bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("ab"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); @@ -1529,7 +1532,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec40) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromASCII(""); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec41) @@ -1541,7 +1544,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec41) parser.Parse(); bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("baaabaac"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); @@ -1551,10 +1554,10 @@ HWTEST_F_L0(RegExpTest, ParseAndExec41) JSHandle str1 = factory->NewFromASCII("baaabaac"); JSHandle str2 = factory->NewFromASCII("ba"); JSHandle str3 = factory->NewFromASCII("abaac"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str1) == 0); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second, str2) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str1) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second.capturedValue, str2) == 0); ASSERT_TRUE(result.captures_[2].first); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[3].second, str3) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[3].second.capturedValue, str3) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec42) @@ -1566,7 +1569,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec42) parser.Parse(); bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("\n\n\\abc324234"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); @@ -1574,7 +1577,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec42) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromASCII("abc324234"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec43) @@ -1586,7 +1589,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec43) parser.Parse(); bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("line1\nline2"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); @@ -1594,7 +1597,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec43) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromASCII("1\nl"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec44) @@ -1606,7 +1609,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec44) parser.Parse(); bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("abc\bdef"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); @@ -1614,7 +1617,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec44) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromASCII("c\bd"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec45) @@ -1626,7 +1629,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec45) parser.Parse(); bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("easy\bto\u0008ride"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); @@ -1634,7 +1637,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec45) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromASCII("easy"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec46) @@ -1646,7 +1649,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec46) parser.Parse(); bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("Course_Creator = Test"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); @@ -1655,9 +1658,9 @@ HWTEST_F_L0(RegExpTest, ParseAndExec46) ASSERT_EQ(result.captures_.size(), 3U); JSHandle str1 = factory->NewFromASCII("Course_Creator = Test"); JSHandle str2 = factory->NewFromASCII("Course_Creator"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str1) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str1) == 0); ASSERT_FALSE(result.captures_[1].first); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second, str2) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second.capturedValue, str2) == 0); ASSERT_TRUE(result.captures_[2].first); } @@ -1670,7 +1673,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec47) parser.Parse(); bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("pilOt\nsoviet robot\topenoffice"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); @@ -1678,7 +1681,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec47) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromASCII("et"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec49) @@ -1690,7 +1693,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec49) parser.Parse(); bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("ab55"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); @@ -1702,11 +1705,11 @@ HWTEST_F_L0(RegExpTest, ParseAndExec49) JSHandle str3 = factory->NewFromASCII("b"); JSHandle str4 = factory->NewFromASCII("5"); JSHandle str5 = factory->NewFromASCII("5"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str1) == 0); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second, str2) == 0); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[2].second, str3) == 0); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[3].second, str4) == 0); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[4].second, str5) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str1) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second.capturedValue, str2) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[2].second.capturedValue, str3) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[3].second.capturedValue, str4) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[4].second.capturedValue, str5) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec50) @@ -1718,7 +1721,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec50) parser.Parse(); bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("2020-12-31"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); @@ -1729,10 +1732,10 @@ HWTEST_F_L0(RegExpTest, ParseAndExec50) JSHandle str2 = factory->NewFromASCII("2020"); JSHandle str3 = factory->NewFromASCII("12-31"); JSHandle str4 = factory->NewFromASCII("31"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str1) == 0); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second, str2) == 0); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[2].second, str3) == 0); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[3].second, str4) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str1) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second.capturedValue, str2) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[2].second.capturedValue, str3) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[3].second.capturedValue, str4) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec51) @@ -1743,7 +1746,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec51) parser.Parse(); bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); std::u16string input(u"\u0000"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length() + 1, parser.GetOriginBuffer(), true); @@ -1761,7 +1764,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec52) parser.Parse(); bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("aabcdaabcd"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer()); @@ -1770,8 +1773,8 @@ HWTEST_F_L0(RegExpTest, ParseAndExec52) ASSERT_EQ(result.captures_.size(), 2U); JSHandle str1 = factory->NewFromASCII("aabcdaa"); JSHandle str2 = factory->NewFromASCII("aa"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str1) == 0); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second, str2) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str1) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[1].second.capturedValue, str2) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec53) @@ -1783,7 +1786,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec53) parser.Parse(); bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); std::u16string input(u"\u0001"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer(), true); @@ -1791,7 +1794,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec53) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromASCII("\u0001"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec54) @@ -1802,7 +1805,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec54) parser.Parse(); bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("pilot\nsoviet robot\topenoffice"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer(), false); @@ -1818,7 +1821,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec55) parser.Parse(); bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("c\u0065"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer(), false); @@ -1826,7 +1829,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec55) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromASCII("e"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec56) @@ -1838,7 +1841,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec56) parser.Parse(); bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); std::u16string input(u"a啊"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.length(), parser.GetOriginBuffer(), true); @@ -1846,7 +1849,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec56) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromUtf8("a啊"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec57) @@ -1857,7 +1860,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec57) parser.Parse(); bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); char16_t data[] = {0xd834, 0xdf06}; bool ret = executor.Execute(reinterpret_cast(data), 0, 2, parser.GetOriginBuffer(), true); ASSERT_FALSE(ret); @@ -1872,7 +1875,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec58) parser.Parse(); bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); char16_t data[] = {0xd834, 0xdf06}; bool ret = executor.Execute(reinterpret_cast(data), 0, 2, parser.GetOriginBuffer(), true); ASSERT_TRUE(ret); @@ -1880,7 +1883,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec58) ASSERT_EQ(result.captures_.size(), 1U); char16_t data1[] = {0xdf06}; JSHandle str = factory->NewFromUtf16(reinterpret_cast(data1), 1); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, ParseAndExec59) @@ -1893,7 +1896,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec59) bool parseResult = parser.IsError(); ASSERT_FALSE(parseResult); - RegExpExecutor executor(chunk_); + RegExpExecutor executor(regExpCachedChunk_); CString input("\u000B"); bool ret = executor.Execute(reinterpret_cast(input.c_str()), 0, input.size(), parser.GetOriginBuffer()); @@ -1902,7 +1905,7 @@ HWTEST_F_L0(RegExpTest, ParseAndExec59) MatchResult result = executor.GetResult(thread, ret); ASSERT_EQ(result.captures_.size(), 1U); JSHandle str = factory->NewFromASCII("\u000B"); - ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second, str) == 0); + ASSERT_TRUE(EcmaStringAccessor::Compare(instance, result.captures_[0].second.capturedValue, str) == 0); } HWTEST_F_L0(RegExpTest, RangeSet1) diff --git a/ecmascript/require/js_cjs_exports.h b/ecmascript/require/js_cjs_exports.h index b7b5714633918dd2399f425aa747a9fde6a1929a..3a942ca31877dfc5a5f5a45041866ea75325a932 100644 --- a/ecmascript/require/js_cjs_exports.h +++ b/ecmascript/require/js_cjs_exports.h @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ECMASCRIPT_REQUIRE_JS_EXPORTS_H -#define ECMASCRIPT_REQUIRE_JS_EXPORTS_H +#ifndef ECMASCRIPT_REQUIRE_JS_CJS_EXPORTS_H +#define ECMASCRIPT_REQUIRE_JS_CJS_EXPORTS_H #include "ecmascript/js_object.h" #include "ecmascript/js_tagged_value.h" @@ -31,4 +31,4 @@ public: DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, JS_CJS_EXPORTS_OFFSET, SIZE) }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_REQUIRE_JS_MODULE_NAMESPACE_H +#endif // ECMASCRIPT_REQUIRE_JS_CJS_EXPORTS_H diff --git a/ecmascript/require/js_cjs_module.cpp b/ecmascript/require/js_cjs_module.cpp index 0b269f44fb027906f66110680b60a1efdaf5de07..f8c8b213e7851edb87ded9df6c96fdd673cfbf3a 100644 --- a/ecmascript/require/js_cjs_module.cpp +++ b/ecmascript/require/js_cjs_module.cpp @@ -23,13 +23,13 @@ #include "ecmascript/jspandafile/js_pandafile_executor.h" #include "ecmascript/jspandafile/js_pandafile_manager.h" #include "ecmascript/module/module_data_extractor.h" +#include "ecmascript/module/module_path_helper.h" #include "ecmascript/platform/file.h" #include "ecmascript/require/js_cjs_module_cache.h" #include "ecmascript/require/js_require_manager.h" namespace panda::ecmascript { -using PathHelper = base::PathHelper; void CjsModule::InitializeModule(JSThread *thread, JSHandle &module, JSHandle &filename, JSHandle &dirname) { @@ -104,7 +104,7 @@ JSHandle CjsModule::Load(JSThread *thread, JSHandle & JSMutableHandle filename(thread, JSTaggedValue::Undefined()); if (jsPandaFile->IsBundlePack()) { - PathHelper::ResolveCurrentPath(thread, parent, dirname, jsPandaFile); + ModulePathHelper::ResolveCurrentPath(thread, parent, dirname, jsPandaFile); filename.Update(ResolveFilenameFromNative(thread, dirname.GetTaggedValue(), request.GetTaggedValue())); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); @@ -112,8 +112,8 @@ JSHandle CjsModule::Load(JSThread *thread, JSHandle & } else { CString currentEntryPoint = ConvertToString(entrypointVal.GetTaggedValue()); CString requestStr = ConvertToString(request.GetTaggedValue()); - requestEntryPoint = PathHelper::ConcatFileNameWithMerge(thread, jsPandaFile, mergedFilename, - currentEntryPoint, requestStr); + requestEntryPoint = ModulePathHelper::ConcatFileNameWithMerge(thread, jsPandaFile, mergedFilename, + currentEntryPoint, requestStr); RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); filename.Update(factory->NewFromUtf8(requestEntryPoint)); } @@ -127,11 +127,19 @@ JSHandle CjsModule::Load(JSThread *thread, JSHandle & // Don't get required exports from cache, execute required JSPandaFile. // module = new Module(), which belongs to required JSPandaFile. JSHandle module = factory->NewCjsModule(); - dirname.Update(PathHelper::ResolveDirPath(thread, filename)); + CString fullName = ConvertToString(filename.GetTaggedValue()); + dirname.Update(PathHelper::ResolveDirPath(thread, fullName)); InitializeModule(thread, module, filename, dirname); PutIntoCache(thread, module, filename); - if (jsPandaFile->IsJson(thread, requestEntryPoint)) { + JSRecordInfo recordInfo; + bool hasRecord = jsPandaFile->CheckAndGetRecordInfo(requestEntryPoint, recordInfo); + if (!hasRecord) { + CString msg = "cannot find record '" + requestEntryPoint + "', please check the request path."; + LOG_FULL(ERROR) << msg; + THROW_NEW_ERROR_AND_RETURN_HANDLE(thread, ErrorType::REFERENCE_ERROR, JSTaggedValue, msg.c_str()); + } + if (jsPandaFile->IsJson(recordInfo)) { JSHandle result = JSHandle(thread, ModuleDataExtractor::JsonParse(thread, jsPandaFile, requestEntryPoint)); // Set module.exports ---> exports diff --git a/ecmascript/require/js_cjs_module.h b/ecmascript/require/js_cjs_module.h index 0be63fe2a386b46b300d03cb89e298ee93e1aad1..9a85f76e511b2c63b64f440078f37735c0468294 100644 --- a/ecmascript/require/js_cjs_module.h +++ b/ecmascript/require/js_cjs_module.h @@ -57,4 +57,4 @@ public: static void RequireExecution(JSThread *thread, CString mergedFilename, CString requestEntryPoint); }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_REQUIRE_CJS_MODULE_H +#endif // ECMASCRIPT_REQUIRE_JS_CJS_MODULE_H diff --git a/ecmascript/require/js_cjs_module_cache.h b/ecmascript/require/js_cjs_module_cache.h index 81f4a791c3514b47ac7da14146fd601de7b83a07..c44a64e5b7698be1c66ad620f029fc5b924ec6ed 100644 --- a/ecmascript/require/js_cjs_module_cache.h +++ b/ecmascript/require/js_cjs_module_cache.h @@ -143,4 +143,4 @@ public: static constexpr int DEAULT_DICTIONART_CAPACITY = 4; }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_REQUIRE_CJS_MODULE_CACHE_H \ No newline at end of file +#endif // ECMASCRIPT_REQUIRE_JS_CJS_MODULE_CACHE_H \ No newline at end of file diff --git a/ecmascript/require/js_cjs_require.h b/ecmascript/require/js_cjs_require.h index 07558b20917fb8489c804a18d73fe9e24fe2bd6b..9dfc6acf7a640a4d61d39e6e6e8aae82fe4dcbdf 100644 --- a/ecmascript/require/js_cjs_require.h +++ b/ecmascript/require/js_cjs_require.h @@ -37,4 +37,4 @@ public: static JSTaggedValue Resolve(EcmaRuntimeCallInfo *argv); }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_REQUIRE_CJS_REQUIRE_H +#endif // ECMASCRIPT_REQUIRE_JS_CJS_REQUIRE_H diff --git a/ecmascript/runtime_call_id.h b/ecmascript/runtime_call_id.h index 74b4a43df1abd11967319c276fbeaeb6b51f1d54..8210a8fc93bc5e55df3b1d7b887da15b7879ba38 100644 --- a/ecmascript/runtime_call_id.h +++ b/ecmascript/runtime_call_id.h @@ -245,6 +245,8 @@ namespace panda::ecmascript { V(Array, Filter) \ V(Array, Find) \ V(Array, FindIndex) \ + V(Array, FindLast) \ + V(Array, FindLastIndex) \ V(Array, ForEach) \ V(Array, IndexOf) \ V(Array, Join) \ @@ -270,6 +272,10 @@ namespace panda::ecmascript { V(Array, Flat) \ V(Array, FlatMap) \ V(Array, At) \ + V(Array, ToReversed) \ + V(Array, With) \ + V(Array, ToSorted) \ + V(Array, ToSpliced) \ V(ArrayBuffer, Constructor) \ V(ArrayBuffer, Slice) \ V(ArrayBuffer, Species) \ @@ -411,6 +417,8 @@ namespace panda::ecmascript { V(Global, EncodeURI) \ V(Global, DecodeURIComponent) \ V(Global, EncodeURIComponent) \ + V(Global, Escape) \ + V(Global, Unescape) \ V(Intl, GetCanonicalLocales) \ V(Iterator, Constructor) \ V(Iterator, Next) \ @@ -564,9 +572,10 @@ namespace panda::ecmascript { V(Object, Seal) \ V(Object, SetPrototypeOf) \ V(Object, HasOwnProperty) \ + V(Object, HasOwn) \ V(Object, IsPrototypeOf) \ V(Object, ToLocaleString) \ - V(Object, GetBuiltinTag) \ + V(Object, GetBuiltinObjectToString) \ V(Object, ToString) \ V(Object, ValueOf) \ V(Object, ProtoGetter) \ @@ -638,6 +647,7 @@ namespace panda::ecmascript { V(RegExp, Split) \ V(RegExp, Create) \ V(RegExp, GetGlobal) \ + V(RegExp, GetHasIndices) \ V(RegExp, GetIgnoreCase) \ V(RegExp, GetMultiline) \ V(RegExp, GetDotAll) \ @@ -738,6 +748,8 @@ namespace panda::ecmascript { V(TypedArray, Fill) \ V(TypedArray, Find) \ V(TypedArray, FindIndex) \ + V(TypedArray, FindLast) \ + V(TypedArray, FindLastIndex) \ V(TypedArray, IndexOf) \ V(TypedArray, LastIndexOf) \ V(TypedArray, Reduce) \ @@ -765,9 +777,13 @@ namespace panda::ecmascript { V(TypedArray, Set) \ V(TypedArray, Slice) \ V(TypedArray, Sort) \ + V(TypedArray, ToSorted) \ V(TypedArray, Subarray) \ V(TypedArray, Values) \ + V(TypedArray, With) \ V(TypedArray, ToStringTag) \ + V(TypedArray, At) \ + V(TypedArray, ToReversed) \ V(WeakMap, Constructor) \ V(WeakMap, Delete) \ V(WeakMap, Get) \ diff --git a/ecmascript/sdk/BUILD.gn b/ecmascript/sdk/BUILD.gn index b6250b8b92e07dc9af94788edb534246e206be21..97bd6786c7bd7c0c86989452f2d740ba0946fb07 100644 --- a/ecmascript/sdk/BUILD.gn +++ b/ecmascript/sdk/BUILD.gn @@ -52,9 +52,8 @@ if (is_standard_system) { hilog_path = get_label_info("$hilog_root:libhilog_windows", "root_out_dir") deps += [ "$hilog_root:libhilog_windows" ] - sources += [ - "${hilog_path}/hiviewdfx/hilog_native/libhilog_windows${dylib_suffix}", - ] + sources += + [ "${hilog_path}/hiviewdfx/hilog/libhilog_windows${dylib_suffix}" ] # Add dynamic library "icu" icu_path = @@ -134,9 +133,8 @@ if (is_standard_system) { # Add dynamic library "hilog" hilog_path = get_label_info("$hilog_root:libhilog_linux", "root_out_dir") deps += [ "$hilog_root:libhilog_linux" ] - sources += [ - "${hilog_path}/hiviewdfx/hilog_native/libhilog_linux${dylib_suffix}", - ] + sources += + [ "${hilog_path}/hiviewdfx/hilog/libhilog_linux${dylib_suffix}" ] # Add dynamic library "icu" icu_path = @@ -236,8 +234,7 @@ if (is_standard_system) { # Add dynamic library "hilog" hilog_path = get_label_info("$hilog_root:libhilog_mac", "root_out_dir") deps += [ "$hilog_root:libhilog_mac" ] - sources += - [ "${hilog_path}/hiviewdfx/hilog_native/libhilog_mac${dylib_suffix}" ] + sources += [ "${hilog_path}/hiviewdfx/hilog/libhilog_mac${dylib_suffix}" ] # Set the output directory for all dynamic libraries. outputs = [ target_out_dir + "/ets/build-tools/ets-loader/bin/ark/build-mac/bin/{{source_file_part}}" ] diff --git a/ecmascript/shared_mm/shared_mm.cpp b/ecmascript/shared_mm/shared_mm.cpp index 19712142f27f29a9f2ec4829f4421f32b7b74c95..b854c7e65228aff22f950ab22c09427e362b3d18 100644 --- a/ecmascript/shared_mm/shared_mm.cpp +++ b/ecmascript/shared_mm/shared_mm.cpp @@ -20,7 +20,7 @@ static constexpr size_t MALLOC_SIZE_LIMIT = 2147483648; // Max internal memory u JSSharedMemoryManager::~JSSharedMemoryManager() { - os::memory::LockHolder lock(jsSharedMemoryLock_); + LockHolder lock(jsSharedMemoryLock_); auto iter = loadedJSSharedMemory_.begin(); while (iter != loadedJSSharedMemory_.end()) { const void *pointer = ToVoidPtr(iter->first); @@ -44,7 +44,7 @@ bool JSSharedMemoryManager::CreateOrLoad(void **pointer, size_t size) void JSSharedMemoryManager::InsertSharedMemory(const void *pointer) { - os::memory::LockHolder lock(jsSharedMemoryLock_); + LockHolder lock(jsSharedMemoryLock_); if (loadedJSSharedMemory_.find((uint64_t)pointer) == loadedJSSharedMemory_.end()) { loadedJSSharedMemory_[(uint64_t)pointer] = 1; } @@ -52,7 +52,7 @@ void JSSharedMemoryManager::InsertSharedMemory(const void *pointer) void JSSharedMemoryManager::IncreaseRefSharedMemory(const void *pointer) { - os::memory::LockHolder lock(jsSharedMemoryLock_); + LockHolder lock(jsSharedMemoryLock_); if (loadedJSSharedMemory_.find((uint64_t)pointer) != loadedJSSharedMemory_.end()) { loadedJSSharedMemory_[(uint64_t)pointer]++; } @@ -60,7 +60,7 @@ void JSSharedMemoryManager::IncreaseRefSharedMemory(const void *pointer) void JSSharedMemoryManager::DecreaseRefSharedMemory(const void *pointer) { - os::memory::LockHolder lock(jsSharedMemoryLock_); + LockHolder lock(jsSharedMemoryLock_); auto iter = loadedJSSharedMemory_.find((uint64_t)pointer); if (iter != loadedJSSharedMemory_.end()) { if (iter->second > 1) { diff --git a/ecmascript/shared_mm/shared_mm.h b/ecmascript/shared_mm/shared_mm.h index c7d1fe0f240a8417ce55e9377f5775a2950fd851..ec5a9f229fd61554e5542a1bffab5bacdd757937 100644 --- a/ecmascript/shared_mm/shared_mm.h +++ b/ecmascript/shared_mm/shared_mm.h @@ -17,7 +17,7 @@ #define ECMASCRIPT_SHARED_MEMORY_MANAGER_MANAGER_H #include "ecmascript/mem/c_containers.h" -#include "libpandabase/os/mutex.h" +#include "ecmascript/platform/mutex.h" namespace panda { class EcmaVm; @@ -41,7 +41,7 @@ private: void DecreaseRefSharedMemory(const void *pointer); void FreeBuffer(void *mem); void *AllocateBuffer(size_t size); - os::memory::RecursiveMutex jsSharedMemoryLock_; + RecursiveMutex jsSharedMemoryLock_; CMap loadedJSSharedMemory_; }; } // namespace ecmascript diff --git a/ecmascript/snapshot/mem/snapshot.cpp b/ecmascript/snapshot/mem/snapshot.cpp index 3bbcc293d833bbaeb07b00be050fe6075fd1a7bf..908ebda5cef77831f27b8e253de44208cd8dc47e 100644 --- a/ecmascript/snapshot/mem/snapshot.cpp +++ b/ecmascript/snapshot/mem/snapshot.cpp @@ -37,6 +37,11 @@ void Snapshot::Serialize(const CString &fileName) { TSManager *tsManager = vm_->GetJSThread()->GetCurrentEcmaContext()->GetTSManager(); JSTaggedValue root = tsManager->GetSnapshotCPList(); + if (root == JSTaggedValue::Hole()) { + // root equals hole means no data stored. + LOG_COMPILER(ERROR) << "error: no data for ai file generation!"; + return; + } Serialize(root.GetTaggedObject(), nullptr, fileName); } diff --git a/ecmascript/snapshot/mem/snapshot.h b/ecmascript/snapshot/mem/snapshot.h index cc44e0205d82154947c5831f15624fbd99a50623..9036a35ec88ffce905e85ac90872d605dcdf8ef2 100644 --- a/ecmascript/snapshot/mem/snapshot.h +++ b/ecmascript/snapshot/mem/snapshot.h @@ -41,13 +41,13 @@ public: bool Deserialize(SnapshotType type, const CString &snapshotFile, bool isBuiltins = false); protected: - struct SnapShotHeader : public base::FileHeader { + struct SnapShotHeader : public base::FileHeaderBase { public: - explicit SnapShotHeader(const VersionType &lastVersion) : base::FileHeader(lastVersion) {} + explicit SnapShotHeader(const VersionType &lastVersion) : base::FileHeaderBase(lastVersion) {} bool Verify(const VersionType &lastVersion) const { - return InternalVerify("snapshot file", lastVersion, AOTFileVersion::AI_STRICT_MATCH); + return VerifyVersion("snapshot file", lastVersion, AOTFileVersion::AI_STRICT_MATCH); } uint32_t oldSpaceObjSize {0}; @@ -60,7 +60,7 @@ protected: uint32_t rootObjectSize {0}; }; - virtual const base::FileHeader::VersionType &GetLastVersion() const + virtual const base::FileHeaderBase::VersionType &GetLastVersion() const { return AOTFileVersion::AI_VERSION; } diff --git a/ecmascript/snapshot/mem/snapshot_env.cpp b/ecmascript/snapshot/mem/snapshot_env.cpp index 0c3f89f2c201e79d61b2f01be254441dbb44e634..e731aba87b2f3dd61e371eafb7ee8b2a56d70c2f 100644 --- a/ecmascript/snapshot/mem/snapshot_env.cpp +++ b/ecmascript/snapshot/mem/snapshot_env.cpp @@ -63,9 +63,18 @@ void SnapshotEnv::InitGlobalEnv() void SnapshotEnv::HandleObjectField(TaggedObject *objectHeader, CQueue *objectQueue, std::set *objectSet) { - auto visitor = [objectQueue, objectSet]([[maybe_unused]] TaggedObject *root, ObjectSlot start, ObjectSlot end, - [[maybe_unused]] bool isNative) { + auto visitor = [objectQueue, objectSet](TaggedObject *root, ObjectSlot start, ObjectSlot end, + VisitObjectArea area) { + auto hclass = root->GetClass(); + int index = 0; for (ObjectSlot slot = start; slot < end; slot++) { + if (area == VisitObjectArea::IN_OBJECT && !hclass->IsAllTaggedProp()) { + auto layout = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject()); + auto attr = layout->GetAttr(index++); + if (!attr.IsTaggedRep()) { + continue; + } + } auto fieldAddr = reinterpret_cast(slot.SlotAddress()); JSTaggedValue fieldValue(*fieldAddr); if (fieldValue.IsHeapObject() && !fieldValue.IsWeak() && !fieldValue.IsString() diff --git a/ecmascript/snapshot/mem/snapshot_processor.cpp b/ecmascript/snapshot/mem/snapshot_processor.cpp index e01541d890e6a1976a56458aa071774b8585d0c0..0d34d0ddff34de4b616ca84f0e4d511c6afcc07c 100644 --- a/ecmascript/snapshot/mem/snapshot_processor.cpp +++ b/ecmascript/snapshot/mem/snapshot_processor.cpp @@ -324,6 +324,7 @@ static uintptr_t g_nativeTable[] = { reinterpret_cast(Object::ProtoSetter), reinterpret_cast(Object::CreateRealm), reinterpret_cast(Object::Entries), + reinterpret_cast(Object::HasOwn), reinterpret_cast(Boolean::BooleanConstructor), reinterpret_cast(Boolean::BooleanPrototypeToString), reinterpret_cast(Boolean::BooleanPrototypeValueOf), @@ -334,6 +335,7 @@ static uintptr_t g_nativeTable[] = { reinterpret_cast(RegExp::GetFlags), reinterpret_cast(RegExp::GetSource), reinterpret_cast(RegExp::GetGlobal), + reinterpret_cast(RegExp::GetHasIndices), reinterpret_cast(RegExp::GetIgnoreCase), reinterpret_cast(RegExp::GetMultiline), reinterpret_cast(RegExp::GetDotAll), @@ -388,6 +390,8 @@ static uintptr_t g_nativeTable[] = { reinterpret_cast(BuiltinsArray::Filter), reinterpret_cast(BuiltinsArray::Find), reinterpret_cast(BuiltinsArray::FindIndex), + reinterpret_cast(BuiltinsArray::FindLast), + reinterpret_cast(BuiltinsArray::FindLastIndex), reinterpret_cast(BuiltinsArray::ForEach), reinterpret_cast(BuiltinsArray::IndexOf), reinterpret_cast(BuiltinsArray::Join), @@ -417,6 +421,10 @@ static uintptr_t g_nativeTable[] = { reinterpret_cast(BuiltinsArray::Flat), reinterpret_cast(BuiltinsArray::FlatMap), reinterpret_cast(BuiltinsArray::At), + reinterpret_cast(BuiltinsArray::ToReversed), + reinterpret_cast(BuiltinsArray::With), + reinterpret_cast(BuiltinsArray::ToSorted), + reinterpret_cast(BuiltinsArray::ToSpliced), reinterpret_cast(BuiltinsTypedArray::TypedArrayBaseConstructor), reinterpret_cast(BuiltinsTypedArray::CopyWithin), reinterpret_cast(BuiltinsTypedArray::Entries), @@ -425,6 +433,8 @@ static uintptr_t g_nativeTable[] = { reinterpret_cast(BuiltinsTypedArray::Filter), reinterpret_cast(BuiltinsTypedArray::Find), reinterpret_cast(BuiltinsTypedArray::FindIndex), + reinterpret_cast(BuiltinsTypedArray::FindLast), + reinterpret_cast(BuiltinsTypedArray::FindLastIndex), reinterpret_cast(BuiltinsTypedArray::ForEach), reinterpret_cast(BuiltinsTypedArray::IndexOf), reinterpret_cast(BuiltinsTypedArray::Join), @@ -438,14 +448,18 @@ static uintptr_t g_nativeTable[] = { reinterpret_cast(BuiltinsTypedArray::Slice), reinterpret_cast(BuiltinsTypedArray::Some), reinterpret_cast(BuiltinsTypedArray::Sort), + reinterpret_cast(BuiltinsTypedArray::ToSorted), reinterpret_cast(BuiltinsTypedArray::Subarray), reinterpret_cast(BuiltinsTypedArray::ToLocaleString), reinterpret_cast(BuiltinsTypedArray::Values), + reinterpret_cast(BuiltinsTypedArray::With), reinterpret_cast(BuiltinsTypedArray::GetBuffer), reinterpret_cast(BuiltinsTypedArray::GetByteLength), reinterpret_cast(BuiltinsTypedArray::GetByteOffset), reinterpret_cast(BuiltinsTypedArray::GetLength), reinterpret_cast(BuiltinsTypedArray::ToStringTag), + reinterpret_cast(BuiltinsTypedArray::At), + reinterpret_cast(BuiltinsTypedArray::ToReversed), reinterpret_cast(BuiltinsTypedArray::From), reinterpret_cast(BuiltinsTypedArray::Of), reinterpret_cast(BuiltinsTypedArray::Species), @@ -1058,6 +1072,7 @@ void SnapshotProcessor::WriteSpaceObjectToFile(Space* space, std::fstream &write auto lastRegion = space->GetCurrentRegion(); space->EnumerateRegions([&writer, lastRegion, alignedRegionObjSize](Region *current) { if (current != lastRegion) { + ASAN_UNPOISON_MEMORY_REGION(reinterpret_cast(ToUintPtr(current)), DEFAULT_REGION_SIZE); // fixme: Except for the last region of a space, // currently the snapshot feature assumes that every serialized region must have fixed size. // The original region size plus the aligned region object size should not exceed DEFAULT_REGION_SIZE. @@ -1439,13 +1454,17 @@ void SnapshotProcessor::SerializeObject(TaggedObject *objectHeader, CQueue(slot.SlotAddress()); SetObjectEncodeField(snapshotObj, slot.SlotAddress() - ToUintPtr(root), NativePointerToEncodeBit(nativePointer).GetValue()); } else { + if (VisitObjectBodyWithRep(root, slot, snapshotObj, index, area)) { + continue; + } auto fieldAddr = reinterpret_cast(slot.SlotAddress()); SetObjectEncodeField(snapshotObj, slot.SlotAddress() - ToUintPtr(root), SerializeTaggedField(fieldAddr, queue, data)); @@ -1456,6 +1475,32 @@ void SnapshotProcessor::SerializeObject(TaggedObject *objectHeader, CQueue(objectHeader, objectHeader->GetClass(), visitor); } +bool SnapshotProcessor::VisitObjectBodyWithRep(TaggedObject *root, ObjectSlot slot, uintptr_t obj, int index, + VisitObjectArea area) +{ + if (area != VisitObjectArea::IN_OBJECT) { + return false; + } + auto hclass = root->GetClass(); + if (hclass->IsAllTaggedProp()) { + return false; + } + auto layout = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject()); + auto attr = layout->GetAttr(index++); + if (attr.GetRepresentation() == Representation::DOUBLE) { + auto fieldAddr = reinterpret_cast(slot.SlotAddress()); + SetObjectEncodeField(obj, slot.SlotAddress() - ToUintPtr(root), + JSTaggedValue(*fieldAddr).GetRawData()); + return true; + } else if (attr.GetRepresentation() == Representation::INT) { + auto fieldAddr = reinterpret_cast(slot.SlotAddress()); + SetObjectEncodeField(obj, slot.SlotAddress() - ToUintPtr(root), + JSTaggedValue(static_cast(*fieldAddr)).GetRawData()); + return true; + } + return false; +} + void SnapshotProcessor::Relocate(SnapshotType type, const JSPandaFile *jsPandaFile, uint64_t rootObjSize) { size_t methodNums = 0; @@ -1633,10 +1678,10 @@ void SnapshotProcessor::DeserializeClassWord(TaggedObject *object) void SnapshotProcessor::DeserializeField(TaggedObject *objectHeader) { - auto visitor = [this]([[maybe_unused]] TaggedObject *root, ObjectSlot start, ObjectSlot end, bool isNative) { + auto visitor = [this]([[maybe_unused]] TaggedObject *root, ObjectSlot start, ObjectSlot end, VisitObjectArea area) { for (ObjectSlot slot = start; slot < end; slot++) { auto encodeBitAddr = reinterpret_cast(slot.SlotAddress()); - if (isNative) { + if (area == VisitObjectArea::NATIVE_POINTER) { DeserializeNativePointer(encodeBitAddr); } else { DeserializeTaggedField(encodeBitAddr, root); @@ -1748,6 +1793,27 @@ void SnapshotProcessor::SerializePandaFileMethod() } } +uintptr_t SnapshotProcessor::GetNewObj(size_t objectSize, TaggedObject *objectHeader) +{ + if (builtinsSerialize_) { + return AllocateObjectToLocalSpace(snapshotLocalSpace_, objectSize); + } + auto region = Region::ObjectAddressToRange(objectHeader); + if (region->InYoungOrOldSpace()) { + return AllocateObjectToLocalSpace(oldLocalSpace_, objectSize); + } + if (region->InMachineCodeSpace()) { + return AllocateObjectToLocalSpace(machineCodeLocalSpace_, objectSize); + } + if (region->InNonMovableSpace() || region->InReadOnlySpace()) { + return AllocateObjectToLocalSpace(nonMovableLocalSpace_, objectSize); + } + if (region->InHugeObjectSpace()) { + return AllocateObjectToLocalSpace(hugeObjectLocalSpace_, objectSize); + } + return AllocateObjectToLocalSpace(snapshotLocalSpace_, objectSize); +} + EncodeBit SnapshotProcessor::EncodeTaggedObject(TaggedObject *objectHeader, CQueue *queue, std::unordered_map *data) { @@ -1787,24 +1853,7 @@ EncodeBit SnapshotProcessor::EncodeTaggedObject(TaggedObject *objectHeader, CQue if (objectSize == 0) { LOG_ECMA_MEM(FATAL) << "It is a zero object. Not Support."; } - uintptr_t newObj = 0; - if (builtinsSerialize_) { - newObj = AllocateObjectToLocalSpace(snapshotLocalSpace_, objectSize); - } else { - auto region = Region::ObjectAddressToRange(objectHeader); - if (region->InYoungOrOldSpace()) { - newObj = AllocateObjectToLocalSpace(oldLocalSpace_, objectSize); - } else if (region->InMachineCodeSpace()) { - newObj = AllocateObjectToLocalSpace(machineCodeLocalSpace_, objectSize); - } else if (region->InNonMovableSpace() || region->InReadOnlySpace()) { - newObj = AllocateObjectToLocalSpace(nonMovableLocalSpace_, objectSize); - } else if (region->InHugeObjectSpace()) { - newObj = AllocateObjectToLocalSpace(hugeObjectLocalSpace_, objectSize); - } else { - newObj = AllocateObjectToLocalSpace(snapshotLocalSpace_, objectSize); - } - } - + uintptr_t newObj = GetNewObj(objectSize, objectHeader); if (newObj == 0) { LOG_ECMA_MEM(FATAL) << "Snapshot Allocate OOM"; } diff --git a/ecmascript/snapshot/mem/snapshot_processor.h b/ecmascript/snapshot/mem/snapshot_processor.h index b5f9a362f50ca2042351d736d92c86be9cbdd23a..5e50a4627d2669d533d7b819349877022ec2d6c2 100644 --- a/ecmascript/snapshot/mem/snapshot_processor.h +++ b/ecmascript/snapshot/mem/snapshot_processor.h @@ -58,6 +58,7 @@ public: void RelocateSpaceObject(const JSPandaFile *jsPandaFile, Space* space, SnapshotType type, MethodLiteral* methods, size_t methodNums, size_t rootObjSize); void SerializePandaFileMethod(); + uintptr_t GetNewObj(size_t objectSize, TaggedObject *objectHeader); EncodeBit EncodeTaggedObject(TaggedObject *objectHeader, CQueue *queue, std::unordered_map *data); EncodeBit GetObjectEncode(JSTaggedValue object, CQueue *queue, @@ -102,6 +103,7 @@ private: AlignUp(sizeof(Region), static_cast(MemAlignment::MEM_ALIGN_REGION))); } + bool VisitObjectBodyWithRep(TaggedObject *root, ObjectSlot slot, uintptr_t obj, int index, VisitObjectArea area); void SetObjectEncodeField(uintptr_t obj, size_t offset, uint64_t value); EncodeBit SerializeObjectHeader(TaggedObject *objectHeader, size_t objectType, CQueue *queue, diff --git a/ecmascript/snapshot/tests/snapshot_mock.h b/ecmascript/snapshot/tests/snapshot_mock.h index b3b2b2e9bb9cd4fdc7c6820e8e94373f7ca8b05b..331f01317276ed768c7b6ff1dff42b040eefb4df 100644 --- a/ecmascript/snapshot/tests/snapshot_mock.h +++ b/ecmascript/snapshot/tests/snapshot_mock.h @@ -33,18 +33,18 @@ public: return SnapShotHeader::ConvToStr(lastVersion_); } - void SetLastVersion(base::FileHeader::VersionType &lastVersion) + void SetLastVersion(base::FileHeaderBase::VersionType &lastVersion) { lastVersion_ = lastVersion; } - const base::FileHeader::VersionType &GetLastVersion() const override + const base::FileHeaderBase::VersionType &GetLastVersion() const override { return lastVersion_; } private: - base::FileHeader::VersionType lastVersion_ {}; + base::FileHeaderBase::VersionType lastVersion_ {}; }; } // namespace panda::ecmascript diff --git a/ecmascript/snapshot/tests/snapshot_test.cpp b/ecmascript/snapshot/tests/snapshot_test.cpp index 3d8e075882c689891a35524615e5829c9b001182..95db17d15dbd5fdfb30302bfb0c2aa6a90e39868 100644 --- a/ecmascript/snapshot/tests/snapshot_test.cpp +++ b/ecmascript/snapshot/tests/snapshot_test.cpp @@ -60,8 +60,8 @@ public: JSNApi::DestroyJSVM(ecmaVm); } - void CompatibilityHelper(base::FileHeader::VersionType serializeVersion, - base::FileHeader::VersionType deserializeVersion, bool expected) + void CompatibilityHelper(base::FileHeaderBase::VersionType serializeVersion, + base::FileHeaderBase::VersionType deserializeVersion, bool expected) { static constexpr uint32_t ARRAY_SIZE = 300; static constexpr uint32_t KILO_BITS = 1024; @@ -312,30 +312,30 @@ HWTEST_F_L0(SnapshotTest, SerializeHugeObject) HWTEST_F_L0(SnapshotTest, BackwardCompatibility) { - base::FileHeader::VersionType oldVersion = {0, 0, 0, 1}; - base::FileHeader::VersionType newVersion = {4, 0, 0, 1}; + base::FileHeaderBase::VersionType oldVersion = {0, 0, 0, 1}; + base::FileHeaderBase::VersionType newVersion = {4, 0, 0, 1}; CompatibilityHelper(oldVersion, newVersion, false); } HWTEST_F_L0(SnapshotTest, ForwardCompatibility) { - base::FileHeader::VersionType oldVersion = {0, 0, 0, 1}; - base::FileHeader::VersionType newVersion = {4, 0, 0, 1}; + base::FileHeaderBase::VersionType oldVersion = {0, 0, 0, 1}; + base::FileHeaderBase::VersionType newVersion = {4, 0, 0, 1}; CompatibilityHelper(newVersion, oldVersion, false); } HWTEST_F_L0(SnapshotTest, StrictCompatibility) { - base::FileHeader::VersionType newVersion = {4, 0, 0, 1}; + base::FileHeaderBase::VersionType newVersion = {4, 0, 0, 1}; CompatibilityHelper(newVersion, newVersion, true); } HWTEST_F_L0(SnapshotTest, VersionTest) { - base::FileHeader::VersionType version = {4, 3, 2, 1}; + base::FileHeaderBase::VersionType version = {4, 3, 2, 1}; uint32_t versionNumber = 0x04030201U; - EXPECT_EQ(version, base::FileHeader::ToVersion(versionNumber)); - EXPECT_EQ(versionNumber, base::FileHeader::ToVersionNumber(version)); + EXPECT_EQ(version, base::FileHeaderBase::ToVersion(versionNumber)); + EXPECT_EQ(versionNumber, base::FileHeaderBase::ToVersionNumber(version)); } } // namespace panda::test diff --git a/ecmascript/stackmap/ark_stackmap_parser.cpp b/ecmascript/stackmap/ark_stackmap_parser.cpp index a9e03d7e375666d7377f9c5c467fe323283f9777..7adad46df785a3f7ad3997687f51cf64a4dcd43b 100644 --- a/ecmascript/stackmap/ark_stackmap_parser.cpp +++ b/ecmascript/stackmap/ark_stackmap_parser.cpp @@ -91,6 +91,35 @@ void ArkStackMapParser::GetConstInfo(uintptr_t callSiteAddr, LLVMStackMapType::C info.emplace_back(v); } +void ArkStackMapParser::GetMethodOffsetInfo(uintptr_t callSiteAddr, std::map& info, + uint8_t *stackmapAddr) const +{ + std::vector deopts; + GetArkDeopt(callSiteAddr, stackmapAddr, deopts); + if (deopts.empty()) { + return; + } + + ARKDeopt target; + size_t shift = Deoptimizier::ComputeShift(MAX_METHOD_OFFSET_NUM); + LLVMStackMapType::VRegId startId = static_cast(SpecVregIndex::FIRST_METHOD_OFFSET_INDEX); + for (int i = MAX_METHOD_OFFSET_NUM - 1; i >= 0; i--) { + LLVMStackMapType::VRegId id = startId - i; + target.id = Deoptimizier::EncodeDeoptVregIndex(id, i, shift); + auto it = std::lower_bound(deopts.begin(), deopts.end(), target, + [](const ARKDeopt& a, const ARKDeopt& b) { + return a.id < b.id; + }); + if (it == deopts.end() || (it->id > target.id)) { + continue; + } + ASSERT(it->kind == LocationTy::Kind::CONSTANT); + ASSERT(std::holds_alternative(it->value)); + auto v = std::get(it->value); + info[static_cast(SpecVregIndex::FIRST_METHOD_OFFSET_INDEX) - id] = static_cast(v); + } +} + uintptr_t ArkStackMapParser::GetStackSlotAddress(const LLVMStackMapType::DwarfRegAndOffsetType info, uintptr_t callSiteSp, uintptr_t callsiteFp) const { diff --git a/ecmascript/stackmap/ark_stackmap_parser.h b/ecmascript/stackmap/ark_stackmap_parser.h index b5d2d40ca1ee17cd6a512570a15890ec540ff52d..142155eb759e4bb92751c20b6fa6920506cc510f 100644 --- a/ecmascript/stackmap/ark_stackmap_parser.h +++ b/ecmascript/stackmap/ark_stackmap_parser.h @@ -38,6 +38,8 @@ public: enableLog_ = false; } void GetConstInfo(uintptr_t callsite, LLVMStackMapType::ConstInfo& info, uint8_t *stackmapAddr = nullptr) const; + void GetMethodOffsetInfo(uintptr_t callSiteAddr, std::map& info, + uint8_t *stackmapAddr) const; bool IteratorStackMap(const RootVisitor& visitor, const RootBaseAndDerivedVisitor& derivedVisitor, uintptr_t callSiteAddr, uintptr_t callsiteFp, diff --git a/ecmascript/stubs/runtime_stubs-inl.h b/ecmascript/stubs/runtime_stubs-inl.h index b4e2402e711b9d971dbf472b63755284b7b1ce1f..21c8ea2a3aa4e51148799794b5085e2d7a71a93d 100644 --- a/ecmascript/stubs/runtime_stubs-inl.h +++ b/ecmascript/stubs/runtime_stubs-inl.h @@ -18,6 +18,7 @@ #include "ecmascript/stubs/runtime_stubs.h" +#include "ecmascript/base/array_helper.h" #include "ecmascript/builtins/builtins_regexp.h" #include "ecmascript/compiler/aot_file/aot_file_manager.h" #include "ecmascript/ecma_string_table.h" @@ -38,11 +39,13 @@ #include "ecmascript/jspandafile/scope_info_extractor.h" #include "ecmascript/module/js_module_manager.h" #include "ecmascript/module/js_module_source_text.h" +#include "ecmascript/platform/file.h" #include "ecmascript/stackmap/llvm_stackmap_parser.h" #include "ecmascript/template_string.h" #include "ecmascript/ts_types/ts_manager.h" namespace panda::ecmascript { +using ArrayHelper = base::ArrayHelper; JSTaggedValue RuntimeStubs::RuntimeInc(JSThread *thread, const JSHandle &value) { JSHandle inputVal = JSTaggedValue::ToNumeric(thread, value); @@ -129,7 +132,7 @@ JSTaggedValue RuntimeStubs::RuntimeInstanceofByHandler(JSThread *thread, JSHandl JSHandle instOfHandler) { // 3. ReturnIfAbrupt(instOfHandler). - RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue(false)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 4. If instOfHandler is not undefined, then if (!instOfHandler->IsUndefined()) { // a. Return ! ToBoolean(? Call(instOfHandler, target, «object»)). @@ -141,20 +144,22 @@ JSTaggedValue RuntimeStubs::RuntimeInstanceofByHandler(JSThread *thread, JSHandl JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, instOfHandler, target, undefined, 1); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); info->SetCallArg(object.GetTaggedValue()); JSTaggedValue tagged = JSFunction::Call(info); - RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue(false)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return tagged; } } // 5. If IsCallable(target) is false, throw a TypeError exception. if (!target->IsCallable()) { - THROW_TYPE_ERROR_AND_RETURN(thread, "InstanceOf error when target is not Callable", JSTaggedValue(false)); + THROW_TYPE_ERROR_AND_RETURN(thread, "InstanceOf error when target is not Callable", JSTaggedValue::Exception()); } // fastpath // 6. Return ? OrdinaryHasInstance(target, object). bool res = JSFunction::OrdinaryHasInstance(thread, target, object); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return JSTaggedValue(res); } @@ -236,10 +241,11 @@ JSTaggedValue RuntimeStubs::RuntimeSuperCallSpread(JSThread *thread, const JSHan const JSHandle &array) { JSHandle superFunc(thread, JSTaggedValue::GetPrototype(thread, func)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); ASSERT(superFunc->IsJSFunction()); JSHandle argv(thread, RuntimeGetCallSpreadArgs(thread, array)); - const int32_t argsLength = static_cast(argv->GetLength()); + const uint32_t argsLength = argv->GetLength(); JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, superFunc, undefined, newTarget, argsLength); @@ -275,6 +281,7 @@ JSTaggedValue RuntimeStubs::RuntimeNewObjApply(JSThread *thread, const JSHandle< JSHandle argsArray = factory->NewTaggedArray(length); for (uint32_t i = 0; i < length; ++i) { auto prop = JSTaggedValue::GetProperty(thread, array, i).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); argsArray->Set(thread, i, prop); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); } @@ -359,6 +366,7 @@ JSTaggedValue RuntimeStubs::RuntimeAsyncGeneratorResolve(JSThread *thread, JSHan ASSERT(flag.IsBoolean()); bool done = flag.IsTrue(); + return JSAsyncGeneratorObject::AsyncGeneratorResolve(thread, asyncGeneratorObjHandle, valueHandle, done); } @@ -379,6 +387,7 @@ JSTaggedValue RuntimeStubs::RuntimeCopyDataProperties(JSThread *thread, const JS if (!src->IsNull() && !src->IsUndefined()) { // 2. Let from be ! ToObject(source). JSHandle from(JSTaggedValue::ToObject(thread, src)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle keys = JSTaggedValue::GetOwnPropertyKeys(thread, from); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -403,7 +412,14 @@ JSTaggedValue RuntimeStubs::RuntimeStArraySpread(JSThread *thread, const JSHandl JSTaggedValue index, const JSHandle &src) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - ASSERT(dst->IsJSArray() && !src->IsNull() && !src->IsUndefined()); + ASSERT(dst->IsJSArray()); + if (dst->IsJSArray()) { + if (src->IsNull() || src->IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "src is not iterable", JSTaggedValue::Exception()); + } + } else { + THROW_TYPE_ERROR_AND_RETURN(thread, "dst is not iterable", JSTaggedValue::Exception()); + } if (src->IsString()) { JSHandle srcString = JSTaggedValue::ToString(thread, src); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -420,6 +436,17 @@ JSTaggedValue RuntimeStubs::RuntimeStArraySpread(JSThread *thread, const JSHandl return JSTaggedValue(dstLen + strLen); } + if (index.GetInt() == 0 && src->IsStableJSArray(thread)) { + JSHandle srcElements(thread, JSHandle::Cast(src)->GetElements()); + uint32_t length = JSHandle::Cast(src)->GetArrayLength(); + JSHandle dstElements = factory->NewTaggedArray(length); + JSHandle dstArray = JSHandle::Cast(dst); + dstArray->SetElements(thread, dstElements); + dstArray->SetArrayLength(thread, length); + TaggedArray::CopyTaggedArrayElement(thread, srcElements, dstElements, length); + return JSTaggedValue(length); + } + JSHandle iter; auto globalConst = thread->GlobalConstants(); if (src->IsJSArrayIterator() || src->IsJSMapIterator() || src->IsJSSetIterator() || @@ -428,6 +455,7 @@ JSTaggedValue RuntimeStubs::RuntimeStArraySpread(JSThread *thread, const JSHandl } else if (src->IsJSArray()) { JSHandle valuesStr = globalConst->GetHandledValuesString(); JSHandle valuesMethod = JSObject::GetMethod(thread, src, valuesStr); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); iter = JSIterator::GetIterator(thread, src, valuesMethod); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); } else { @@ -448,6 +476,7 @@ JSTaggedValue RuntimeStubs::RuntimeStArraySpread(JSThread *thread, const JSHandl bool success = JSTaggedValue::GetOwnProperty(thread, iterResult, valueStr, desc); if (success && desc.IsEnumerable()) { JSTaggedValue::DefineOwnProperty(thread, dst, indexHandle, desc); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); int tmp = indexHandle->GetInt(); indexHandle.Update(JSTaggedValue(tmp + 1)); } @@ -517,7 +546,7 @@ JSTaggedValue RuntimeStubs::RuntimeStObjByValue(JSThread *thread, const JSHandle RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle propKey(JSTaggedValue::ToPropertyKey(thread, prop)); - + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // strict mode is true JSTaggedValue::SetProperty(thread, object, propKey, value, true); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -540,7 +569,9 @@ JSTaggedValue RuntimeStubs::RuntimeStOwnByValue(JSThread *thread, const JSHandle PropertyDescriptor desc(thread, value, true, enumerable, true); JSHandle propKey = JSTaggedValue::ToPropertyKey(thread, key); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); bool ret = JSTaggedValue::DefineOwnProperty(thread, obj, propKey, desc); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); if (!ret) { return RuntimeThrowTypeError(thread, "StOwnByValue failed"); } @@ -559,6 +590,7 @@ JSTaggedValue RuntimeStubs::RuntimeLdSuperByValue(JSThread *thread, const JSHand } JSHandle propKey(JSTaggedValue::ToPropertyKey(thread, key)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle superBase(thread, JSTaggedValue::GetSuperBase(thread, homeObject)); JSTaggedValue::RequireObjectCoercible(thread, superBase); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -581,6 +613,7 @@ JSTaggedValue RuntimeStubs::RuntimeStSuperByValue(JSThread *thread, const JSHand } JSHandle propKey(JSTaggedValue::ToPropertyKey(thread, key)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle superBase(thread, JSTaggedValue::GetSuperBase(thread, homeObject)); JSTaggedValue::RequireObjectCoercible(thread, superBase); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -645,6 +678,7 @@ JSTaggedValue RuntimeStubs::RuntimeStOwnByIndex(JSThread *thread, const JSHandle PropertyDescriptor desc(thread, value, true, enumerable, true); bool ret = JSTaggedValue::DefineOwnProperty(thread, obj, idx, desc); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); if (!ret) { return RuntimeThrowTypeError(thread, "SetOwnByIndex failed"); } @@ -727,8 +761,8 @@ JSTaggedValue RuntimeStubs::RuntimeResolveClass(JSThread *thread, const JSHandle ASSERT(ctor.GetTaggedValue().IsClassConstructor()); FrameHandler frameHandler(thread); - JSTaggedValue currentFunc = frameHandler.GetFunction(); - JSHandle ecmaModule(thread, JSFunction::Cast(currentFunc.GetTaggedObject())->GetModule()); + Method *currentMethod = frameHandler.GetMethod(); + JSHandle ecmaModule(thread, currentMethod->GetModule()); RuntimeSetClassInheritanceRelationship(thread, JSHandle(ctor), base); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -741,7 +775,7 @@ JSTaggedValue RuntimeStubs::RuntimeResolveClass(JSThread *thread, const JSHandle if (LIKELY(value.IsJSFunction())) { JSFunction *func = JSFunction::Cast(value.GetTaggedObject()); func->SetLexicalEnv(thread, lexenv.GetTaggedValue()); - func->SetModule(thread, ecmaModule); + Method::Cast(func->GetMethod())->SetModule(thread, ecmaModule); } } @@ -797,57 +831,39 @@ JSTaggedValue RuntimeStubs::RuntimeCreateClassWithBuffer(JSThread *thread, CString entry = ModuleManager::GetRecordName(module.GetTaggedValue()); // For class constructor. - auto methodObj = ConstantPool::GetMethodFromCache(thread, constpool.GetTaggedValue(), methodId); + auto methodObj = ConstantPool::GetMethodFromCache( + thread, constpool.GetTaggedValue(), module.GetTaggedValue(), methodId); JSHandle method(thread, methodObj); JSHandle constpoolHandle = JSHandle::Cast(constpool); + JSHandle cls; + JSMutableHandle ihc(thread, JSTaggedValue::Undefined()); + JSMutableHandle chc(thread, JSTaggedValue::Undefined()); + + JSTaggedValue val = constpoolHandle->GetObjectFromCache(literalId); + if (val.IsAOTLiteralInfo()) { + JSHandle aotLiteralInfo(thread, val); + ihc.Update(aotLiteralInfo->GetIhc()); + chc.Update(aotLiteralInfo->GetChc()); + } auto literalObj = ConstantPool::GetClassLiteralFromCache(thread, constpoolHandle, literalId, entry); JSHandle classLiteral(thread, literalObj); JSHandle arrayHandle(thread, classLiteral->GetArray()); JSHandle extractor = factory->NewClassInfoExtractor(method); - ClassInfoExtractor::BuildClassInfoExtractorFromLiteral(thread, extractor, arrayHandle); - JSHandle cls = ClassHelper::DefineClassFromExtractor(thread, base, extractor, lexenv); - - RuntimeSetClassInheritanceRelationship(thread, JSHandle(cls), base); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - return cls.GetTaggedValue(); -} -JSTaggedValue RuntimeStubs::RuntimeCreateClassWithIHClass(JSThread *thread, - const JSHandle &base, - const JSHandle &lexenv, - const JSHandle &constpool, - const uint16_t methodId, uint16_t literalId, - const JSHandle &ihclass, - const JSHandle &constructorHClass, - const JSHandle &module) -{ - [[maybe_unused]] EcmaHandleScope handleScope(thread); - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - CString entry = ModuleManager::GetRecordName(module.GetTaggedValue()); - - // For class constructor. - auto methodObj = ConstantPool::GetMethodFromCache(thread, constpool.GetTaggedValue(), methodId); - JSHandle method(thread, methodObj); - JSHandle constpoolHandle = JSHandle::Cast(constpool); - auto literalObj = ConstantPool::GetClassLiteralFromCache(thread, constpoolHandle, literalId, entry); - JSHandle classLiteral(thread, literalObj); - if (classLiteral->GetIsAOTUsed()) { - // the prototype of IHClass can only use once - return RuntimeCreateClassWithBuffer(thread, base, lexenv, constpool, methodId, literalId, module); + if ((ihc->IsUndefined() && chc->IsUndefined()) || + (classLiteral->GetIsAOTUsed())) { + cls = ClassHelper::DefineClassFromExtractor(thread, base, extractor, lexenv); } else { classLiteral->SetIsAOTUsed(true); + JSHandle ihclass(ihc); + JSHandle chclass(chc); + cls = ClassHelper::DefineClassWithIHClass(thread, extractor, + lexenv, ihclass, chclass); } - JSHandle arrayHandle(thread, classLiteral->GetArray()); - JSHandle extractor = factory->NewClassInfoExtractor(method); - - ClassInfoExtractor::BuildClassInfoExtractorFromLiteral(thread, extractor, arrayHandle); - JSHandle cls = ClassHelper::DefineClassWithIHClass(thread, base, extractor, - lexenv, ihclass, constructorHClass); RuntimeSetClassInheritanceRelationship(thread, JSHandle(cls), base); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - return cls.GetTaggedValue(); } @@ -891,6 +907,7 @@ JSTaggedValue RuntimeStubs::RuntimeSetClassInheritanceRelationship(JSThread *thr method->SetFunctionKind(FunctionKind::DERIVED_CONSTRUCTOR); parentPrototype = JSTaggedValue::GetProperty(thread, parent, globalConst->GetHandledPrototypeString()).GetValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); if (!parentPrototype->IsECMAObject() && !parentPrototype->IsNull()) { return RuntimeThrowTypeError(thread, "parent class have no valid prototype"); } @@ -965,7 +982,9 @@ JSTaggedValue RuntimeStubs::RuntimeStOwnByValueWithNameSet(JSThread *thread, con PropertyDescriptor desc(thread, value, true, enumerable, true); JSHandle propKey = JSTaggedValue::ToPropertyKey(thread, key); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); bool ret = JSTaggedValue::DefineOwnProperty(thread, obj, propKey, desc); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); if (!ret) { return RuntimeThrowTypeError(thread, "StOwnByValueWithNameSet failed"); } @@ -990,6 +1009,7 @@ JSTaggedValue RuntimeStubs::RuntimeStOwnByName(JSThread *thread, const JSHandle< PropertyDescriptor desc(thread, value, true, enumerable, true); bool ret = JSTaggedValue::DefineOwnProperty(thread, obj, prop, desc); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); if (!ret) { return RuntimeThrowTypeError(thread, "SetOwnByName failed"); } @@ -1003,12 +1023,13 @@ JSTaggedValue RuntimeStubs::RuntimeStOwnByNameWithNameSet(JSThread *thread, { ASSERT(propHandle->IsStringOrSymbol()); JSHandle propKey = JSTaggedValue::ToPropertyKey(thread, propHandle); - + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // property in class is non-enumerable bool enumerable = !(objHandle->IsClassPrototype() || objHandle->IsClassConstructor()); PropertyDescriptor desc(thread, valueHandle, true, enumerable, true); bool ret = JSTaggedValue::DefineOwnProperty(thread, objHandle, propHandle, desc); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); if (!ret) { return RuntimeThrowTypeError(thread, "SetOwnByNameWithNameSet failed"); } @@ -1219,6 +1240,7 @@ JSTaggedValue RuntimeStubs::RuntimeGetIterator(JSThread *thread, const JSHandle< JSTaggedValue RuntimeStubs::RuntimeGetAsyncIterator(JSThread *thread, const JSHandle &obj) { JSHandle asyncit = JSIterator::GetAsyncIterator(thread, obj); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); return asyncit.GetTaggedValue(); } @@ -1287,6 +1309,7 @@ JSTaggedValue RuntimeStubs::RuntimeTryLdGlobalByName(JSThread *thread, const JSH const JSHandle &prop) { OperationResult res = JSTaggedValue::GetProperty(thread, obj, prop); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); if (!res.GetPropertyMetaData().IsFound()) { return RuntimeThrowReferenceError(thread, prop, " is not defined"); } @@ -1366,43 +1389,29 @@ JSTaggedValue RuntimeStubs::RuntimeDynamicImport(JSThread *thread, const JSHandl // get current filename Method *method = JSFunction::Cast(func->GetTaggedObject())->GetCallTarget(); const JSPandaFile *jsPandaFile = method->GetJSPandaFile(); - CString filename = jsPandaFile->GetJSPandaFileDesc(); + CString currentfilename = jsPandaFile->GetJSPandaFileDesc(); - // parse dirPath from filename - JSHandle dirPath; + JSMutableHandle dirPath(thread, thread->GlobalConstants()->GetUndefined()); JSMutableHandle recordName(thread, thread->GlobalConstants()->GetUndefined()); if (jsPandaFile->IsBundlePack()) { - int foundPos = static_cast(filename.find_last_of("/\\")); - if (foundPos == -1) { - RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Hole()); - } - CString dirPathStr = filename.substr(0, foundPos + 1); - dirPath = factory->NewFromUtf8(dirPathStr); + dirPath.Update(factory->NewFromUtf8(currentfilename).GetTaggedValue()); } else { - JSHandle module(thread, JSFunction::Cast(func->GetTaggedObject())->GetModule()); - dirPath = factory->NewFromUtf8(filename.c_str()); - if (module->IsSourceTextModule()) { - recordName.Update(SourceTextModule::Cast(module->GetTaggedObject())->GetEcmaModuleRecordName()); - } else if (module->IsString()) { - recordName.Update(module); - } else { - LOG_INTERPRETER(FATAL) << "module is undefined"; - UNREACHABLE(); - } + recordName.Update(method->GetRecordName()); + dirPath.Update(factory->NewFromUtf8(currentfilename).GetTaggedValue()); } // 4. Let promiseCapability be !NewPromiseCapability(%Promise%). JSHandle promiseFunc = env->GetPromiseFunction(); JSHandle promiseCapability = JSPromise::NewPromiseCapability(thread, promiseFunc); - + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle job = thread->GetCurrentEcmaContext()->GetMicroJobQueue(); JSHandle argv = factory->NewTaggedArray(5); // 5: 5 means parameters stored in array argv->Set(thread, 0, promiseCapability->GetResolve()); - argv->Set(thread, 1, promiseCapability->GetReject()); // 1 : first argument - argv->Set(thread, 2, dirPath); // 2: second argument - argv->Set(thread, 3, specifier); // 3 : third argument - argv->Set(thread, 4, recordName); // 4 : fourth argument + argv->Set(thread, 1, promiseCapability->GetReject()); // 1 : reject method + argv->Set(thread, 2, dirPath); // 2 : current file path(containing file name) + argv->Set(thread, 3, specifier); // 3 : request module's path + argv->Set(thread, 4, recordName); // 4 : js recordName or undefined JSHandle dynamicImportJob(env->GetDynamicImportJob()); job::MicroJobQueue::EnqueueJob(thread, job, job::QueueType::QUEUE_PROMISE, dynamicImportJob, argv); @@ -1463,8 +1472,10 @@ JSTaggedValue RuntimeStubs::RuntimeAdd2(JSThread *thread, const JSHandle &right) { if (left->IsString() && right->IsString()) { - return JSTaggedValue(EcmaStringAccessor::Concat( - thread->GetEcmaVM(), JSHandle(left), JSHandle(right))); + EcmaString *resultStr = EcmaStringAccessor::Concat( + thread->GetEcmaVM(), JSHandle(left), JSHandle(right)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue(resultStr); } JSHandle primitiveA0(thread, JSTaggedValue::ToPrimitive(thread, left)); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -1476,7 +1487,9 @@ JSTaggedValue RuntimeStubs::RuntimeAdd2(JSThread *thread, const JSHandle stringA1 = JSTaggedValue::ToString(thread, primitiveA1); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - return JSTaggedValue(EcmaStringAccessor::Concat(thread->GetEcmaVM(), stringA0, stringA1)); + EcmaString *resultStr = EcmaStringAccessor::Concat(thread->GetEcmaVM(), stringA0, stringA1); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + return JSTaggedValue(resultStr); } JSHandle valLeft = JSTaggedValue::ToNumeric(thread, primitiveA0); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -1907,7 +1920,7 @@ JSTaggedValue RuntimeStubs::CommonCreateObjectWithExcludedKeys(JSThread *thread, return restObj.GetTaggedValue(); } JSHandle obj(JSTaggedValue::ToObject(thread, objVal)); - + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSHandle allKeys = JSObject::GetOwnPropertyKeys(thread, obj); uint32_t numAllKeys = allKeys->GetLength(); JSMutableHandle key(thread, JSTaggedValue::Undefined()); @@ -1973,7 +1986,15 @@ JSTaggedValue RuntimeStubs::RuntimeCreateObjectWithExcludedKeys(JSThread *thread excludedKeys->Set(thread, i, excludedKey); } } - return CommonCreateObjectWithExcludedKeys(thread, objVal, numExcludedKeys, excludedKeys); + + JSHandle finalVal = objVal; + if (finalVal->CheckIsJSProxy()) { + JSHandle proxyVal(thread, finalVal.GetTaggedValue()); + + finalVal = proxyVal->GetSourceTarget(thread); + } + + return CommonCreateObjectWithExcludedKeys(thread, finalVal, numExcludedKeys, excludedKeys); } JSTaggedValue RuntimeStubs::RuntimeDefineMethod(JSThread *thread, const JSHandle &methodHandle, @@ -1997,6 +2018,7 @@ JSTaggedValue RuntimeStubs::RuntimeCallSpread(JSThread *thread, uint32_t length = coretypesArray->GetLength(); JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, obj, undefined, length); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); info->SetCallArg(length, coretypesArray); return EcmaInterpreter::Execute(info); } @@ -2007,7 +2029,7 @@ JSTaggedValue RuntimeStubs::RuntimeDefineGetterSetterByValue(JSThread *thread, c const JSHandle &setter, bool flag) { JSHandle propKey = JSTaggedValue::ToPropertyKey(thread, prop); - + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); auto globalConst = thread->GlobalConstants(); if (obj.GetTaggedValue().IsClassConstructor() && JSTaggedValue::SameValue(propKey, globalConst->GetHandledPrototypeString())) { @@ -2060,7 +2082,7 @@ JSTaggedValue RuntimeStubs::RuntimeSuperCall(JSThread *thread, const JSHandle superFunc(thread, JSTaggedValue::GetPrototype(thread, func)); - ASSERT(superFunc->IsJSFunction()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle argv = factory->NewTaggedArray(length); @@ -2085,7 +2107,7 @@ JSTaggedValue RuntimeStubs::RuntimeOptSuperCall(JSThread *thread, uintptr_t argv JSHandle func = GetHArg(argv, argc, 0); JSHandle newTarget = GetHArg(argv, argc, 1); JSHandle superFunc(thread, JSTaggedValue::GetPrototype(thread, func)); - ASSERT(superFunc->IsJSFunction()); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); uint16_t length = argc - fixNums; JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, superFunc, undefined, newTarget, length); @@ -2113,6 +2135,14 @@ JSTaggedValue RuntimeStubs::RuntimeGetCallSpreadArgs(JSThread *thread, const JSH ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle argv = factory->NewTaggedArray(argvMayMaxLength); JSHandle itor = JSIterator::GetIterator(thread, jsArray); + + // Fast path when array is stablearray and Iterator not change. + if (jsArray->IsStableJSArray(thread) && itor->IsJSArrayIterator()) { + JSHandle srcElements(thread, JSHandle::Cast(jsArray)->GetElements()); + TaggedArray::CopyTaggedArrayElement(thread, srcElements, argv, argvMayMaxLength); + return argv.GetTaggedValue(); + } + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); JSMutableHandle next(thread, JSTaggedValue::Undefined()); JSMutableHandle nextArg(thread, JSTaggedValue::Undefined()); @@ -2361,7 +2391,7 @@ JSTaggedValue RuntimeStubs::RuntimeOptConstructProxy(JSThread *thread, JSHandle< } // step 8 ~ 9 Call(trap, handler, «target, argArray, newTarget »). - const int32_t argsLength = 3; // 3: «target, argArray, newTarget » + const uint32_t argsLength = 3; // 3: «target, argArray, newTarget » JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, method, handler, undefined, argsLength); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); @@ -2400,6 +2430,37 @@ JSTaggedValue RuntimeStubs::RuntimeOptConstructBoundFunction(JSThread *thread, J return RuntimeOptConstruct(thread, target, newTargetMutable, newPreArgs, args); } +JSTaggedValue RuntimeStubs::GetResultValue(JSThread *thread, bool isAotMethod, JSHandle ctor, + CVector &values, JSHandle newTgt, uint32_t &size, JSHandle obj) +{ + JSTaggedValue resultValue; + if (isAotMethod && ctor->IsClassConstructor()) { + uint32_t numArgs = ctor->GetCallTarget()->GetNumArgsWithCallField(); + bool needPushUndefined = numArgs > size; + const JSTaggedType *prevFp = thread->GetLastLeaveFrame(); + if (ctor->GetCallTarget()->IsFastCall()) { + if (needPushUndefined) { + values.reserve(numArgs + NUM_MANDATORY_JSFUNC_ARGS - 1); + for (uint32_t i = size; i < numArgs; i++) { + values.emplace_back(JSTaggedValue::VALUE_UNDEFINED); + } + size = numArgs; + } + resultValue = thread->GetEcmaVM()->FastCallAot(size, values.data(), prevFp); + } else { + resultValue = thread->GetCurrentEcmaContext()->ExecuteAot(size, values.data(), prevFp, needPushUndefined); + } + } else { + ctor->GetCallTarget()->SetAotCodeBit(false); // if Construct is not ClassConstructor, don't run aot + EcmaRuntimeCallInfo *info = + EcmaInterpreter::NewRuntimeCallInfo(thread, JSHandle(ctor), obj, newTgt, size); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + info->SetCallArg(size, values.data()); + resultValue = EcmaInterpreter::Execute(info); + } + return resultValue; +} + JSTaggedValue RuntimeStubs::RuntimeOptConstructGeneric(JSThread *thread, JSHandle ctor, JSHandle newTgt, JSHandle preArgs, JSHandle args) @@ -2449,31 +2510,7 @@ JSTaggedValue RuntimeStubs::RuntimeOptConstructGeneric(JSThread *thread, JSHandl values.emplace_back(args->Get(i).GetRawData()); } } - JSTaggedValue resultValue; - if (isAotMethod && ctor->IsClassConstructor()) { - uint32_t numArgs = ctor->GetCallTarget()->GetNumArgsWithCallField(); - bool needPushUndefined = numArgs > size; - const JSTaggedType *prevFp = thread->GetLastLeaveFrame(); - if (ctor->GetCallTarget()->IsFastCall()) { - if (needPushUndefined) { - values.reserve(numArgs + NUM_MANDATORY_JSFUNC_ARGS - 1); - for (uint32_t i = size; i < numArgs; i++) { - values.emplace_back(JSTaggedValue::VALUE_UNDEFINED); - } - size = numArgs; - } - resultValue = thread->GetEcmaVM()->FastCallAot(size, values.data(), prevFp); - } else { - resultValue = thread->GetCurrentEcmaContext()->ExecuteAot(size, values.data(), prevFp, needPushUndefined); - } - } else { - ctor->GetCallTarget()->SetAotCodeBit(false); // if Construct is not ClassConstructor, don't run aot - EcmaRuntimeCallInfo *info = - EcmaInterpreter::NewRuntimeCallInfo(thread, JSHandle(ctor), obj, newTgt, size); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - info->SetCallArg(size, values.data()); - resultValue = EcmaInterpreter::Execute(info); - } + JSTaggedValue resultValue = RuntimeStubs::GetResultValue(thread, isAotMethod, ctor, values, newTgt, size, obj); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 9.3.2 [[Construct]] (argumentsList, newTarget) if (resultValue.IsECMAObject()) { @@ -2629,5 +2666,79 @@ JSTaggedValue RuntimeStubs::RuntimeNotifyDebuggerStatement(JSThread *thread) } return JSTaggedValue::Hole(); } + +bool RuntimeStubs::CheckElementsNumber(JSHandle elements, uint32_t len) +{ + ASSERT(len <= elements->GetLength()); + for (uint32_t i = 0; i < len; i++) { + if (!elements->Get(i).IsNumber()) { + return false; + } + } + return true; +} + +JSHandle RuntimeStubs::GetOrCreateNumberString(JSThread *thread, JSHandle presentValue, + std::map> &cachedString) +{ + JSMutableHandle presentString(thread, JSTaggedValue::Undefined()); + auto iter = cachedString.find(presentValue->GetRawData()); + if (iter != cachedString.end()) { + presentString.Update(iter->second); + } else { + presentString.Update(JSTaggedValue::ToString(thread, presentValue).GetTaggedValue()); + cachedString[presentValue->GetRawData()] = presentString; + } + return presentString; +} + +JSTaggedValue RuntimeStubs::TryCopyCOWArray(JSThread *thread, JSHandle holderHandler, bool &isCOWArray) +{ + if (isCOWArray) { + JSArray::CheckAndCopyArray(thread, holderHandler); + isCOWArray = false; + } + return holderHandler->GetElements(); +} + +JSTaggedValue RuntimeStubs::ArrayNumberSort(JSThread *thread, JSHandle thisObj, uint32_t len) +{ + JSMutableHandle presentValue(thread, JSTaggedValue::Undefined()); + JSMutableHandle middleValue(thread, JSTaggedValue::Undefined()); + JSMutableHandle previousValue(thread, JSTaggedValue::Undefined()); + bool isCOWArray = JSHandle(thisObj)->IsJSCOWArray(); + JSMutableHandle elements(thread, thisObj->GetElements()); + std::map> cachedString; + for (uint32_t i = 1; i < len; i++) { + uint32_t beginIndex = 0; + uint32_t endIndex = i; + presentValue.Update(elements->Get(i)); + JSHandle presentString = GetOrCreateNumberString(thread, presentValue, cachedString); + while (beginIndex < endIndex) { + uint32_t middleIndex = beginIndex + (endIndex - beginIndex) / 2; // 2 : half + middleValue.Update(elements->Get(middleIndex)); + JSHandle middleString = GetOrCreateNumberString(thread, middleValue, cachedString); + double compareResult = ArrayHelper::StringSortCompare(thread, middleString, presentString); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + if (compareResult > 0) { + endIndex = middleIndex; + } else { + beginIndex = middleIndex + 1; + } + } + if (endIndex >= 0 && endIndex < i) { + for (uint32_t j = i; j > endIndex; j--) { + previousValue.Update(elements->Get(j - 1)); + elements.Update(TryCopyCOWArray(thread, JSHandle(thisObj), isCOWArray)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + elements->Set(thread, j, previousValue); + } + elements.Update(TryCopyCOWArray(thread, JSHandle(thisObj), isCOWArray)); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + elements->Set(thread, endIndex, presentValue); + } + } + return thisObj.GetTaggedValue(); +} } // namespace panda::ecmascript #endif // ECMASCRIPT_STUBS_RUNTIME_STUBS_INL_H diff --git a/ecmascript/stubs/runtime_stubs.cpp b/ecmascript/stubs/runtime_stubs.cpp index 294edf53ff3a4b74956df2bba240cd35f0c3c10f..2aadee3fa5f62ebb81dda78786015c053246f3f6 100644 --- a/ecmascript/stubs/runtime_stubs.cpp +++ b/ecmascript/stubs/runtime_stubs.cpp @@ -15,9 +15,11 @@ #include #include "ecmascript/js_tagged_value.h" +#include "ecmascript/log.h" #include "ecmascript/log_wrapper.h" #include "ecmascript/stubs/runtime_stubs-inl.h" #include "ecmascript/accessor_data.h" +#include "ecmascript/base/fast_json_stringifier.h" #include "ecmascript/base/number_helper.h" #include "ecmascript/base/string_helper.h" #include "ecmascript/compiler/builtins/containers_stub_builder.h" @@ -49,11 +51,22 @@ #include "ecmascript/message_string.h" #include "ecmascript/object_factory.h" #include "ecmascript/pgo_profiler/pgo_profiler.h" +#include "ecmascript/stubs/runtime_stubs.h" #include "ecmascript/subtyping_operator.h" #include "ecmascript/tagged_dictionary.h" #include "ecmascript/tagged_node.h" #include "ecmascript/ts_types/ts_manager.h" +#include "ecmascript/linked_hash_table.h" #include "libpandafile/bytecode_instruction-inl.h" +#include "macros.h" +#ifdef ARK_SUPPORT_INTL +#include "ecmascript/js_collator.h" +#include "ecmascript/js_locale.h" +#else +#ifndef ARK_NOT_SUPPORT_INTL_GLOBAL +#include "ecmascript/intl/global_intl_helper.h" +#endif +#endif namespace panda::ecmascript { #if defined(__clang__) @@ -127,15 +140,6 @@ DEF_RUNTIME_STUBS(CallInternalSetter) return JSTaggedValue::Undefined().GetRawData(); } -DEF_RUNTIME_STUBS(Dump) -{ - RUNTIME_STUBS_HEADER(Dump); - JSTaggedValue value = GetArg(argv, argc, 0); - value.D(); - std::cout << "======================================================" << std::endl; - return JSTaggedValue::Undefined().GetRawData(); -} - DEF_RUNTIME_STUBS(GetHash32) { JSTaggedValue argKey = GetArg(argv, argc, 0); // 0: means the zeroth parameter @@ -265,7 +269,9 @@ DEF_RUNTIME_STUBS(PropertiesSetValue) if (capacity == 0) { properties = factory->NewTaggedArray(JSObject::MIN_PROPERTIES_LENGTH); } else { - properties = factory->CopyArray(arrayHandle, capacity, JSObject::ComputePropertyCapacity(capacity)); + uint32_t maxNonInlinedFastPropsCapacity = objHandle->GetNonInlinedFastPropsCapacity(); + uint32_t newLen = JSObject::ComputeNonInlinedFastPropsCapacity(capacity, maxNonInlinedFastPropsCapacity); + properties = factory->CopyArray(arrayHandle, capacity, newLen); } properties->Set(thread, index, valueHandle); objHandle->SetProperties(thread, properties); @@ -308,6 +314,16 @@ DEF_RUNTIME_STUBS(CheckAndCopyArray) return receiverHandle->GetElements().GetRawData(); } +DEF_RUNTIME_STUBS(JSObjectGrowElementsCapacity) +{ + RUNTIME_STUBS_HEADER(JSObjectGrowElementsCapacity); + JSHandle elements = GetHArg(argv, argc, 0); // 0: means the zeroth parameter + JSTaggedValue length = GetArg(argv, argc, 1); // 1: means the zeroth parameter + uint32_t newLength = static_cast(length.GetInt()); + JSHandle newElements = JSObject::GrowElementsCapacity(thread, elements, newLength, true); + return newElements.GetTaggedValue().GetRawData(); +} + DEF_RUNTIME_STUBS(NewEcmaHClass) { RUNTIME_STUBS_HEADER(NewEcmaHClass); @@ -354,6 +370,101 @@ DEF_RUNTIME_STUBS(UpdateLayOutAndAddTransition) return JSTaggedValue::Hole().GetRawData(); } +DEF_RUNTIME_STUBS(CopyAndUpdateObjLayout) +{ + RUNTIME_STUBS_HEADER(CopyAndUpdateObjLayout); + JSHandle newHClassHandle = GetHArg(argv, argc, 1); // 1: means the first parameter + JSHandle keyHandle = GetHArg(argv, argc, 2); // 2: means the second parameter + JSTaggedValue attr = GetArg(argv, argc, 3); // 3: means the third parameter + + auto factory = thread->GetEcmaVM()->GetFactory(); + PropertyAttributes attrValue(attr.GetInt()); + + // 1. Copy + JSHandle oldLayout(thread, newHClassHandle->GetLayout()); + JSHandle newLayout(factory->CopyLayoutInfo(oldLayout)); + newHClassHandle->SetLayout(thread, newLayout); + + // 2. Update attr + auto hclass = JSHClass::Cast(newHClassHandle.GetTaggedValue().GetTaggedObject()); + int entry = JSHClass::FindPropertyEntry(thread, hclass, keyHandle.GetTaggedValue()); + ASSERT(entry != -1); + newLayout->SetNormalAttr(thread, entry, attrValue); + + // 3. Maybe Transition And Maintain subtypeing check + return JSTaggedValue::Hole().GetRawData(); +} + +DEF_RUNTIME_STUBS(UpdateHClassForElementsKind) +{ + RUNTIME_STUBS_HEADER(UpdateHClassForElementsKind); + JSHandle receiver = GetHArg(argv, argc, 0); // 0: means the first parameter + JSTaggedType elementsKind = GetTArg(argv, argc, 1); // 1: means the first parameter + ASSERT(receiver->IsJSArray()); + ElementsKind kind = Elements::FixElementsKind(static_cast(elementsKind)); + auto arrayIndexMap = thread->GetArrayHClassIndexMap(); + if (arrayIndexMap.find(kind) != arrayIndexMap.end()) { + auto index = thread->GetArrayHClassIndexMap().at(kind); + auto globalConst = thread->GlobalConstants(); + auto targetHClassValue = globalConst->GetGlobalConstantObject(static_cast(index)); + auto hclass = JSHClass::Cast(targetHClassValue.GetTaggedObject()); + auto array = JSHandle(receiver); + array->SetClass(hclass); + // Update TrackInfo + if (!thread->IsPGOProfilerEnable()) { + return JSTaggedValue::Hole().GetRawData(); + } + auto trackInfoVal = array->GetTrackInfo(); + thread->GetEcmaVM()->GetPGOProfiler()->UpdateTrackInfo(trackInfoVal, kind); + } + return JSTaggedValue::Hole().GetRawData(); +} + +void RuntimeStubs::Dump(JSTaggedType rawValue) +{ + DumpWithHint(reinterpret_cast(nullptr), rawValue); +} + +void RuntimeStubs::DebugDump(JSTaggedType rawValue) +{ + DebugDumpWithHint(reinterpret_cast(nullptr), rawValue); +} + +void RuntimeStubs::DumpWithHint(uintptr_t hintStrAddress, JSTaggedType rawValue) +{ + const char *origHintStr = reinterpret_cast(hintStrAddress); // May be nullptr + const char *hintStr = (origHintStr == nullptr) ? "" : origHintStr; + DumpToStreamWithHint(std::cout, hintStr, JSTaggedValue(rawValue)); + std::cout << std::endl; // New line +} + +void RuntimeStubs::DebugDumpWithHint(uintptr_t hintStrAddress, JSTaggedType rawValue) +{ + const char *origHintStr = reinterpret_cast(hintStrAddress); // May be nullptr + const char *hintStr = (origHintStr == nullptr) ? "" : origHintStr; + // The immediate lambda expression call is not evaluated when the logger is unabled. + LOG_ECMA(DEBUG) << [](const char *hintStr, JSTaggedType rawValue) { + std::ostringstream out; + DumpToStreamWithHint(out, hintStr, JSTaggedValue(rawValue)); + return out.str(); + }(hintStr, rawValue); +} + +void RuntimeStubs::DumpToStreamWithHint(std::ostream &out, std::string_view hint, JSTaggedValue value) +{ + constexpr std::string_view dumpDelimiterLine = "================"; + // Begin line + out << dumpDelimiterLine << " Begin dump: " << hint << ' ' << dumpDelimiterLine << std::endl; + // Dumps raw data + out << "(Raw value = 0x" << std::setw(base::INT64_HEX_DIGITS) << std::hex + << std::setfill('0') << value.GetRawData() << ") "; + out << std::dec << std::setfill(' '); // Recovers integer radix & fill character + // Dumps tagged value + value.Dump(out); + // End line + out << dumpDelimiterLine << " End dump: " << hint << ' ' << dumpDelimiterLine; +} + void RuntimeStubs::DebugPrint(int fmtMessageId, ...) { std::string format = MessageString::GetMessageString(fmtMessageId); @@ -361,13 +472,22 @@ void RuntimeStubs::DebugPrint(int fmtMessageId, ...) va_start(args, fmtMessageId); std::string result = base::StringHelper::Vformat(format.c_str(), args); if (MessageString::IsBuiltinsStubMessageString(fmtMessageId)) { - LOG_BUILTINS(ERROR) << result; + LOG_BUILTINS(DEBUG) << result; } else { - LOG_ECMA(ERROR) << result; + LOG_ECMA(DEBUG) << result; } va_end(args); } +void RuntimeStubs::DebugPrintCustom(uintptr_t fmt, ...) +{ + va_list args; + va_start(args, fmt); + std::string result = base::StringHelper::Vformat(reinterpret_cast(fmt), args); + LOG_ECMA(DEBUG) << result; + va_end(args); +} + void RuntimeStubs::DebugPrintInstruction([[maybe_unused]] uintptr_t argGlue, const uint8_t *pc) { BytecodeInstruction inst(pc); @@ -380,30 +500,6 @@ void RuntimeStubs::Comment(uintptr_t argStr) LOG_ECMA(DEBUG) << str; } -void RuntimeStubs::ProfileCall(uintptr_t argGlue, uintptr_t func) -{ - auto thread = JSThread::GlueToJSThread(argGlue); - thread->GetEcmaVM()->GetPGOProfiler()->ProfileCall(func); -} - -void RuntimeStubs::ProfileOpType(uintptr_t argGlue, uintptr_t func, int32_t offset, int32_t type) -{ - auto thread = JSThread::GlueToJSThread(argGlue); - thread->GetEcmaVM()->GetPGOProfiler()->ProfileOpType(func, offset, type); -} - -void RuntimeStubs::ProfileDefineClass(uintptr_t argGlue, uintptr_t func, int32_t offset, uintptr_t ctor) -{ - auto thread = JSThread::GlueToJSThread(argGlue); - thread->GetEcmaVM()->GetPGOProfiler()->ProfileDefineClass(thread, func, offset, ctor); -} - -void RuntimeStubs::ProfileObjLayout(uintptr_t argGlue, uintptr_t func, int32_t offset, uintptr_t object, int32_t store) -{ - auto thread = JSThread::GlueToJSThread(argGlue); - thread->GetEcmaVM()->GetPGOProfiler()->ProfileObjLayout(thread, func, offset, object, store != 0); -} - void RuntimeStubs::FatalPrint(int fmtMessageId, ...) { std::string format = MessageString::GetMessageString(fmtMessageId); @@ -416,6 +512,17 @@ void RuntimeStubs::FatalPrint(int fmtMessageId, ...) UNREACHABLE(); } +void RuntimeStubs::FatalPrintCustom(uintptr_t fmt, ...) +{ + va_list args; + va_start(args, fmt); + std::string result = base::StringHelper::Vformat(reinterpret_cast(fmt), args); + LOG_FULL(FATAL) << result; + va_end(args); + LOG_ECMA(FATAL) << "this branch is unreachable"; + UNREACHABLE(); +} + DEF_RUNTIME_STUBS(NoticeThroughChainAndRefreshUser) { RUNTIME_STUBS_HEADER(NoticeThroughChainAndRefreshUser); @@ -672,7 +779,7 @@ DEF_RUNTIME_STUBS(LoadICByValue) JSHandle propKey = JSTaggedValue::ToPropertyKey(thread, key); RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception().GetRawData()); LoadICRuntime icRuntime(thread, JSHandle::Cast(profileTypeInfo), slotId.GetInt(), ICKind::LoadIC); - return icRuntime.LoadMiss(receiver, propKey).GetRawData(); + return icRuntime.LoadValueMiss(receiver, propKey).GetRawData(); } DEF_RUNTIME_STUBS(StoreICByValue) @@ -749,8 +856,9 @@ DEF_RUNTIME_STUBS(GetMethodFromCache) RUNTIME_STUBS_HEADER(GetMethodFromCache); JSHandle constpool = GetHArg(argv, argc, 0); // 0: means the zeroth parameter JSTaggedValue index = GetArg(argv, argc, 1); // 1: means the first parameter + JSHandle module = GetHArg(argv, argc, 2); // 2: means the second parameter return ConstantPool::GetMethodFromCache( - thread, constpool.GetTaggedValue(), index.GetInt()).GetRawData(); + thread, constpool.GetTaggedValue(), module.GetTaggedValue(), index.GetInt()).GetRawData(); } DEF_RUNTIME_STUBS(GetStringFromCache) @@ -907,23 +1015,6 @@ DEF_RUNTIME_STUBS(CreateClassWithBuffer) static_cast(literalId.GetInt()), module).GetRawData(); } -DEF_RUNTIME_STUBS(CreateClassWithIHClass) -{ - RUNTIME_STUBS_HEADER(CreateClassWithIHClass); - JSHandle base = GetHArg(argv, argc, 0); // 0: means the zeroth parameter - JSHandle lexenv = GetHArg(argv, argc, 1); // 1: means the first parameter - JSHandle constpool = GetHArg(argv, argc, 2); // 2: means the second parameter - JSTaggedValue methodId = GetArg(argv, argc, 3); // 3: means the third parameter - JSTaggedValue literalId = GetArg(argv, argc, 4); // 4: means the four parameter - JSHandle ihclass = GetHArg(argv, argc, 5); // 5: means the fifth parameter - JSHandle constructorHClass = GetHArg(argv, argc, 6); // 6: means the fifth parameter - JSHandle module = GetHArg(argv, argc, 7); // 7: means the sixth parameter - return RuntimeCreateClassWithIHClass(thread, base, lexenv, constpool, - static_cast(methodId.GetInt()), - static_cast(literalId.GetInt()), - ihclass, constructorHClass, module).GetRawData(); -} - DEF_RUNTIME_STUBS(SetClassConstructorLength) { RUNTIME_STUBS_HEADER(SetClassConstructorLength); @@ -947,6 +1038,22 @@ DEF_RUNTIME_STUBS(UpdateHotnessCounter) return profileTypeInfo.GetRawData(); } +DEF_RUNTIME_STUBS(PGODump) +{ + RUNTIME_STUBS_HEADER(PGODump); + JSHandle thisFunc = GetHArg(argv, argc, 0); // 0: means the zeroth parameter + thread->GetEcmaVM()->GetPGOProfiler()->PGODump(thisFunc.GetTaggedType()); + return JSTaggedValue::Undefined().GetRawData(); +} + +DEF_RUNTIME_STUBS(PGOPreDump) +{ + RUNTIME_STUBS_HEADER(PGOPreDump); + JSHandle thisFunc = GetHArg(argv, argc, 0); // 0: means the zeroth parameter + thread->GetEcmaVM()->GetPGOProfiler()->PGOPreDump(thisFunc.GetTaggedType()); + return JSTaggedValue::Undefined().GetRawData(); +} + DEF_RUNTIME_STUBS(UpdateHotnessCounterWithProf) { RUNTIME_STUBS_HEADER(UpdateHotnessCounterWithProf); @@ -955,9 +1062,12 @@ DEF_RUNTIME_STUBS(UpdateHotnessCounterWithProf) JSHandle method(thread, thisFunc->GetMethod()); auto profileTypeInfo = method->GetProfileTypeInfo(); if (profileTypeInfo.IsUndefined()) { - thread->GetEcmaVM()->GetPGOProfiler()->ProfileCall(thisFunc.GetTaggedType(), SampleMode::HOTNESS_MODE); uint32_t slotSize = method->GetSlotSize(); auto res = RuntimeNotifyInlineCache(thread, method, slotSize); + thread->GetEcmaVM()->GetPGOProfiler()->ProfileCall(thisFunc.GetTaggedType(), pgo::SampleMode::HOTNESS_MODE); + if (!res.IsUndefined()) { + thread->GetEcmaVM()->GetPGOProfiler()->PGOPreDump(thisFunc.GetTaggedType()); + } return res.GetRawData(); } return profileTypeInfo.GetRawData(); @@ -1117,8 +1227,13 @@ DEF_RUNTIME_STUBS(UpFrame) uintptr_t pc = reinterpret_cast(method->GetBytecodeArray() + pcOffset); return JSTaggedValue(static_cast(pc)).GetRawData(); } + if (!method->IsNativeWithCallField()) { + auto *debuggerMgr = thread->GetEcmaVM()->GetJsDebuggerManager(); + debuggerMgr->GetNotificationManager()->MethodExitEvent(thread, method); + } } - return JSTaggedValue(static_cast(0)).GetRawData(); + LOG_FULL(FATAL) << "EXCEPTION: EntryFrame Not Found"; + UNREACHABLE(); } DEF_RUNTIME_STUBS(GetModuleNamespaceByIndex) @@ -1515,6 +1630,49 @@ DEF_RUNTIME_STUBS(NotifyDebuggerStatement) return RuntimeNotifyDebuggerStatement(thread).GetRawData(); } +DEF_RUNTIME_STUBS(MethodEntry) +{ + RUNTIME_STUBS_HEADER(MethodEntry); + JSHandle func = GetHArg(argv, argc, 0); // 0: means the zeroth parameter + if (func.GetTaggedValue().IsECMAObject()) { + Method *method = ECMAObject::Cast(func.GetTaggedValue().GetTaggedObject())->GetCallTarget(); + if (method->IsNativeWithCallField()) { + return JSTaggedValue::Hole().GetRawData(); + } + JSHandle funcObj = JSHandle::Cast(func); + FrameHandler frameHandler(thread); + for (; frameHandler.HasFrame(); frameHandler.PrevJSFrame()) { + if (frameHandler.IsEntryFrame() || frameHandler.IsBuiltinFrame()) { + continue; + } + auto *debuggerMgr = thread->GetEcmaVM()->GetJsDebuggerManager(); + debuggerMgr->GetNotificationManager()->MethodEntryEvent(thread, method, funcObj->GetLexicalEnv()); + return JSTaggedValue::Hole().GetRawData(); + } + } + return JSTaggedValue::Hole().GetRawData(); +} + +DEF_RUNTIME_STUBS(MethodExit) +{ + RUNTIME_STUBS_HEADER(MethodExit); + FrameHandler frameHandler(thread); + for (; frameHandler.HasFrame(); frameHandler.PrevJSFrame()) { + if (frameHandler.IsEntryFrame() || frameHandler.IsBuiltinFrame()) { + continue; + } + Method *method = frameHandler.GetMethod(); + // Skip builtins method + if (method->IsNativeWithCallField()) { + continue; + } + auto *debuggerMgr = thread->GetEcmaVM()->GetJsDebuggerManager(); + debuggerMgr->GetNotificationManager()->MethodExitEvent(thread, method); + return JSTaggedValue::Hole().GetRawData(); + } + return JSTaggedValue::Hole().GetRawData(); +} + DEF_RUNTIME_STUBS(CreateEmptyObject) { RUNTIME_STUBS_HEADER(CreateEmptyObject); @@ -1859,8 +2017,8 @@ DEF_RUNTIME_STUBS(DebugAOTPrint) int ecmaOpcode = GetArg(argv, argc, 0).GetInt(); int path = GetArg(argv, argc, 1).GetInt(); std::string result = kungfu::GetEcmaOpcodeStr(static_cast(ecmaOpcode)); - std::string pathStr = path == 0 ? "slowpath " : "typedpath "; - LOG_ECMA(INFO) << "aot " << pathStr << result; + std::string pathStr = path == 0 ? "slow path " : "TYPED path "; + LOG_ECMA(INFO) << "AOT " << pathStr << result; return JSTaggedValue::Undefined().GetRawData(); } @@ -1956,7 +2114,7 @@ DEF_RUNTIME_STUBS(StringEqual) EcmaVM *vm = thread->GetEcmaVM(); left = JSHandle(thread, EcmaStringAccessor::Flatten(vm, left)); right = JSHandle(thread, EcmaStringAccessor::Flatten(vm, right)); - if (EcmaStringAccessor::StringsAreEqualSameUtfEncoding(*left, *right)) { + if (EcmaStringAccessor::StringsAreEqualDiffUtfEncoding(*left, *right)) { return JSTaggedValue::VALUE_TRUE; } return JSTaggedValue::VALUE_FALSE; @@ -2026,35 +2184,10 @@ DEF_RUNTIME_STUBS(ContainerRBTreeForEach) DEF_RUNTIME_STUBS(SlowFlattenString) { RUNTIME_STUBS_HEADER(SlowFlattenString); - JSHandle str = GetHArg(argv, argc, 0); // 0: means the zeroth parameter + JSHandle str = GetHArg(argv, argc, 0); // 0: means the zeroth parameter return JSTaggedValue(EcmaStringAccessor::SlowFlatten(thread->GetEcmaVM(), str)).GetRawData(); } -DEF_RUNTIME_STUBS(OtherToNumber) -{ - RUNTIME_STUBS_HEADER(OtherToNumber); - JSTaggedValue tagged = GetArg(argv, argc, 0); // 0: means the zeroth parameter - if (tagged.IsString()) { - return JSTaggedValue::StringToDouble(tagged).GetRawData(); - } - if (tagged.IsECMAObject()) { - JSHandle taggedHandle(thread, tagged); - JSHandle primValue(thread, JSTaggedValue::ToPrimitive(thread, taggedHandle, PREFER_NUMBER)); - RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception().GetRawData()); - return JSTaggedValue::ToNumber(thread, primValue).GetRawData(); - } - - if (tagged.IsSymbol()) { - THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert a Symbol value to a number", - JSTaggedValue::VALUE_EXCEPTION); - } - if (tagged.IsBigInt()) { - THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert a BigInt value to a number", - JSTaggedValue::VALUE_EXCEPTION); - } - THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert a Unknown value to a number", JSTaggedValue::VALUE_EXCEPTION); -} - JSTaggedType RuntimeStubs::CreateArrayFromList([[maybe_unused]] uintptr_t argGlue, int32_t argc, JSTaggedValue *argvPtr) { @@ -2138,9 +2271,21 @@ JSTaggedType RuntimeStubs::FloatFloor(double x) return JSTaggedValue(result).GetRawData(); } -int32_t RuntimeStubs::DoubleToInt(double x) +int32_t RuntimeStubs::DoubleToInt(double x, size_t bits) +{ + return base::NumberHelper::DoubleToInt(x, bits); +} + +JSTaggedType RuntimeStubs::DoubleToLength(double x) { - return base::NumberHelper::DoubleToInt(x, base::INT32_BITS); + double length = base::NumberHelper::TruncateDouble(x); + if (length < 0.0) { + return JSTaggedNumber(static_cast(0)).GetRawData(); + } + if (length > SAFE_NUMBER) { + return JSTaggedNumber(static_cast(SAFE_NUMBER)).GetRawData(); + } + return JSTaggedNumber(length).GetRawData(); } void RuntimeStubs::InsertOldToNewRSet([[maybe_unused]] uintptr_t argGlue, @@ -2192,7 +2337,7 @@ void RuntimeStubs::StoreBarrier([[maybe_unused]] uintptr_t argGlue, bool RuntimeStubs::StringsAreEquals(EcmaString *str1, EcmaString *str2) { - return EcmaStringAccessor::StringsAreEqualSameUtfEncoding(str1, str2); + return EcmaStringAccessor::StringsAreEqualDiffUtfEncoding(str1, str2); } bool RuntimeStubs::BigIntEquals(JSTaggedType left, JSTaggedType right) @@ -2200,6 +2345,11 @@ bool RuntimeStubs::BigIntEquals(JSTaggedType left, JSTaggedType right) return BigInt::Equal(JSTaggedValue(left), JSTaggedValue(right)); } +bool RuntimeStubs::BigIntSameValueZero(JSTaggedType left, JSTaggedType right) +{ + return BigInt::SameValueZero(JSTaggedValue(left), JSTaggedValue(right)); +} + double RuntimeStubs::TimeClip(double time) { return JSDate::TimeClip(time); @@ -2255,15 +2405,13 @@ void RuntimeStubs::SaveFrameToContext(JSThread *thread, JSHandleSet(thread, i, value); } context->SetRegsArray(thread, regsArray.GetTaggedValue()); - JSHandle function(thread, frameHandler.GetFunction()); - Method *method = JSFunction::Cast(function->GetTaggedObject())->GetCallTarget(); - if (method->IsAotWithCallField()) { - FunctionKind kind = method->GetFunctionKind(); - JSHandle jsFunc(function); - JSHandle oldHclass(thread, jsFunc->GetClass()); + JSHandle function(thread, frameHandler.GetFunction()); + JSHandle hclass(thread, function->GetClass()); + if (hclass->IsOptimized()) { + FunctionKind kind = function->GetCallTarget()->GetFunctionKind(); // instead of hclass by non_optimized hclass when method ClearAOTFlags - JSHandle newHClass = factory->GetNonOptimizedHclass(oldHclass, kind); - jsFunc->SetClass(newHClass); + JSHandle newHClass = factory->GetNonOptimizedHclass(hclass, kind); + function->SynchronizedSetClass(*newHClass); } context->SetMethod(thread, function.GetTaggedValue()); context->SetThis(thread, frameHandler.GetThis()); @@ -2285,10 +2433,17 @@ JSTaggedValue RuntimeStubs::CallBoundFunction(EcmaRuntimeCallInfo *info) THROW_TYPE_ERROR_AND_RETURN(thread, "class constructor cannot called without 'new'", JSTaggedValue::Exception()); } - + if (thread->IsPGOProfilerEnable()) { + ECMAObject *callTarget = reinterpret_cast(targetFunc.GetTaggedValue().GetTaggedObject()); + ASSERT(callTarget != nullptr); + Method *method = callTarget->GetCallTarget(); + if (!method->IsNativeWithCallField()) { + thread->GetEcmaVM()->GetPGOProfiler()->ProfileCall(targetFunc.GetTaggedType()); + } + } JSHandle boundArgs(thread, boundFunc->GetBoundArguments()); - const int32_t boundLength = static_cast(boundArgs->GetLength()); - const int32_t argsLength = static_cast(info->GetArgsNumber()) + boundLength; + const uint32_t boundLength = boundArgs->GetLength(); + const uint32_t argsLength = info->GetArgsNumber() + boundLength; JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); EcmaRuntimeCallInfo *runtimeInfo = EcmaInterpreter::NewRuntimeCallInfo(thread, JSHandle(targetFunc), info->GetThis(), undefined, argsLength); @@ -2327,8 +2482,8 @@ DEF_RUNTIME_STUBS(AotInlineTrace) JSFunction *inlineJSFunc = JSFunction::Cast(inlineFunc); Method *callerMethod = Method::Cast(JSFunction::Cast(callerJSFunc)->GetMethod()); Method *inlineMethod = Method::Cast(JSFunction::Cast(inlineJSFunc)->GetMethod()); - auto callerRecordName = callerMethod->GetRecordName(); - auto inlineRecordNanme = inlineMethod->GetRecordName(); + auto callerRecordName = callerMethod->GetRecordNameStr(); + auto inlineRecordNanme = inlineMethod->GetRecordNameStr(); const std::string callerFuncName(callerMethod->GetMethodName()); const std::string inlineFuncNanme(inlineMethod->GetMethodName()); std::string callerFullName = callerFuncName + "@" + std::string(callerRecordName); @@ -2338,6 +2493,97 @@ DEF_RUNTIME_STUBS(AotInlineTrace) return JSTaggedValue::Undefined().GetRawData(); } +DEF_RUNTIME_STUBS(LocaleCompare) +{ + RUNTIME_STUBS_HEADER(LocaleCompare); + + JSHandle thisTag = GetHArg(argv, argc, 0); // 0: means the zeroth parameter + JSHandle thatTag = GetHArg(argv, argc, 1); // 1: means the first parameter + JSHandle locales = GetHArg(argv, argc, 2); // 2: means the second parameter + JSHandle options = GetHArg(argv, argc, 3); // 3: means the third parameter + + JSHandle thisObj(JSTaggedValue::RequireObjectCoercible(thread, thisTag)); + [[maybe_unused]] JSHandle thisHandle = JSTaggedValue::ToString(thread, thisObj); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception().GetRawData()); + [[maybe_unused]] JSHandle thatHandle = JSTaggedValue::ToString(thread, thatTag); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception().GetRawData()); + + [[maybe_unused]] bool cacheable = options->IsUndefined() && (locales->IsUndefined() || locales->IsString()); +#ifdef ARK_SUPPORT_INTL + if (cacheable) { + auto collator = JSCollator::GetCachedIcuCollator(thread, locales); + if (collator != nullptr) { + JSTaggedValue result = JSCollator::CompareStrings(collator, thisHandle, thatHandle); + return result.GetRawData(); + } + } + EcmaVM *ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle ctor = ecmaVm->GetGlobalEnv()->GetCollatorFunction(); + JSHandle collator = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(ctor))); + JSHandle initCollator = + JSCollator::InitializeCollator(thread, collator, locales, options, cacheable, true); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception().GetRawData()); + icu::Collator *icuCollator = nullptr; + if (cacheable) { + icuCollator = JSCollator::GetCachedIcuCollator(thread, locales); + ASSERT(icuCollator != nullptr); + } else { + icuCollator = initCollator->GetIcuCollator(); + } + JSTaggedValue result = JSCollator::FastCompareStrings(thread, icuCollator, thisHandle, thatHandle); + return result.GetRawData(); +#else +#ifdef ARK_NOT_SUPPORT_INTL_GLOBAL + ARK_SUPPORT_INTL_RETURN_JSVALUE(thread, "LocaleCompare"); +#else + intl::GlobalIntlHelper gh(thread, intl::GlobalFormatterType::Collator); + auto collator = gh.GetGlobalObject(thread, + locales, options, intl::GlobalFormatterType::Collator, cacheable); + if (collator == nullptr) { + LOG_ECMA(ERROR) << "BuiltinsString::LocaleCompare:collator is nullptr"; + } + ASSERT(collator != nullptr); + auto result = collator->Compare(EcmaStringAccessor(thisHandle).ToStdString(), + EcmaStringAccessor(thatHandle).ToStdString()); + return JSTaggedValue(result).GetRawData(); +#endif +#endif +} + +DEF_RUNTIME_STUBS(ArraySort) +{ + RUNTIME_STUBS_HEADER(ArraySort); + + JSHandle thisHandle = GetHArg(argv, argc, 0); + return RuntimeArraySort(thread, thisHandle).GetRawData(); +} + +JSTaggedValue RuntimeStubs::RuntimeArraySort(JSThread *thread, JSHandle thisHandle) +{ + // 1. Let obj be ToObject(this value). + JSHandle thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + + // 2. Let len be ToLength(Get(obj, "length")). + int64_t len = ArrayHelper::GetArrayLength(thread, JSHandle(thisObjHandle)); + // 3. ReturnIfAbrupt(len). + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); + JSHandle hclass(thread, thisObjHandle->GetClass()); + if (!hclass->IsDictionaryElement()) { + JSHandle elements(thread, thisObjHandle->GetElements()); + // remove elements number check with pgo later and add int fast path at the same time + if (len <= elements->GetLength() && CheckElementsNumber(elements, len)) { + return ArrayNumberSort(thread, thisObjHandle, len); + } + } + + JSHandle callbackFnHandle(thread, JSTaggedValue::Undefined()); + JSArray::Sort(thread, JSHandle::Cast(thisObjHandle), callbackFnHandle); + return thisObjHandle.GetTaggedValue(); +} + void RuntimeStubs::StartCallTimer(uintptr_t argGlue, JSTaggedType func, bool isAot) { auto thread = JSThread::GlueToJSThread(argGlue); @@ -2364,6 +2610,36 @@ void RuntimeStubs::EndCallTimer(uintptr_t argGlue, JSTaggedType func) callTimer->StopCount(method); } +DEF_RUNTIME_STUBS(FastStringify) +{ + RUNTIME_STUBS_HEADER(FastStringify); + JSHandle value = GetHArg(argv, argc, 0); + base::FastJsonStringifier fastJsonStringifier(thread); + JSHandle result = fastJsonStringifier.Stringify(value); + return result.GetTaggedValue().GetRawData(); +} + +DEF_RUNTIME_STUBS(GetLinkedHash) +{ + RUNTIME_STUBS_HEADER(GetLinkedHash); + JSTaggedValue key = GetArg(argv, argc, 0); // 0: means the zeroth parameter + return JSTaggedValue(LinkedHash::Hash(key)).GetRawData(); +} + +DEF_RUNTIME_STUBS(LinkedHashMapComputeCapacity) +{ + RUNTIME_STUBS_HEADER(LinkedHashMapComputeCapacity); + JSTaggedValue value = GetArg(argv, argc, 0); // 0: means the zeroth parameter + return JSTaggedValue(LinkedHashMap::ComputeCapacity(value.GetInt())).GetRawData(); +} + +DEF_RUNTIME_STUBS(LinkedHashSetComputeCapacity) +{ + RUNTIME_STUBS_HEADER(LinkedHashSetComputeCapacity); + JSTaggedValue value = GetArg(argv, argc, 0); // 0: means the zeroth parameter + return JSTaggedValue(LinkedHashSet::ComputeCapacity(value.GetInt())).GetRawData(); +} + void RuntimeStubs::Initialize(JSThread *thread) { #define DEF_RUNTIME_STUB(name) kungfu::RuntimeStubCSigns::ID_##name diff --git a/ecmascript/stubs/runtime_stubs.h b/ecmascript/stubs/runtime_stubs.h index 3dddb9d7dfd11a5ca4460bfa7024f97422b8103b..4dc00c2148c0700d33dc12b532eb51d2411025a8 100644 --- a/ecmascript/stubs/runtime_stubs.h +++ b/ecmascript/stubs/runtime_stubs.h @@ -68,6 +68,7 @@ using FastCallAotEntryType = JSTaggedValue (*)(uintptr_t glue, uint32_t argc, co V(ResumeRspAndReturn) \ V(ResumeCaughtFrameAndDispatch) \ V(ResumeUncaughtFrameAndReturn) \ + V(ResumeRspAndRollback) \ V(CallSetter) \ V(CallGetter) \ V(CallContainersArgs3) \ @@ -94,19 +95,22 @@ using FastCallAotEntryType = JSTaggedValue (*)(uintptr_t glue, uint32_t argc, co #define RUNTIME_STUB_WITHOUT_GC_LIST(V) \ + V(Dump) \ + V(DebugDump) \ + V(DumpWithHint) \ + V(DebugDumpWithHint) \ V(DebugPrint) \ + V(DebugPrintCustom) \ V(DebugPrintInstruction) \ - V(ProfileCall) \ - V(ProfileDefineClass) \ - V(ProfileOpType) \ - V(ProfileObjLayout) \ V(Comment) \ V(FatalPrint) \ + V(FatalPrintCustom) \ V(GetActualArgvNoGC) \ V(InsertOldToNewRSet) \ V(MarkingBarrier) \ V(StoreBarrier) \ V(DoubleToInt) \ + V(DoubleToLength) \ V(FloatMod) \ V(FloatSqrt) \ V(FloatCos) \ @@ -121,7 +125,8 @@ using FastCallAotEntryType = JSTaggedValue (*)(uintptr_t glue, uint32_t argc, co V(TimeClip) \ V(SetDateValues) \ V(StartCallTimer) \ - V(EndCallTimer) + V(EndCallTimer) \ + V(BigIntSameValueZero) #define RUNTIME_STUB_WITH_GC_LIST(V) \ V(AddElementInternal) \ @@ -130,7 +135,6 @@ using FastCallAotEntryType = JSTaggedValue (*)(uintptr_t glue, uint32_t argc, co V(CallInternalSetter) \ V(CallGetPrototype) \ V(ThrowTypeError) \ - V(Dump) \ V(GetHash32) \ V(ComputeHashcode) \ V(GetTaggedArrayPtrTest) \ @@ -143,6 +147,8 @@ using FastCallAotEntryType = JSTaggedValue (*)(uintptr_t glue, uint32_t argc, co V(CheckAndCopyArray) \ V(NewEcmaHClass) \ V(UpdateLayOutAndAddTransition) \ + V(CopyAndUpdateObjLayout) \ + V(UpdateHClassForElementsKind) \ V(NoticeThroughChainAndRefreshUser) \ V(JumpToCInterpreter) \ V(StGlobalRecord) \ @@ -193,12 +199,13 @@ using FastCallAotEntryType = JSTaggedValue (*)(uintptr_t glue, uint32_t argc, co V(StObjByIndex) \ V(StOwnByIndex) \ V(CreateClassWithBuffer) \ - V(CreateClassWithIHClass) \ V(SetClassConstructorLength) \ V(LoadICByName) \ V(StoreICByName) \ V(UpdateHotnessCounter) \ V(CheckSafePoint) \ + V(PGODump) \ + V(PGOPreDump) \ V(UpdateHotnessCounterWithProf) \ V(GetModuleNamespaceByIndex) \ V(GetModuleNamespaceByIndexOnJSFunc) \ @@ -276,6 +283,8 @@ using FastCallAotEntryType = JSTaggedValue (*)(uintptr_t glue, uint32_t argc, co V(OptCopyRestArgs) \ V(NotifyBytecodePcChanged) \ V(NotifyDebuggerStatement) \ + V(MethodEntry) \ + V(MethodExit) \ V(OptNewLexicalEnvWithName) \ V(OptSuspendGenerator) \ V(OptAsyncGeneratorResolve) \ @@ -303,8 +312,14 @@ using FastCallAotEntryType = JSTaggedValue (*)(uintptr_t glue, uint32_t argc, co V(ContainerRBTreeForEach) \ V(SlowFlattenString) \ V(NotifyConcurrentResult) \ - V(OtherToNumber) \ - V(AotInlineTrace) + V(AotInlineTrace) \ + V(LocaleCompare) \ + V(ArraySort) \ + V(FastStringify) \ + V(GetLinkedHash) \ + V(LinkedHashMapComputeCapacity) \ + V(LinkedHashSetComputeCapacity) \ + V(JSObjectGrowElementsCapacity) #define RUNTIME_STUB_LIST(V) \ RUNTIME_ASM_STUB_LIST(V) \ @@ -348,14 +363,16 @@ public: return reinterpret_cast(*(reinterpret_cast(argv) + (index))); } + static void Dump(JSTaggedType value); + static void DebugDump(JSTaggedType value); + static void DumpWithHint(uintptr_t hintStrAddress, JSTaggedType value); + static void DebugDumpWithHint(uintptr_t hintStrAddress, JSTaggedType value); static void DebugPrint(int fmtMessageId, ...); + static void DebugPrintCustom(uintptr_t fmt, ...); static void DebugPrintInstruction([[maybe_unused]] uintptr_t argGlue, const uint8_t *pc); static void Comment(uintptr_t argStr); - static void ProfileCall(uintptr_t argGlue, uintptr_t func); - static void ProfileDefineClass(uintptr_t argGlue, uintptr_t func, int32_t offset, uintptr_t ctor); - static void ProfileOpType(uintptr_t argGlue, uintptr_t func, int32_t offset, int32_t type); - static void ProfileObjLayout(uintptr_t argGlue, uintptr_t func, int32_t offset, uintptr_t object, int32_t store); static void FatalPrint(int fmtMessageId, ...); + static void FatalPrintCustom(uintptr_t fmt, ...); static void MarkingBarrier([[maybe_unused]] uintptr_t argGlue, uintptr_t object, size_t offset, TaggedObject *value); static void StoreBarrier([[maybe_unused]] uintptr_t argGlue, @@ -363,7 +380,8 @@ public: static JSTaggedType CreateArrayFromList([[maybe_unused]] uintptr_t argGlue, int32_t argc, JSTaggedValue *argvPtr); static JSTaggedType GetActualArgvNoGC(uintptr_t argGlue); static void InsertOldToNewRSet([[maybe_unused]] uintptr_t argGlue, uintptr_t object, size_t offset); - static int32_t DoubleToInt(double x); + static int32_t DoubleToInt(double x, size_t bits); + static JSTaggedType DoubleToLength(double x); static double FloatMod(double x, double y); static JSTaggedType FloatSqrt(double x); static JSTaggedType FloatCos(double x); @@ -375,13 +393,16 @@ public: JSTaggedType key, int32_t num); static bool StringsAreEquals(EcmaString *str1, EcmaString *str2); static bool BigIntEquals(JSTaggedType left, JSTaggedType right); + static bool BigIntSameValueZero(JSTaggedType key, JSTaggedType other); static double TimeClip(double time); static double SetDateValues(double year, double month, double day); static void StartCallTimer(uintptr_t argGlue, JSTaggedType func, bool isAot); static void EndCallTimer(uintptr_t argGlue, JSTaggedType func); + static JSTaggedValue RuntimeArraySort(JSThread *thread, JSHandle thisHandle); static JSTaggedValue CallBoundFunction(EcmaRuntimeCallInfo *info); private: + static void DumpToStreamWithHint(std::ostream &out, std::string_view prompt, JSTaggedValue value); static void PrintHeapReginInfo(uintptr_t argGlue); static inline JSTaggedValue RuntimeInc(JSThread *thread, const JSHandle &value); @@ -469,14 +490,6 @@ private: const JSHandle &constpool, uint16_t methodId, uint16_t literalId, const JSHandle &module); - static inline JSTaggedValue RuntimeCreateClassWithIHClass(JSThread *thread, - const JSHandle &base, - const JSHandle &lexenv, - const JSHandle &constpool, - const uint16_t methodId, uint16_t literalId, - const JSHandle &ihclass, - const JSHandle &constructorHClass, - const JSHandle &module); static inline JSTaggedValue RuntimeSetClassInheritanceRelationship(JSThread *thread, const JSHandle &ctor, const JSHandle &base); @@ -666,6 +679,8 @@ private: static inline JSTaggedValue RuntimeOptConstructGeneric(JSThread *thread, JSHandle ctor, JSHandle newTgt, JSHandle preArgs, JSHandle args); + static inline JSTaggedValue GetResultValue(JSThread *thread, bool isAotMethod, JSHandle ctor, + CVector &values, JSHandle newTgt, uint32_t &size, JSHandle obj); static inline JSTaggedValue RuntimeOptGenerateScopeInfo(JSThread *thread, uint16_t scopeId, JSTaggedValue func); static inline JSTaggedType *GetActualArgv(JSThread *thread); static inline JSTaggedType *GetActualArgvFromStub(JSThread *thread); @@ -681,6 +696,11 @@ private: static inline JSTaggedValue RuntimeNotifyConcurrentResult(JSThread *thread, JSTaggedValue result, JSTaggedValue hint); static inline JSTaggedValue RuntimeNotifyDebuggerStatement(JSThread *thread); + static inline bool CheckElementsNumber(JSHandle elements, uint32_t len); + static inline JSHandle GetOrCreateNumberString(JSThread *thread, + JSHandle presentValue, std::map> &cachedString); + static inline JSTaggedValue TryCopyCOWArray(JSThread *thread, JSHandle holderHandler, bool &isCOWArray); + static inline JSTaggedValue ArrayNumberSort(JSThread *thread, JSHandle thisObj, uint32_t len); friend class SlowRuntimeStub; }; } // namespace panda::ecmascript diff --git a/ecmascript/subtyping_operator.cpp b/ecmascript/subtyping_operator.cpp index 1e9670eed6defda142a95f63b75fba4a212092fb..453cb3239475a054fc6ba5fecfb63c334d5536dc 100644 --- a/ecmascript/subtyping_operator.cpp +++ b/ecmascript/subtyping_operator.cpp @@ -212,6 +212,7 @@ void SubtypingOperator::TryMaintainTSSubtyping(const JSThread *thread, const JSH ASSERT(!oldHClass->IsPrototype()); // normal object hclass JSHandle vtable(thread, oldHClass->GetVTable()); + ASSERT(!vtable.GetTaggedValue().IsUndefined()); ASSERT(vtable->GetNumberOfTuples() > 0); // there have default key 'constructor' at least if (vtable->Find(key.GetTaggedValue())) { // new key shadows vtable property diff --git a/ecmascript/tagged_array-inl.h b/ecmascript/tagged_array-inl.h index c41aadf02d3969841dcff39e4e1dc23e40346fc7..5955ca75e7fd875d8d52b05b9076ea2435df410f 100644 --- a/ecmascript/tagged_array-inl.h +++ b/ecmascript/tagged_array-inl.h @@ -16,8 +16,10 @@ #ifndef ECMASCRIPT_TAGGED_ARRAY_INL_H #define ECMASCRIPT_TAGGED_ARRAY_INL_H +#include "ecmascript/mem/barriers-inl.h" #include "ecmascript/js_tagged_value-inl.h" #include "ecmascript/js_thread.h" +#include "ecmascript/mem/region-inl.h" #include "ecmascript/object_factory.h" #include "ecmascript/tagged_array.h" @@ -63,13 +65,14 @@ inline void TaggedArray::Set(const JSThread *thread, uint32_t idx, const JSHandl } } +template inline void TaggedArray::Set(const JSThread *thread, uint32_t idx, const JSTaggedValue &value) { ASSERT(idx < GetLength()); size_t offset = JSTaggedValue::TaggedTypeSize() * idx; // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon) - if (value.IsHeapObject()) { + if (needBarrier && value.IsHeapObject()) { Barriers::SetObject(thread, GetData(), offset, value.GetRawData()); } else { // NOLINTNEXTLINE(readability-misleading-indentation) Barriers::SetPrimitive(GetData(), offset, value.GetRawData()); diff --git a/ecmascript/tagged_array.h b/ecmascript/tagged_array.h index 68990602cdae5efbd636126213f28dd46df0d733..535bc032a19f379ae37b35327135f35138a33386 100644 --- a/ecmascript/tagged_array.h +++ b/ecmascript/tagged_array.h @@ -39,6 +39,7 @@ public: JSTaggedValue Get(const JSThread *thread, uint32_t idx) const; + template void Set(const JSThread *thread, uint32_t idx, const JSTaggedValue &value); static inline JSHandle Append(const JSThread *thread, const JSHandle &first, diff --git a/ecmascript/tagged_dictionary.cpp b/ecmascript/tagged_dictionary.cpp index 509b08a596aa6e7cfe571b0aa20d2493899f81c1..192dbd70c37d66878a5c7985d310dcc843640c5b 100644 --- a/ecmascript/tagged_dictionary.cpp +++ b/ecmascript/tagged_dictionary.cpp @@ -14,6 +14,7 @@ */ #include "ecmascript/tagged_dictionary.h" +#include "ecmascript/ecma_string-inl.h" #include "ecmascript/filter_helper.h" #include "ecmascript/tagged_hash_table.h" @@ -36,11 +37,31 @@ int NameDictionary::Hash(const JSTaggedValue &key) UNREACHABLE(); } +int NameDictionary::Hash(const uint8_t* str, int strSize) +{ + return EcmaString::ComputeHashForData(str, strSize, 0); +} + bool NameDictionary::IsMatch(const JSTaggedValue &key, const JSTaggedValue &other) { return key == other; } +bool NameDictionary::IsMatch(const uint8_t* str, int size, const JSTaggedValue &other) +{ + if (!other.IsString()) { + return false; + } + EcmaString *keyString = reinterpret_cast(other.GetTaggedObject()); + + Span data1(EcmaStringAccessor(keyString).GetDataUtf8(), keyString->GetLength()); + Span data2(str, size); + if (data1.Size() != data2.Size()) { + return false; + } + return EcmaString::StringsAreEquals(data1, data2); +} + void NameDictionary::GetAllKeys(const JSThread *thread, int offset, TaggedArray *keyArray) const { int arrayIndex = 0; @@ -62,7 +83,7 @@ void NameDictionary::GetAllKeys(const JSThread *thread, int offset, TaggedArray } void NameDictionary::GetAllKeysByFilter(const JSThread *thread, uint32_t &keyArrayEffectivelength, - TaggedArray *keyArray, uint32_t filter) const + TaggedArray *keyArray, uint32_t filter) const { int size = Size(); CVector> sortArr; @@ -94,8 +115,8 @@ void NameDictionary::GetAllKeysByFilter(const JSThread *thread, uint32_t &keyArr void NameDictionary::GetAllEnumKeys(const JSThread *thread, int offset, TaggedArray *keyArray, uint32_t *keys) const { uint32_t arrayIndex = 0; - int size = Size(); CVector> sortArr; + int size = Size(); for (int hashIndex = 0; hashIndex < size; hashIndex++) { JSTaggedValue key = GetKey(hashIndex); if (key.IsString()) { @@ -164,6 +185,10 @@ int NumberDictionary::Hash(const JSTaggedValue &key) int keyValue = key.GetInt(); return GetHash32(reinterpret_cast(&keyValue), sizeof(keyValue) / sizeof(uint8_t)); } + if (key.IsDouble()) { + int32_t keyValue = static_cast(static_cast(key.GetDouble())); + return GetHash32(reinterpret_cast(&keyValue), sizeof(keyValue) / sizeof(uint8_t)); + } // key must be object LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); @@ -181,6 +206,17 @@ bool NumberDictionary::IsMatch(const JSTaggedValue &key, const JSTaggedValue &ot } return false; } + + if (key.IsDouble()) { + if (other.IsInt()) { + int32_t keyValue = static_cast(static_cast(key.GetDouble())); + return keyValue == other.GetInt(); + } + if (other.IsDouble()) { + return key.GetDouble() == other.GetDouble(); + } + return false; + } // key must be integer LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); @@ -211,22 +247,22 @@ void NumberDictionary::GetAllKeys(const JSThread *thread, const JSHandle &obj, - uint32_t &keyArrayEffectivelength, const JSHandle &keyArray, uint32_t filter) + uint32_t &keyArrayEffectivelength, const JSHandle &keyArray, + uint32_t filter) { ASSERT_PRINT(keyArrayEffectivelength + static_cast(obj->EntriesCount()) <= keyArray->GetLength(), "keyArray capacity is not enough for dictionary"); - uint32_t size = static_cast(obj->Size()); CVector sortArr; + uint32_t size = static_cast(obj->Size()); for (uint32_t hashIndex = 0; hashIndex < size; hashIndex++) { JSTaggedValue key = obj->GetKey(hashIndex); if (key.IsUndefined() || key.IsHole()) { continue; } - + PropertyAttributes attr = obj->GetAttributes(hashIndex); bool bIgnore = FilterHelper::IgnoreKeyByFilter(attr, filter); - if (!bIgnore) { sortArr.push_back(JSTaggedValue(static_cast(key.GetInt()))); } diff --git a/ecmascript/tagged_dictionary.h b/ecmascript/tagged_dictionary.h index fd1f6e8abd7f936f79b49f3b98493c4865034c88..3a00d1ed5200102684b0140d1a84864cdde7eb65 100644 --- a/ecmascript/tagged_dictionary.h +++ b/ecmascript/tagged_dictionary.h @@ -42,7 +42,11 @@ public: return ENTRY_SIZE; } static int Hash(const JSTaggedValue &key); + static int Hash(const uint8_t* str, int strSize); + static bool IsMatch(const JSTaggedValue &key, const JSTaggedValue &other); + static bool IsMatch(const uint8_t* str, int size, const JSTaggedValue &other); + static JSHandle Create(const JSThread *thread, int numberOfElements = OrderHashTableT::DEFAULT_ELEMENTS_NUMBER); // Returns the property metaData for the property at entry. diff --git a/ecmascript/tagged_hash_table.h b/ecmascript/tagged_hash_table.h index dc69374d7e5b1932801e680352e00cf712096d84..c3178ef4a2354d499b311fa33faf29b5bfdb97f9 100644 --- a/ecmascript/tagged_hash_table.h +++ b/ecmascript/tagged_hash_table.h @@ -18,7 +18,9 @@ #include +#include "ecmascript/ecma_vm.h" #include "ecmascript/js_handle.h" +#include "ecmascript/object_factory.h" #include "ecmascript/tagged_array.h" namespace panda::ecmascript { @@ -102,14 +104,14 @@ public: static JSHandle Insert(const JSThread *thread, JSHandle &table, const JSHandle &key, const JSHandle &value) { - // Make sure the key object has an identity hash code. - int32_t hash = static_cast(Derived::Hash(key.GetTaggedValue())); int entry = table->FindEntry(key.GetTaggedValue()); if (entry != -1) { table->SetValue(thread, entry, value.GetTaggedValue()); return table; } + // Make sure the key object has an identity hash code. + int32_t hash = static_cast(Derived::Hash(key.GetTaggedValue())); JSHandle newTable = GrowHashTable(thread, table); newTable->AddElement(thread, newTable->FindInsertIndex(hash), key, value); return newTable; @@ -240,6 +242,29 @@ public: return -1; } + inline int FindEntry(const uint8_t* str, int strSize) + { + int size = Size(); + int count = 1; + JSTaggedValue keyValue; + int32_t hash = static_cast(Derived::Hash(str, strSize)); + + for (uint32_t entry = GetFirstPosition(hash, size);; entry = GetNextPosition(entry, count++, size)) { + keyValue = GetKey(entry); + if (keyValue.IsHole()) { + continue; + } + if (keyValue.IsUndefined()) { + return -1; + } + // keyValue defaults to ecmaString + if (Derived::IsMatch(str, strSize, keyValue)) { + return entry; + } + } + return -1; + } + inline int FindInsertIndex(int hash) { int size = Size(); @@ -387,20 +412,21 @@ public: const JSHandle &key, const JSHandle &value, const PropertyAttributes &metaData) { - int32_t hash = static_cast(Derived::Hash(key.GetTaggedValue())); - /* no need to add key if exist */ int entry = table->FindEntry(key.GetTaggedValue()); if (entry != -1) { return table; } + int enumIndex = table->NextEnumerationIndex(thread); PropertyAttributes attr(metaData); attr.SetDictionaryOrder(enumIndex); + attr.SetRepresentation(Representation::TAGGED); // Check whether the table should be growed. JSHandle newTable = HashTableT::GrowHashTable(thread, table); // Compute the key object. + int32_t hash = static_cast(Derived::Hash(key.GetTaggedValue())); entry = newTable->FindInsertIndex(hash); newTable->SetEntry(thread, entry, key.GetTaggedValue(), value.GetTaggedValue(), attr); @@ -413,19 +439,21 @@ public: const JSHandle &key, const JSHandle &value, const PropertyAttributes &metaData) { - int hash = Derived::Hash(key.GetTaggedValue()); int enumIndex = table->NextEnumerationIndex(thread); PropertyAttributes attr(metaData); attr.SetDictionaryOrder(enumIndex); + attr.SetRepresentation(Representation::TAGGED); int entry = table->FindEntry(key.GetTaggedValue()); if (entry != -1) { table->SetEntry(thread, entry, key.GetTaggedValue(), value.GetTaggedValue(), attr); return table; } + // Check whether the table should be extended. JSHandle newTable = HashTableT::GrowHashTable(thread, table); // Compute the key object. + int hash = Derived::Hash(key.GetTaggedValue()); entry = newTable->FindInsertIndex(hash); newTable->SetEntry(thread, entry, key.GetTaggedValue(), value.GetTaggedValue(), attr); @@ -465,6 +493,7 @@ public: int enumIndex = PropertyAttributes::INITIAL_PROPERTY_INDEX + i; PropertyAttributes attr = table->GetAttributes(oldIndex); attr.SetDictionaryOrder(enumIndex); + attr.SetRepresentation(Representation::TAGGED); table->SetAttributes(thread, oldIndex, attr); } index = PropertyAttributes::INITIAL_PROPERTY_INDEX + length; @@ -501,4 +530,4 @@ public: static constexpr int TABLE_HEADER_SIZE = 4; }; } // namespace panda::ecmascript -#endif // ECMASCRIPT_NEW_HASH_TABLE_H \ No newline at end of file +#endif // ECMASCRIPT_NEW_HASH_TABLE_H diff --git a/ecmascript/tagged_list.cpp b/ecmascript/tagged_list.cpp index 1e93b5aa157342145072c2f3507f82d429bd6b63..c98f5c0026a94f75adafb3778230e18b74abb5ec 100644 --- a/ecmascript/tagged_list.cpp +++ b/ecmascript/tagged_list.cpp @@ -134,7 +134,7 @@ JSHandle TaggedList::OwnKeys(JSThread *thread, const JSHan JSHandle keys = factory->NewTaggedArray(length); for (uint32_t i = 0; i < length; i++) { - auto key = base::NumberHelper::NumberToString(thread, JSTaggedValue(i)); + auto key = base::NumberHelper::IntToEcmaString(thread, i); keys->Set(thread, i, key); } return keys; diff --git a/ecmascript/tagged_tree.cpp b/ecmascript/tagged_tree.cpp index 0d09a99e0fdafad369722a5736148162a3c1a232..c22e14efc9f342f391a4a0ae00cf4829ad2d6198 100644 --- a/ecmascript/tagged_tree.cpp +++ b/ecmascript/tagged_tree.cpp @@ -141,7 +141,7 @@ JSHandle TaggedTree::AdjustTaggedTree(const JSThread *thread, return newTree; } - int elements = tree->NumberOfElements(); + uint32_t elements = tree->NumberOfElements(); newTree.Update(factory->NewTaggedArray(len).GetTaggedValue()); newTree->SetNumberOfElements(thread, elements); newTree->SetNumberOfDeletedElements(thread, 0); @@ -393,7 +393,7 @@ JSHandle TaggedTree::Insert(JSThread *thread, JSHandleGetKey(parentIndex)); } - int entry = newTree->NumberOfElements() + newTree->NumberOfDeletedElements(); + uint32_t entry = newTree->NumberOfElements() + newTree->NumberOfDeletedElements(); if (res != ComparisonResult::LESS) { newTree->InsertRightEntry(thread, parentIndex, entry, key.GetTaggedValue(), value.GetTaggedValue()); } else { @@ -407,9 +407,9 @@ JSHandle TaggedTree::Insert(JSThread *thread, JSHandle JSHandle TaggedTree::GrowCapacity(const JSThread *thread, JSHandle &tree) { - int nof = tree->NumberOfElements() + tree->NumberOfDeletedElements(); + uint32_t nof = tree->NumberOfElements() + tree->NumberOfDeletedElements(); int oldCapacity = tree->Capacity(); - if (nof + 1 <= oldCapacity) { + if (static_cast(nof + 1) <= oldCapacity) { return tree; } @@ -470,7 +470,7 @@ template JSHandle TaggedTree::Shrink(const JSThread *thread, const JSHandle &tree) { int oldCapacity = static_cast(tree->Capacity()); - if (tree->NumberOfElements() >= (oldCapacity + 1) / 4) { // 4: quarter + if (static_cast(tree->NumberOfElements()) >= (oldCapacity + 1) / 4) { // 4: quarter return tree; } uint32_t newCapacity = static_cast(oldCapacity - 1) >> 1; diff --git a/ecmascript/tagged_tree.h b/ecmascript/tagged_tree.h index 66d019e1708fdc02efbb060264fd54aead429083..4b4bb512362d5eb4fdd3c212f3ca72261a10e33c 100644 --- a/ecmascript/tagged_tree.h +++ b/ecmascript/tagged_tree.h @@ -59,12 +59,12 @@ public: static void Remove(const JSThread *thread, const JSHandle &tree, int entry); - inline int NumberOfElements() const + inline uint32_t NumberOfElements() const { return Get(NUMBER_OF_ELEMENTS_INDEX).GetInt(); } - inline int NumberOfDeletedElements() const + inline uint32_t NumberOfDeletedElements() const { return Get(NUMBER_OF_HOLE_ENTRIES_INDEX).GetInt(); } @@ -109,7 +109,7 @@ public: return !key.IsHole(); } - inline void SetNumberOfElements(const JSThread *thread, int num) + inline void SetNumberOfElements(const JSThread *thread, uint32_t num) { Set(thread, NUMBER_OF_ELEMENTS_INDEX, JSTaggedValue(num)); } @@ -141,7 +141,7 @@ public: JSHandle compareFn(thread, fn); JSHandle thisArgHandle = thread->GlobalConstants()->GetHandledUndefined(); - const int32_t argsLength = 2; + const uint32_t argsLength = 2; JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, compareFn, thisArgHandle, undefined, argsLength); diff --git a/ecmascript/taskpool/runner.cpp b/ecmascript/taskpool/runner.cpp index 02095a66dc8ef4808c5af93162f8c17397e14f12..f67b365a55be63280ba92338d9c757f9e8b33e09 100644 --- a/ecmascript/taskpool/runner.cpp +++ b/ecmascript/taskpool/runner.cpp @@ -15,8 +15,11 @@ #include "ecmascript/taskpool/runner.h" -#include "libpandabase/os/mutex.h" +#include "ecmascript/platform/mutex.h" #include "libpandabase/os/thread.h" +#ifdef ENABLE_QOS +#include "qos.h" +#endif namespace panda::ecmascript { Runner::Runner(uint32_t threadNum) : totalThreadNum_(threadNum) @@ -24,7 +27,6 @@ Runner::Runner(uint32_t threadNum) : totalThreadNum_(threadNum) for (uint32_t i = 0; i < threadNum; i++) { // main thread is 0; std::unique_ptr thread = std::make_unique(&Runner::Run, this, i + 1); - os::thread::SetThreadName(thread->native_handle(), "GC_WorkerThread"); threadPool_.emplace_back(std::move(thread)); } @@ -36,7 +38,7 @@ Runner::Runner(uint32_t threadNum) : totalThreadNum_(threadNum) void Runner::TerminateTask(int32_t id, TaskType type) { taskQueue_.TerminateTask(id, type); - os::memory::LockHolder holder(mtx_); + LockHolder holder(mtx_); for (uint32_t i = 0; i < runningTask_.size(); i++) { if (runningTask_[i] != nullptr) { if (id != ALL_TASK_ID && id != runningTask_[i]->GetId()) { @@ -55,7 +57,7 @@ void Runner::TerminateThread() TerminateTask(ALL_TASK_ID, TaskType::ALL); taskQueue_.Terminate(); - os::memory::LockHolder holder(mtxPool_); + LockHolder holder(mtxPool_); uint32_t threadNum = threadPool_.size(); for (uint32_t i = 0; i < threadNum; i++) { threadPool_.at(i)->join(); @@ -63,14 +65,38 @@ void Runner::TerminateThread() threadPool_.clear(); } +void Runner::SetQosPriority([[maybe_unused]] bool isForeground) +{ +#ifdef ENABLE_QOS + if (isForeground) { + for (uint32_t threadId : gcThreadId_) { + OHOS::QOS::SetQosForOtherThread(OHOS::QOS::QosLevel::qos_user_initiated, threadId); + } + } else { + for (uint32_t threadId : gcThreadId_) { + OHOS::QOS::ResetQosForOtherThread(threadId); + } + } +#endif +} + +void Runner::RecordThreadId() +{ + LockHolder holder(mtx_); + gcThreadId_.emplace_back(os::thread::GetCurrentThreadId()); +} + void Runner::SetRunTask(uint32_t threadId, Task *task) { - os::memory::LockHolder holder(mtx_); + LockHolder holder(mtx_); runningTask_[threadId] = task; } void Runner::Run(uint32_t threadId) { + os::thread::native_handle_type thread = os::thread::GetNativeHandle(); + os::thread::SetThreadName(thread, "GC_WorkerThread"); + RecordThreadId(); while (std::unique_ptr task = taskQueue_.PopTask()) { SetRunTask(threadId, task.get()); task->Run(threadId); diff --git a/ecmascript/taskpool/runner.h b/ecmascript/taskpool/runner.h index b1f6718e55ce92762817e8359ab631bad52edac6..c3b8e7f5ac87c38e15b35324bd93764033e07f1d 100644 --- a/ecmascript/taskpool/runner.h +++ b/ecmascript/taskpool/runner.h @@ -23,7 +23,7 @@ #include "ecmascript/common.h" #include "ecmascript/taskpool/task_queue.h" -#include "libpandabase/os/mutex.h" +#include "ecmascript/platform/mutex.h" namespace panda::ecmascript { static constexpr uint32_t MIN_TASKPOOL_THREAD_NUM = 3; @@ -45,6 +45,8 @@ public: void PUBLIC_API TerminateThread(); void TerminateTask(int32_t id, TaskType type); + void SetQosPriority(bool isForeground); + void RecordThreadId(); uint32_t GetTotalThreadNum() const { @@ -53,7 +55,7 @@ public: bool IsInThreadPool(std::thread::id id) { - os::memory::LockHolder holder(mtxPool_); + LockHolder holder(mtxPool_); for (auto &thread : threadPool_) { if (thread->get_id() == id) { return true; @@ -70,8 +72,9 @@ private: TaskQueue taskQueue_ {}; std::array runningTask_; uint32_t totalThreadNum_ {0}; - os::memory::Mutex mtx_; - os::memory::Mutex mtxPool_; + std::vector gcThreadId_ {}; + Mutex mtx_; + Mutex mtxPool_; }; } // namespace panda::ecmascript #endif // ECMASCRIPT_TASKPOOL_RUNNER_H diff --git a/ecmascript/taskpool/task_queue.cpp b/ecmascript/taskpool/task_queue.cpp index de570d56a9a82220531e2dd631693c5c77c01fef..5d6121dc4c5a2f94a24f553bd090d9c9df8932fa 100644 --- a/ecmascript/taskpool/task_queue.cpp +++ b/ecmascript/taskpool/task_queue.cpp @@ -18,7 +18,7 @@ namespace panda::ecmascript { void TaskQueue::PostTask(std::unique_ptr task) { - os::memory::LockHolder holder(mtx_); + LockHolder holder(mtx_); ASSERT(!terminate_); tasks_.push_back(std::move(task)); cv_.Signal(); @@ -26,7 +26,7 @@ void TaskQueue::PostTask(std::unique_ptr task) std::unique_ptr TaskQueue::PopTask() { - os::memory::LockHolder holder(mtx_); + LockHolder holder(mtx_); while (true) { if (!tasks_.empty()) { std::unique_ptr task = std::move(tasks_.front()); @@ -43,7 +43,7 @@ std::unique_ptr TaskQueue::PopTask() void TaskQueue::TerminateTask(int32_t id, TaskType type) { - os::memory::LockHolder holder(mtx_); + LockHolder holder(mtx_); for (auto &task : tasks_) { if (id != ALL_TASK_ID && id != task->GetId()) { continue; @@ -57,7 +57,7 @@ void TaskQueue::TerminateTask(int32_t id, TaskType type) void TaskQueue::Terminate() { - os::memory::LockHolder holder(mtx_); + LockHolder holder(mtx_); terminate_ = true; cv_.SignalAll(); } diff --git a/ecmascript/taskpool/task_queue.h b/ecmascript/taskpool/task_queue.h index 4bb2072ce957b20440c55403c8bf2f9fc70c197a..ce4895879b202d891eb00d295e045753b93b14bd 100644 --- a/ecmascript/taskpool/task_queue.h +++ b/ecmascript/taskpool/task_queue.h @@ -22,7 +22,7 @@ #include #include "ecmascript/taskpool/task.h" -#include "libpandabase/os/mutex.h" +#include "ecmascript/platform/mutex.h" namespace panda::ecmascript { class TaskQueue { @@ -43,8 +43,8 @@ private: std::deque> tasks_; std::atomic_bool terminate_ = false; - os::memory::Mutex mtx_; - os::memory::ConditionVariable cv_; + Mutex mtx_; + ConditionVariable cv_; }; } // namespace panda::ecmascript #endif // ECMASCRIPT_TASKPOOL_TASK_QUEUE_H diff --git a/ecmascript/taskpool/taskpool.cpp b/ecmascript/taskpool/taskpool.cpp index 5f957cedce3dc3c718faf9382a04de959aa918a7..3487c0f8b3d2621f00add026ad4937c3e4432de7 100644 --- a/ecmascript/taskpool/taskpool.cpp +++ b/ecmascript/taskpool/taskpool.cpp @@ -26,7 +26,7 @@ Taskpool *Taskpool::GetCurrentTaskpool() void Taskpool::Initialize(int threadNum) { - os::memory::LockHolder lock(mutex_); + LockHolder lock(mutex_); if (isInitialized_++ <= 0) { runner_ = std::make_unique(TheMostSuitableThreadNum(threadNum)); } @@ -34,7 +34,7 @@ void Taskpool::Initialize(int threadNum) void Taskpool::Destroy(int32_t id) { - os::memory::LockHolder lock(mutex_); + LockHolder lock(mutex_); if (isInitialized_ <= 0) { return; } @@ -48,11 +48,8 @@ void Taskpool::Destroy(int32_t id) void Taskpool::TerminateTask(int32_t id, TaskType type) { - { - os::memory::LockHolder lock(mutex_); - if (isInitialized_ <= 0) { - return; - } + if (isInitialized_ <= 0) { + return; } runner_->TerminateTask(id, type); } diff --git a/ecmascript/taskpool/taskpool.h b/ecmascript/taskpool/taskpool.h index 88280916e0cce28bd8f410d37f3181978dec41c6..0553f71e77de54450eddfcc0aaa2eda20c9bcb4b 100644 --- a/ecmascript/taskpool/taskpool.h +++ b/ecmascript/taskpool/taskpool.h @@ -20,7 +20,7 @@ #include "ecmascript/common.h" #include "ecmascript/taskpool/runner.h" -#include "libpandabase/os/mutex.h" +#include "ecmascript/platform/mutex.h" namespace panda::ecmascript { class Taskpool { @@ -30,7 +30,7 @@ public: Taskpool() = default; PUBLIC_API ~Taskpool() { - os::memory::LockHolder lock(mutex_); + LockHolder lock(mutex_); runner_->TerminateThread(); isInitialized_ = 0; } @@ -43,8 +43,9 @@ public: void PostTask(std::unique_ptr task) const { - ASSERT(isInitialized_ > 0); - runner_->PostTask(std::move(task)); + if (isInitialized_ > 0) { + runner_->PostTask(std::move(task)); + } } // Terminate a task of a specified type @@ -60,12 +61,17 @@ public: return runner_->IsInThreadPool(id); } + void SetThreadPriority(bool isForeground) + { + runner_->SetQosPriority(isForeground); + } + private: uint32_t TheMostSuitableThreadNum(uint32_t threadNum) const; std::unique_ptr runner_; - int isInitialized_ = 0; - os::memory::Mutex mutex_; + volatile int isInitialized_ = 0; + Mutex mutex_; }; } // namespace panda::ecmascript #endif // ECMASCRIPT_PALTFORM_PLATFORM_H diff --git a/ecmascript/template_string.cpp b/ecmascript/template_string.cpp index 509dee2d1192b5e58cc63e70a478e21d0f2ad571..8b8271003c1c350a7ff46bba069342e76ab6617e 100644 --- a/ecmascript/template_string.cpp +++ b/ecmascript/template_string.cpp @@ -39,7 +39,9 @@ JSHandle TemplateString::GetTemplateObject(JSThread *thread, JSHa uint32_t count = cookedStrings->GetArrayLength(); auto countNum = JSTaggedNumber(count); JSHandle templateArr = JSArray::ArrayCreate(thread, countNum); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); JSHandle rawArr = JSArray::ArrayCreate(thread, countNum); + RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread); JSHandle templateObj(templateArr); JSHandle rawObj(rawArr); for (uint32_t i = 0; i < count; i++) { diff --git a/ecmascript/tests/accessor_data_test.cpp b/ecmascript/tests/accessor_data_test.cpp index 7ea12290af46a5e8894e509dd1995a1a364edd7d..f6ac2cab5a57f6fa48e07ac6a0e7fe11f7327199 100644 --- a/ecmascript/tests/accessor_data_test.cpp +++ b/ecmascript/tests/accessor_data_test.cpp @@ -15,7 +15,7 @@ #include "ecmascript/global_env.h" #include "ecmascript/accessor_data.h" -#include "ecmascript/object_factory.h" +#include "ecmascript/object_factory-inl.h" #include "ecmascript/object_operator.h" #include "ecmascript/tests/test_helper.h" diff --git a/ecmascript/tests/byte_array_test.cpp b/ecmascript/tests/byte_array_test.cpp index cb5fa98a7c553b6dbad3d584f0489a2b8704ac3a..1a979366aa50972edce853bebc46e66b2cec3b33 100644 --- a/ecmascript/tests/byte_array_test.cpp +++ b/ecmascript/tests/byte_array_test.cpp @@ -78,7 +78,7 @@ HWTEST_F_L0(ByteArrayTest, SetAndGet) uint32_t value = 4294967295; JSTaggedType val = JSTaggedValue(value).GetRawData(); JSHandle byteArray = factory->NewByteArray(3, sizeof(value)); - byteArray->Set(1, DataViewType::UINT32, val, 2); + byteArray->Set(thread, 1, DataViewType::UINT32, val, 2); EXPECT_EQ(byteArray->Get(thread, 1, DataViewType::UINT32, 2), JSTaggedValue(value)); } } // namespace panda::ecmascript \ No newline at end of file diff --git a/ecmascript/tests/constant_string_test.cpp b/ecmascript/tests/constant_string_test.cpp index d364c2eb51593dec66fae40d07cd6cc45374394a..a1c985a1fac6b34ebcd069531f40577918493579 100644 --- a/ecmascript/tests/constant_string_test.cpp +++ b/ecmascript/tests/constant_string_test.cpp @@ -413,14 +413,14 @@ HWTEST_F_L0(ConstantStringTest, StringsAreEqual_002) /* * @tc.name: StringsAreEqualUtf8_001 - * @tc.desc: Check whether the bool returned through calling StringsAreEqualUtf8 function with an EcmaString made by + * @tc.desc: Check whether the bool returned through calling StringIsEqualUint8Data function with an EcmaString made by * CreateConstantString() and an Array(uint8_t) is within expectations. * @tc.type: FUNC * @tc.require: */ HWTEST_F_L0(ConstantStringTest, StringsAreEqualUtf8_001) { - // StringsAreEqualUtf8(). EcmaString made by CreateConstantString(), Array:U8. + // StringIsEqualUint8Data(). EcmaString made by CreateConstantString(), Array:U8. uint8_t arrayU8No1[4] = {45, 92, 78}; uint8_t arrayU8No2[5] = {45, 92, 78, 24}; uint8_t arrayU8No3[3] = {45, 92}; @@ -433,13 +433,13 @@ HWTEST_F_L0(ConstantStringTest, StringsAreEqualUtf8_001) EcmaStringAccessor::CreateConstantString(ecmaVMPtr, &arrayU8No2[0], lengthEcmaStrU8No2, true)); JSHandle handleEcmaStrU8No3(thread, EcmaStringAccessor::CreateConstantString(ecmaVMPtr, &arrayU8No3[0], lengthEcmaStrU8No3, true)); - EXPECT_TRUE(EcmaStringAccessor::StringsAreEqualUtf8(*handleEcmaStrU8No1, &arrayU8No1[0], lengthEcmaStrU8No1, + EXPECT_TRUE(EcmaStringAccessor::StringIsEqualUint8Data(*handleEcmaStrU8No1, &arrayU8No1[0], lengthEcmaStrU8No1, true)); - EXPECT_FALSE(EcmaStringAccessor::StringsAreEqualUtf8(*handleEcmaStrU8No1, &arrayU8No1[0], lengthEcmaStrU8No1, + EXPECT_FALSE(EcmaStringAccessor::StringIsEqualUint8Data(*handleEcmaStrU8No1, &arrayU8No1[0], lengthEcmaStrU8No1, false)); - EXPECT_FALSE(EcmaStringAccessor::StringsAreEqualUtf8(*handleEcmaStrU8No2, &arrayU8No1[0], lengthEcmaStrU8No1, + EXPECT_FALSE(EcmaStringAccessor::StringIsEqualUint8Data(*handleEcmaStrU8No2, &arrayU8No1[0], lengthEcmaStrU8No1, true)); - EXPECT_FALSE(EcmaStringAccessor::StringsAreEqualUtf8(*handleEcmaStrU8No3, &arrayU8No1[0], lengthEcmaStrU8No1, + EXPECT_FALSE(EcmaStringAccessor::StringIsEqualUint8Data(*handleEcmaStrU8No3, &arrayU8No1[0], lengthEcmaStrU8No1, true)); } diff --git a/ecmascript/tests/dump_test.cpp b/ecmascript/tests/dump_test.cpp index 5eab10144c5c75643cb2302acf87caeb63d9006d..b6ec9fcfd0d908ea1f3fc274f1b7808b648e597b 100644 --- a/ecmascript/tests/dump_test.cpp +++ b/ecmascript/tests/dump_test.cpp @@ -103,6 +103,7 @@ #include "ecmascript/layout_info-inl.h" #include "ecmascript/lexical_env.h" #include "ecmascript/linked_hash_table.h" +#include "ecmascript/marker_cell.h" #include "ecmascript/mem/assert_scope.h" #include "ecmascript/mem/c_containers.h" #include "ecmascript/mem/machine_code.h" @@ -394,19 +395,30 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) auto globalEnv = thread->GetEcmaVM()->GetGlobalEnv(); auto globalConst = const_cast(thread->GlobalConstants()); JSHandle proto = globalEnv->GetFunctionPrototype(); - std::vector> snapshotVector; + std::vector snapshotVector; std::ostringstream os; -#define DUMP_FOR_HANDLE(dumpHandle) \ - dumpHandle.GetTaggedValue().Dump(os); \ - dumpHandle.GetTaggedValue().DumpForSnapshot(snapshotVector); +#define DUMP_FOR_HANDLE(dumpHandle) \ + do { \ + JSTaggedValue dumpValue = dumpHandle.GetTaggedValue(); \ + dumpValue.Dump(os); \ + dumpValue.DumpForSnapshot(snapshotVector); \ + /* Testing runtime stubs: */ \ + JSTaggedType dumpRawData = dumpValue.GetRawData(); \ + uintptr_t hintStr = reinterpret_cast("Testing with: " #dumpHandle); \ + RuntimeStubs::Dump(dumpRawData); \ + RuntimeStubs::DebugDump(dumpRawData); \ + RuntimeStubs::DumpWithHint(hintStr, dumpRawData); \ + RuntimeStubs::DebugDumpWithHint(hintStr, dumpRawData); \ + } while (false) -#define NEW_OBJECT_AND_DUMP(ClassName, TypeName) \ - JSHandle class##ClassName = \ - factory->NewEcmaHClass(ClassName::SIZE, JSType::TypeName, proto); \ - JSHandle object##ClassName = factory->NewJSObjectWithInit(class##ClassName); \ - object##ClassName.GetTaggedValue().Dump(os); \ - object##ClassName.GetTaggedValue().DumpForSnapshot(snapshotVector); +#define NEW_OBJECT_AND_DUMP(ClassName, TypeName) \ + do { \ + JSHandle class##ClassName = \ + factory->NewEcmaHClass(ClassName::SIZE, JSType::TypeName, proto); \ + JSHandle object##ClassName = factory->NewJSObjectWithInit(class##ClassName); \ + DUMP_FOR_HANDLE(object##ClassName); \ + } while (false) for (JSType type = JSType::JS_OBJECT; type <= JSType::TYPE_LAST; type = JSType(static_cast(type) + 1)) { switch (type) { @@ -423,20 +435,20 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) case JSType::JS_OBJECT: { CHECK_DUMP_FIELDS(ECMAObject::SIZE, JSObject::SIZE, 2U); JSHandle jsObj = NewJSObject(thread, factory, globalEnv); - DUMP_FOR_HANDLE(jsObj) + DUMP_FOR_HANDLE(jsObj); break; } case JSType::JS_REALM: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSRealm::SIZE, 2U); JSHandle jsRealm = factory->NewJSRealm(); - DUMP_FOR_HANDLE(jsRealm) + DUMP_FOR_HANDLE(jsRealm); break; } case JSType::METHOD: { #ifdef PANDA_TARGET_64 - CHECK_DUMP_FIELDS(TaggedObject::TaggedObjectSize(), Method::SIZE, 7U); + CHECK_DUMP_FIELDS(TaggedObject::TaggedObjectSize(), Method::SIZE, 8U); #else - CHECK_DUMP_FIELDS(TaggedObject::TaggedObjectSize(), Method::SIZE, 6U); + CHECK_DUMP_FIELDS(TaggedObject::TaggedObjectSize(), Method::SIZE, 7U); #endif break; } @@ -445,9 +457,9 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) break; } case JSType::JS_FUNCTION: { - CHECK_DUMP_FIELDS(JSFunctionBase::SIZE, JSFunction::SIZE, 4U); + CHECK_DUMP_FIELDS(JSFunctionBase::SIZE, JSFunction::SIZE, 3U); JSHandle jsFunc = globalEnv->GetFunctionFunction(); - DUMP_FOR_HANDLE(jsFunc) + DUMP_FOR_HANDLE(jsFunc); break; } case JSType::JS_PROXY_REVOC_FUNCTION: { @@ -455,7 +467,7 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) JSHandle proxyRevocClass = JSHandle::Cast(globalEnv->GetProxyRevocFunctionClass()); JSHandle proxyRevocFunc = factory->NewJSObjectWithInit(proxyRevocClass); - DUMP_FOR_HANDLE(proxyRevocFunc) + DUMP_FOR_HANDLE(proxyRevocFunc); break; } case JSType::JS_PROMISE_REACTIONS_FUNCTION: { @@ -463,7 +475,7 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) JSHandle promiseReactClass = JSHandle::Cast(globalEnv->GetPromiseReactionFunctionClass()); JSHandle promiseReactFunc = factory->NewJSObjectWithInit(promiseReactClass); - DUMP_FOR_HANDLE(promiseReactFunc) + DUMP_FOR_HANDLE(promiseReactFunc); break; } case JSType::JS_PROMISE_EXECUTOR_FUNCTION: { @@ -471,7 +483,7 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) JSHandle promiseExeClass = JSHandle::Cast(globalEnv->GetPromiseExecutorFunctionClass()); JSHandle promiseExeFunc = factory->NewJSObjectWithInit(promiseExeClass); - DUMP_FOR_HANDLE(promiseExeFunc) + DUMP_FOR_HANDLE(promiseExeFunc); break; } case JSType::JS_PROMISE_ALL_RESOLVE_ELEMENT_FUNCTION: { @@ -479,7 +491,7 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) JSHandle promiseAllClass = JSHandle::Cast(globalEnv->GetPromiseAllResolveElementFunctionClass()); JSHandle promiseAllFunc = factory->NewJSObjectWithInit(promiseAllClass); - DUMP_FOR_HANDLE(promiseAllFunc) + DUMP_FOR_HANDLE(promiseAllFunc); break; } case JSType::JS_PROMISE_ANY_REJECT_ELEMENT_FUNCTION: { @@ -487,7 +499,7 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) JSHandle promiseAnyClass = JSHandle::Cast(globalEnv->GetPromiseAnyRejectElementFunctionClass()); JSHandle promiseAnyFunc = factory->NewJSObjectWithInit(promiseAnyClass); - DUMP_FOR_HANDLE(promiseAnyFunc) + DUMP_FOR_HANDLE(promiseAnyFunc); break; } case JSType::JS_PROMISE_ALL_SETTLED_ELEMENT_FUNCTION: { @@ -495,7 +507,7 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) JSHandle promiseAllSettledClass = JSHandle::Cast(globalEnv->GetPromiseAllSettledElementFunctionClass()); JSHandle promiseAllSettledFunc = factory->NewJSObjectWithInit(promiseAllSettledClass); - DUMP_FOR_HANDLE(promiseAllSettledFunc) + DUMP_FOR_HANDLE(promiseAllSettledFunc); break; } case JSType::JS_PROMISE_FINALLY_FUNCTION: { @@ -503,7 +515,7 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) JSHandle promiseFinallyClass = JSHandle::Cast(globalEnv->GetPromiseFinallyFunctionClass()); JSHandle promiseFinallyFunc = factory->NewJSObjectWithInit(promiseFinallyClass); - DUMP_FOR_HANDLE(promiseFinallyFunc) + DUMP_FOR_HANDLE(promiseFinallyFunc); break; } case JSType::JS_PROMISE_VALUE_THUNK_OR_THROWER_FUNCTION: { @@ -511,7 +523,7 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) JSHandle promiseValueClass = JSHandle::Cast(globalEnv->GetPromiseValueThunkOrThrowerFunctionClass()); JSHandle promiseValueFunc = factory->NewJSObjectWithInit(promiseValueClass); - DUMP_FOR_HANDLE(promiseValueFunc) + DUMP_FOR_HANDLE(promiseValueFunc); break; } case JSType::JS_ASYNC_GENERATOR_FUNCTION: { @@ -530,36 +542,36 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) CHECK_DUMP_FIELDS(JSFunction::SIZE, JSIntlBoundFunction::SIZE, 3U); JSHandle intlBoundFunc = factory->NewJSIntlBoundFunction( MethodIndex::BUILTINS_NUMBER_FORMAT_NUMBER_FORMAT_INTERNAL_FORMAT_NUMBER); - DUMP_FOR_HANDLE(intlBoundFunc) + DUMP_FOR_HANDLE(intlBoundFunc); break; } case JSType::JS_ASYNC_AWAIT_STATUS_FUNCTION: { CHECK_DUMP_FIELDS(JSFunction::SIZE, JSAsyncAwaitStatusFunction::SIZE, 1U); JSHandle asyncAwaitFunc = factory->NewJSAsyncAwaitStatusFunction( MethodIndex::BUILTINS_PROMISE_HANDLER_ASYNC_AWAIT_FULFILLED); - DUMP_FOR_HANDLE(asyncAwaitFunc) + DUMP_FOR_HANDLE(asyncAwaitFunc); break; } case JSType::JS_BOUND_FUNCTION: { CHECK_DUMP_FIELDS(JSFunctionBase::SIZE, JSBoundFunction::SIZE, 3U); - NEW_OBJECT_AND_DUMP(JSBoundFunction, JS_BOUND_FUNCTION) + NEW_OBJECT_AND_DUMP(JSBoundFunction, JS_BOUND_FUNCTION); break; } case JSType::JS_REG_EXP: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSRegExp::SIZE, 5U); - NEW_OBJECT_AND_DUMP(JSRegExp, JS_REG_EXP) + NEW_OBJECT_AND_DUMP(JSRegExp, JS_REG_EXP); break; } case JSType::JS_SET: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSSet::SIZE, 1U); JSHandle jsSet = NewJSSet(thread, factory, proto); - DUMP_FOR_HANDLE(jsSet) + DUMP_FOR_HANDLE(jsSet); break; } case JSType::JS_MAP: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSMap::SIZE, 1U); JSHandle jsMap = NewJSMap(thread, factory, proto); - DUMP_FOR_HANDLE(jsMap) + DUMP_FOR_HANDLE(jsMap); break; } case JSType::JS_WEAK_MAP: { @@ -568,7 +580,7 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) JSHandle jsWeakMap = JSHandle::Cast(factory->NewJSObjectWithInit(weakMapClass)); JSHandle weakLinkedMap(LinkedHashMap::Create(thread)); jsWeakMap->SetLinkedMap(thread, weakLinkedMap); - DUMP_FOR_HANDLE(jsWeakMap) + DUMP_FOR_HANDLE(jsWeakMap); break; } case JSType::JS_WEAK_SET: { @@ -577,7 +589,7 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) JSHandle jsWeakSet = JSHandle::Cast(factory->NewJSObjectWithInit(weakSetClass)); JSHandle weakLinkedSet(LinkedHashSet::Create(thread)); jsWeakSet->SetLinkedSet(thread, weakLinkedSet); - DUMP_FOR_HANDLE(jsWeakSet) + DUMP_FOR_HANDLE(jsWeakSet); break; } case JSType::JS_WEAK_REF: { @@ -585,7 +597,7 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) JSHandle weakRefClass = factory->NewEcmaHClass(JSWeakRef::SIZE, JSType::JS_WEAK_REF, proto); JSHandle jsWeakRef = JSHandle::Cast(factory->NewJSObjectWithInit(weakRefClass)); jsWeakRef->SetWeakObject(thread, JSTaggedValue::Undefined()); - DUMP_FOR_HANDLE(jsWeakRef) + DUMP_FOR_HANDLE(jsWeakRef); break; } case JSType::JS_FINALIZATION_REGISTRY: { @@ -596,13 +608,13 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) JSHandle::Cast(factory->NewJSObjectWithInit(finalizationRegistryClass)); JSHandle weakLinkedMap(LinkedHashMap::Create(thread)); jsFinalizationRegistry->SetMaybeUnregister(thread, weakLinkedMap); - DUMP_FOR_HANDLE(jsFinalizationRegistry) + DUMP_FOR_HANDLE(jsFinalizationRegistry); break; } case JSType::CELL_RECORD: { CHECK_DUMP_FIELDS(Record::SIZE, CellRecord::SIZE, 2U); JSHandle cellRecord = factory->NewCellRecord(); - DUMP_FOR_HANDLE(cellRecord) + DUMP_FOR_HANDLE(cellRecord); break; } case JSType::JS_DATE: { @@ -611,28 +623,28 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) JSHandle date = JSHandle::Cast(factory->NewJSObjectWithInit(dateClass)); date->SetTimeValue(thread, JSTaggedValue(0.0)); date->SetLocalOffset(thread, JSTaggedValue(0.0)); - DUMP_FOR_HANDLE(date) + DUMP_FOR_HANDLE(date); break; } case JSType::JS_FORIN_ITERATOR: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSForInIterator::SIZE, 4U); JSHandle array(thread, factory->NewJSArray().GetTaggedValue()); JSHandle forInIter = factory->NewJSForinIterator(array); - DUMP_FOR_HANDLE(forInIter) + DUMP_FOR_HANDLE(forInIter); break; } case JSType::JS_MAP_ITERATOR: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSMapIterator::SIZE, 2U); JSHandle jsMapIter = factory->NewJSMapIterator(NewJSMap(thread, factory, proto), IterationKind::KEY); - DUMP_FOR_HANDLE(jsMapIter) + DUMP_FOR_HANDLE(jsMapIter); break; } case JSType::JS_SET_ITERATOR: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSSetIterator::SIZE, 2U); JSHandle jsSetIter = factory->NewJSSetIterator(NewJSSet(thread, factory, proto), IterationKind::KEY); - DUMP_FOR_HANDLE(jsSetIter) + DUMP_FOR_HANDLE(jsSetIter); break; } case JSType::JS_REG_EXP_ITERATOR: { @@ -641,103 +653,103 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) JSHandle jsRegExp(NewJSRegExp(thread, factory, proto)); JSHandle jsRegExpIter = factory->NewJSRegExpIterator(jsRegExp, emptyString, false, false); - DUMP_FOR_HANDLE(jsRegExpIter) + DUMP_FOR_HANDLE(jsRegExpIter); break; } case JSType::JS_ARRAY_ITERATOR: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSArrayIterator::SIZE, 2U); JSHandle arrayIter = factory->NewJSArrayIterator(JSHandle::Cast(factory->NewJSArray()), IterationKind::KEY); - DUMP_FOR_HANDLE(arrayIter) + DUMP_FOR_HANDLE(arrayIter); break; } case JSType::JS_STRING_ITERATOR: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSStringIterator::SIZE, 2U); JSHandle stringIter = globalEnv->GetStringIterator(); - DUMP_FOR_HANDLE(stringIter) + DUMP_FOR_HANDLE(stringIter); break; } case JSType::JS_INTL: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSIntl::SIZE, 1U); - NEW_OBJECT_AND_DUMP(JSIntl, JS_INTL) + NEW_OBJECT_AND_DUMP(JSIntl, JS_INTL); break; } case JSType::JS_LOCALE: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSLocale::SIZE, 1U); - NEW_OBJECT_AND_DUMP(JSLocale, JS_LOCALE) + NEW_OBJECT_AND_DUMP(JSLocale, JS_LOCALE); break; } case JSType::JS_DATE_TIME_FORMAT: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSDateTimeFormat::SIZE, 9U); - NEW_OBJECT_AND_DUMP(JSDateTimeFormat, JS_DATE_TIME_FORMAT) + NEW_OBJECT_AND_DUMP(JSDateTimeFormat, JS_DATE_TIME_FORMAT); break; } case JSType::JS_RELATIVE_TIME_FORMAT: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSRelativeTimeFormat::SIZE, 4U); - NEW_OBJECT_AND_DUMP(JSRelativeTimeFormat, JS_RELATIVE_TIME_FORMAT) + NEW_OBJECT_AND_DUMP(JSRelativeTimeFormat, JS_RELATIVE_TIME_FORMAT); break; } case JSType::JS_NUMBER_FORMAT: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSNumberFormat::SIZE, 13U); - NEW_OBJECT_AND_DUMP(JSNumberFormat, JS_NUMBER_FORMAT) + NEW_OBJECT_AND_DUMP(JSNumberFormat, JS_NUMBER_FORMAT); break; } case JSType::JS_COLLATOR: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSCollator::SIZE, 5U); - NEW_OBJECT_AND_DUMP(JSCollator, JS_COLLATOR) + NEW_OBJECT_AND_DUMP(JSCollator, JS_COLLATOR); break; } case JSType::JS_PLURAL_RULES: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSPluralRules::SIZE, 9U); - NEW_OBJECT_AND_DUMP(JSPluralRules, JS_PLURAL_RULES) + NEW_OBJECT_AND_DUMP(JSPluralRules, JS_PLURAL_RULES); break; } case JSType::JS_DISPLAYNAMES: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSDisplayNames::SIZE, 3U); - NEW_OBJECT_AND_DUMP(JSDisplayNames, JS_DISPLAYNAMES) + NEW_OBJECT_AND_DUMP(JSDisplayNames, JS_DISPLAYNAMES); break; } case JSType::JS_LIST_FORMAT: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSListFormat::SIZE, 3U); - NEW_OBJECT_AND_DUMP(JSListFormat, JS_LIST_FORMAT) + NEW_OBJECT_AND_DUMP(JSListFormat, JS_LIST_FORMAT); break; } case JSType::JS_SHARED_ARRAY_BUFFER: case JSType::JS_ARRAY_BUFFER: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSArrayBuffer::SIZE, 2U); - NEW_OBJECT_AND_DUMP(JSArrayBuffer, JS_ARRAY_BUFFER) + NEW_OBJECT_AND_DUMP(JSArrayBuffer, JS_ARRAY_BUFFER); break; } case JSType::JS_PROMISE: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSPromise::SIZE, 4U); - NEW_OBJECT_AND_DUMP(JSPromise, JS_PROMISE) + NEW_OBJECT_AND_DUMP(JSPromise, JS_PROMISE); break; } case JSType::JS_DATA_VIEW: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSDataView::SIZE, 3U); - NEW_OBJECT_AND_DUMP(JSDataView, JS_DATA_VIEW) + NEW_OBJECT_AND_DUMP(JSDataView, JS_DATA_VIEW); break; } case JSType::JS_GENERATOR_OBJECT: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSGeneratorObject::SIZE, 3U); - NEW_OBJECT_AND_DUMP(JSGeneratorObject, JS_GENERATOR_OBJECT) + NEW_OBJECT_AND_DUMP(JSGeneratorObject, JS_GENERATOR_OBJECT); break; } case JSType::JS_ASYNC_GENERATOR_OBJECT: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSAsyncGeneratorObject::SIZE, 5U); - NEW_OBJECT_AND_DUMP(JSAsyncGeneratorObject, JS_ASYNC_GENERATOR_OBJECT) + NEW_OBJECT_AND_DUMP(JSAsyncGeneratorObject, JS_ASYNC_GENERATOR_OBJECT); break; } case JSType::JS_ASYNC_FUNC_OBJECT: { CHECK_DUMP_FIELDS(JSGeneratorObject::SIZE, JSAsyncFuncObject::SIZE, 1U); JSHandle asyncFuncObject = factory->NewJSAsyncFuncObject(); - DUMP_FOR_HANDLE(asyncFuncObject) + DUMP_FOR_HANDLE(asyncFuncObject); break; } case JSType::JS_ARRAY: { - CHECK_DUMP_FIELDS(JSObject::SIZE, JSArray::SIZE, 1U); + CHECK_DUMP_FIELDS(JSObject::SIZE, JSArray::SIZE, 2U); JSHandle jsArray = factory->NewJSArray(); - DUMP_FOR_HANDLE(jsArray) + DUMP_FOR_HANDLE(jsArray); break; } case JSType::JS_TYPED_ARRAY: @@ -753,41 +765,42 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) case JSType::JS_BIGINT64_ARRAY: case JSType::JS_BIGUINT64_ARRAY: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSTypedArray::SIZE, 4U); - NEW_OBJECT_AND_DUMP(JSTypedArray, JS_TYPED_ARRAY) + NEW_OBJECT_AND_DUMP(JSTypedArray, JS_TYPED_ARRAY); break; } case JSType::JS_PRIMITIVE_REF: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSPrimitiveRef::SIZE, 1U); - NEW_OBJECT_AND_DUMP(JSPrimitiveRef, JS_PRIMITIVE_REF) + NEW_OBJECT_AND_DUMP(JSPrimitiveRef, JS_PRIMITIVE_REF); break; } case JSType::JS_GLOBAL_OBJECT: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSGlobalObject::SIZE, 0U); JSHandle globalObject = globalEnv->GetJSGlobalObject(); - DUMP_FOR_HANDLE(globalObject) + DUMP_FOR_HANDLE(globalObject); break; } case JSType::JS_PROXY: { - CHECK_DUMP_FIELDS(ECMAObject::SIZE, JSProxy::SIZE, 3U); + CHECK_DUMP_FIELDS(ECMAObject::SIZE, JSProxy::SIZE, 4U); JSHandle emptyObj(thread, NewJSObject(thread, factory, globalEnv).GetTaggedValue()); JSHandle proxy = factory->NewJSProxy(emptyObj, emptyObj); - DUMP_FOR_HANDLE(proxy) + DUMP_FOR_HANDLE(proxy); break; } case JSType::HCLASS: { - CHECK_DUMP_FIELDS(TaggedObject::TaggedObjectSize(), JSHClass::SIZE, 9U); + CHECK_DUMP_FIELDS(TaggedObject::TaggedObjectSize(), JSHClass::SIZE, 10U); JSHandle hclass = factory->NewEcmaHClass(JSHClass::SIZE, JSType::HCLASS, proto); - DUMP_FOR_HANDLE(hclass) + DUMP_FOR_HANDLE(hclass); break; } case JSType::LINE_STRING: case JSType::CONSTANT_STRING: - case JSType::TREE_STRING: { - DUMP_FOR_HANDLE(globalEnv->GetObjectFunction()) + case JSType::TREE_STRING: + case JSType::SLICED_STRING: { + DUMP_FOR_HANDLE(globalEnv->GetObjectFunction()); break; } case JSType::BIGINT: { - DUMP_FOR_HANDLE(globalEnv->GetBigIntFunction()) + DUMP_FOR_HANDLE(globalEnv->GetBigIntFunction()); break; } case JSType::TAGGED_ARRAY: @@ -795,127 +808,142 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) case JSType::LEXICAL_ENV: case JSType::AOT_LITERAL_INFO: { JSHandle taggedArray = factory->NewTaggedArray(4); - DUMP_FOR_HANDLE(taggedArray) + DUMP_FOR_HANDLE(taggedArray); break; } case JSType::CONSTANT_POOL: { JSHandle constantPool = factory->NewConstantPool(4); - DUMP_FOR_HANDLE(constantPool) + DUMP_FOR_HANDLE(constantPool); + break; + } + case JSType::PROFILE_TYPE_INFO: { + JSHandle info = factory->NewProfileTypeInfo(4); + DUMP_FOR_HANDLE(info); break; } case JSType::TAGGED_DICTIONARY: { JSHandle dict = factory->NewDictionaryArray(4); - DUMP_FOR_HANDLE(dict) + DUMP_FOR_HANDLE(dict); break; } case JSType::BYTE_ARRAY: { JSHandle byteArray = factory->NewByteArray(4, 8); - DUMP_FOR_HANDLE(byteArray) + DUMP_FOR_HANDLE(byteArray); break; } case JSType::COW_TAGGED_ARRAY: { JSHandle dict = factory->NewCOWTaggedArray(4); - DUMP_FOR_HANDLE(dict) + DUMP_FOR_HANDLE(dict); break; } case JSType::GLOBAL_ENV: { - DUMP_FOR_HANDLE(globalEnv) + DUMP_FOR_HANDLE(globalEnv); break; } case JSType::ACCESSOR_DATA: case JSType::INTERNAL_ACCESSOR: { CHECK_DUMP_FIELDS(Record::SIZE, AccessorData::SIZE, 2U); JSHandle accessor = factory->NewAccessorData(); - DUMP_FOR_HANDLE(accessor) + DUMP_FOR_HANDLE(accessor); break; } case JSType::SYMBOL: { CHECK_DUMP_FIELDS(TaggedObject::TaggedObjectSize(), JSSymbol::SIZE, 2U); JSHandle symbol = factory->NewJSSymbol(); - DUMP_FOR_HANDLE(symbol) + DUMP_FOR_HANDLE(symbol); break; } case JSType::JS_GENERATOR_CONTEXT: { CHECK_DUMP_FIELDS(TaggedObject::TaggedObjectSize(), GeneratorContext::SIZE, 7U); JSHandle genContext = factory->NewGeneratorContext(); - DUMP_FOR_HANDLE(genContext) + DUMP_FOR_HANDLE(genContext); break; } case JSType::PROTOTYPE_HANDLER: { CHECK_DUMP_FIELDS(TaggedObject::TaggedObjectSize(), PrototypeHandler::SIZE, 3U); JSHandle protoHandler = factory->NewPrototypeHandler(); - DUMP_FOR_HANDLE(protoHandler) + DUMP_FOR_HANDLE(protoHandler); break; } case JSType::TRANSITION_HANDLER: { CHECK_DUMP_FIELDS(TaggedObject::TaggedObjectSize(), TransitionHandler::SIZE, 2U); JSHandle transitionHandler = factory->NewTransitionHandler(); - DUMP_FOR_HANDLE(transitionHandler) + DUMP_FOR_HANDLE(transitionHandler); break; } case JSType::TRANS_WITH_PROTO_HANDLER: { CHECK_DUMP_FIELDS(TaggedObject::TaggedObjectSize(), TransWithProtoHandler::SIZE, 3U); JSHandle transWithProtoHandler = factory->NewTransWithProtoHandler(); - DUMP_FOR_HANDLE(transWithProtoHandler) + DUMP_FOR_HANDLE(transWithProtoHandler); break; } case JSType::STORE_TS_HANDLER: { CHECK_DUMP_FIELDS(TaggedObject::TaggedObjectSize(), StoreTSHandler::SIZE, 3U); JSHandle storeTSHandler = factory->NewStoreTSHandler(); - DUMP_FOR_HANDLE(storeTSHandler) + DUMP_FOR_HANDLE(storeTSHandler); break; } case JSType::PROPERTY_BOX: { CHECK_DUMP_FIELDS(TaggedObject::TaggedObjectSize(), PropertyBox::SIZE, 1U); JSHandle PropertyBox = factory->NewPropertyBox(globalConst->GetHandledEmptyArray()); - DUMP_FOR_HANDLE(PropertyBox) + DUMP_FOR_HANDLE(PropertyBox); break; } case JSType::PROTO_CHANGE_MARKER: { CHECK_DUMP_FIELDS(TaggedObject::TaggedObjectSize(), ProtoChangeMarker::SIZE, 1U); JSHandle protoMaker = factory->NewProtoChangeMarker(); - DUMP_FOR_HANDLE(protoMaker) + DUMP_FOR_HANDLE(protoMaker); + break; + } + case JSType::MARKER_CELL: { + CHECK_DUMP_FIELDS(TaggedObject::TaggedObjectSize(), MarkerCell::SIZE, 1U); + JSHandle markerCell = factory->NewMarkerCell(); + DUMP_FOR_HANDLE(markerCell); + break; + } + case JSType::TRACK_INFO: { + CHECK_DUMP_FIELDS(TaggedObject::TaggedObjectSize(), TrackInfo::SIZE, 3U); break; } case JSType::PROTOTYPE_INFO: { CHECK_DUMP_FIELDS(TaggedObject::TaggedObjectSize(), ProtoChangeDetails::SIZE, 2U); JSHandle protoDetails = factory->NewProtoChangeDetails(); - DUMP_FOR_HANDLE(protoDetails) + DUMP_FOR_HANDLE(protoDetails); break; } case JSType::TEMPLATE_MAP: { JSHandle templateMap = globalEnv->GetTemplateMap(); - DUMP_FOR_HANDLE(templateMap) + DUMP_FOR_HANDLE(templateMap); break; } case JSType::PROGRAM: { CHECK_DUMP_FIELDS(ECMAObject::SIZE, Program::SIZE, 1U); JSHandle program = factory->NewProgram(); - DUMP_FOR_HANDLE(program) + DUMP_FOR_HANDLE(program); break; } case JSType::PROMISE_CAPABILITY: { CHECK_DUMP_FIELDS(Record::SIZE, PromiseCapability::SIZE, 3U); JSHandle promiseCapa = factory->NewPromiseCapability(); - DUMP_FOR_HANDLE(promiseCapa) + DUMP_FOR_HANDLE(promiseCapa); break; } case JSType::PROMISE_RECORD: { CHECK_DUMP_FIELDS(Record::SIZE, PromiseRecord::SIZE, 1U); JSHandle promiseRecord = factory->NewPromiseRecord(); - DUMP_FOR_HANDLE(promiseRecord) + DUMP_FOR_HANDLE(promiseRecord); break; } case JSType::RESOLVING_FUNCTIONS_RECORD: { CHECK_DUMP_FIELDS(Record::SIZE, ResolvingFunctionsRecord::SIZE, 2U); JSHandle ResolvingFunc = factory->NewResolvingFunctionsRecord(); - DUMP_FOR_HANDLE(ResolvingFunc) + DUMP_FOR_HANDLE(ResolvingFunc); break; } case JSType::ASYNC_GENERATOR_REQUEST: { CHECK_DUMP_FIELDS(Record::SIZE, AsyncGeneratorRequest::SIZE, 2U); JSHandle asyncGeneratorRequest = factory->NewAsyncGeneratorRequest(); - DUMP_FOR_HANDLE(asyncGeneratorRequest) + DUMP_FOR_HANDLE(asyncGeneratorRequest); break; } case JSType::ASYNC_ITERATOR_RECORD: { @@ -924,12 +952,12 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) JSHandle emptyMethod(thread, NewJSObject(thread, factory, globalEnv).GetTaggedValue()); JSHandle asyncIteratorRecord = factory->NewAsyncIteratorRecord(emptyObj, emptyMethod, false); - DUMP_FOR_HANDLE(asyncIteratorRecord) + DUMP_FOR_HANDLE(asyncIteratorRecord); break; } case JSType::JS_ASYNC_FROM_SYNC_ITERATOR: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSAsyncFromSyncIterator::SIZE, 1U); - NEW_OBJECT_AND_DUMP(JSAsyncFromSyncIterator, JS_ASYNC_FROM_SYNC_ITERATOR) + NEW_OBJECT_AND_DUMP(JSAsyncFromSyncIterator, JS_ASYNC_FROM_SYNC_ITERATOR); break; } case JSType::JS_ASYNC_FROM_SYNC_ITER_UNWARP_FUNCTION: { @@ -939,20 +967,20 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) case JSType::PROMISE_REACTIONS: { CHECK_DUMP_FIELDS(Record::SIZE, PromiseReaction::SIZE, 3U); JSHandle promiseReact = factory->NewPromiseReaction(); - DUMP_FOR_HANDLE(promiseReact) + DUMP_FOR_HANDLE(promiseReact); break; } case JSType::PROMISE_ITERATOR_RECORD: { CHECK_DUMP_FIELDS(Record::SIZE, PromiseIteratorRecord::SIZE, 2U); JSHandle emptyObj(thread, NewJSObject(thread, factory, globalEnv).GetTaggedValue()); JSHandle promiseIter = factory->NewPromiseIteratorRecord(emptyObj, false); - DUMP_FOR_HANDLE(promiseIter) + DUMP_FOR_HANDLE(promiseIter); break; } case JSType::MICRO_JOB_QUEUE: { CHECK_DUMP_FIELDS(Record::SIZE, ecmascript::job::MicroJobQueue::SIZE, 2U); JSHandle microJob = factory->NewMicroJobQueue(); - DUMP_FOR_HANDLE(microJob) + DUMP_FOR_HANDLE(microJob); break; } case JSType::PENDING_JOB: { @@ -966,14 +994,14 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) JSHandle pendingJob(thread, factory->NewObject(pendingClass)); ecmascript::job::PendingJob::Cast(*pendingJob)->SetJob(thread, JSTaggedValue::Undefined()); ecmascript::job::PendingJob::Cast(*pendingJob)->SetArguments(thread, JSTaggedValue::Undefined()); - DUMP_FOR_HANDLE(pendingJob) + DUMP_FOR_HANDLE(pendingJob); break; } case JSType::COMPLETION_RECORD: { CHECK_DUMP_FIELDS(Record::SIZE, CompletionRecord::SIZE, 2U); JSHandle comRecord = factory->NewCompletionRecord(CompletionRecordType::NORMAL, globalConst->GetHandledEmptyArray()); - DUMP_FOR_HANDLE(comRecord) + DUMP_FOR_HANDLE(comRecord); break; } case JSType::MACHINE_CODE_OBJECT: { @@ -985,68 +1013,68 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) CHECK_DUMP_FIELDS(TaggedObject::TaggedObjectSize(), ClassInfoExtractor::SIZE, 8U); JSHandle classInfoExtractor = factory->NewClassInfoExtractor( JSHandle(thread, JSTaggedValue::Undefined())); - DUMP_FOR_HANDLE(classInfoExtractor) + DUMP_FOR_HANDLE(classInfoExtractor); break; } case JSType::TS_OBJECT_TYPE: { CHECK_DUMP_FIELDS(TaggedObject::TaggedObjectSize(), TSObjectType::SIZE, 3U); JSHandle objectType = factory->NewTSObjectType(0); - DUMP_FOR_HANDLE(objectType) + DUMP_FOR_HANDLE(objectType); break; } case JSType::TS_CLASS_TYPE: { CHECK_DUMP_FIELDS(TaggedObject::TaggedObjectSize(), TSClassType::SIZE, 7U); JSHandle classType = factory->NewTSClassType(); - DUMP_FOR_HANDLE(classType) + DUMP_FOR_HANDLE(classType); break; } case JSType::TS_INTERFACE_TYPE: { CHECK_DUMP_FIELDS(TaggedObject::TaggedObjectSize(), TSInterfaceType::SIZE, 4U); JSHandle interfaceType = factory->NewTSInterfaceType(); - DUMP_FOR_HANDLE(interfaceType) + DUMP_FOR_HANDLE(interfaceType); break; } case JSType::TS_CLASS_INSTANCE_TYPE: { CHECK_DUMP_FIELDS(TaggedObject::TaggedObjectSize(), TSClassInstanceType::SIZE, 2U); JSHandle classInstanceType = factory->NewTSClassInstanceType(); - DUMP_FOR_HANDLE(classInstanceType) + DUMP_FOR_HANDLE(classInstanceType); break; } case JSType::TS_UNION_TYPE: { CHECK_DUMP_FIELDS(TaggedObject::TaggedObjectSize(), TSUnionType::SIZE, 2U); JSHandle unionType = factory->NewTSUnionType(1); - DUMP_FOR_HANDLE(unionType) + DUMP_FOR_HANDLE(unionType); break; } case JSType::TS_FUNCTION_TYPE: { CHECK_DUMP_FIELDS(TaggedObject::TaggedObjectSize(), TSFunctionType::SIZE, 5U); JSHandle functionType = factory->NewTSFunctionType(1); - DUMP_FOR_HANDLE(functionType) + DUMP_FOR_HANDLE(functionType); break; } case JSType::TS_ARRAY_TYPE: { CHECK_DUMP_FIELDS(TaggedObject::TaggedObjectSize(), TSArrayType::SIZE, 2U); JSHandle arrayType = factory->NewTSArrayType(); - DUMP_FOR_HANDLE(arrayType) + DUMP_FOR_HANDLE(arrayType); break; } case JSType::TS_ITERATOR_INSTANCE_TYPE: { CHECK_DUMP_FIELDS(TaggedObject::TaggedObjectSize(), TSIteratorInstanceType::SIZE, 2U); JSHandle iteratorInstanceType = factory->NewTSIteratorInstanceType(); - DUMP_FOR_HANDLE(iteratorInstanceType) + DUMP_FOR_HANDLE(iteratorInstanceType); break; } case JSType::TS_NAMESPACE_TYPE: { CHECK_DUMP_FIELDS(TaggedObject::TaggedObjectSize(), TSNamespaceType::SIZE, 2U); JSHandle namespaceType = factory->NewTSNamespaceType(); - DUMP_FOR_HANDLE(namespaceType) + DUMP_FOR_HANDLE(namespaceType); break; } case JSType::JS_API_ARRAY_LIST: { // 1 : 1 dump fileds number CHECK_DUMP_FIELDS(JSObject::SIZE, JSAPIArrayList::SIZE, 1U); JSHandle jsArrayList = NewJSAPIArrayList(thread, factory, proto); - DUMP_FOR_HANDLE(jsArrayList) + DUMP_FOR_HANDLE(jsArrayList); break; } case JSType::JS_API_ARRAYLIST_ITERATOR: { @@ -1054,7 +1082,7 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) CHECK_DUMP_FIELDS(JSObject::SIZE, JSAPIArrayListIterator::SIZE, 2U); JSHandle jsArrayList = NewJSAPIArrayList(thread, factory, proto); JSHandle jsArrayListIter = factory->NewJSAPIArrayListIterator(jsArrayList); - DUMP_FOR_HANDLE(jsArrayListIter) + DUMP_FOR_HANDLE(jsArrayListIter); break; } case JSType::LINKED_NODE: { @@ -1068,13 +1096,13 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) case JSType::JS_API_HASH_MAP: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSAPIHashMap::SIZE, 2U); JSHandle jsHashMap = NewJSAPIHashMap(thread, factory); - DUMP_FOR_HANDLE(jsHashMap) + DUMP_FOR_HANDLE(jsHashMap); break; } case JSType::JS_API_HASH_SET: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSAPIHashSet::SIZE, 2U); JSHandle jsHashSet = NewJSAPIHashSet(thread, factory); - DUMP_FOR_HANDLE(jsHashSet) + DUMP_FOR_HANDLE(jsHashSet); break; } case JSType::JS_API_HASHMAP_ITERATOR: { @@ -1082,7 +1110,7 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) JSHandle jsHashMap = NewJSAPIHashMap(thread, factory); JSHandle jsHashMapIter = factory->NewJSAPIHashMapIterator(jsHashMap, IterationKind::KEY); - DUMP_FOR_HANDLE(jsHashMapIter) + DUMP_FOR_HANDLE(jsHashMapIter); break; } case JSType::JS_API_HASHSET_ITERATOR: { @@ -1090,13 +1118,13 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) JSHandle jsHashSet = NewJSAPIHashSet(thread, factory); JSHandle jsHashSetIter = factory->NewJSAPIHashSetIterator(jsHashSet, IterationKind::KEY); - DUMP_FOR_HANDLE(jsHashSetIter) + DUMP_FOR_HANDLE(jsHashSetIter); break; } case JSType::JS_API_LIGHT_WEIGHT_MAP: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSAPILightWeightMap::SIZE, 4U); JSHandle jSAPILightWeightMap = NewJSAPILightWeightMap(thread, factory); - DUMP_FOR_HANDLE(jSAPILightWeightMap) + DUMP_FOR_HANDLE(jSAPILightWeightMap); break; } case JSType::JS_API_LIGHT_WEIGHT_MAP_ITERATOR: { @@ -1104,13 +1132,13 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) JSHandle jSAPILightWeightMap = NewJSAPILightWeightMap(thread, factory); JSHandle jSAPILightWeightMapIterator = factory->NewJSAPILightWeightMapIterator(jSAPILightWeightMap, IterationKind::KEY); - DUMP_FOR_HANDLE(jSAPILightWeightMapIterator) + DUMP_FOR_HANDLE(jSAPILightWeightMapIterator); break; } case JSType::JS_API_LIGHT_WEIGHT_SET: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSAPILightWeightSet::SIZE, 3U); JSHandle jSAPILightWeightSet = NewJSAPILightWeightSet(thread, factory); - DUMP_FOR_HANDLE(jSAPILightWeightSet) + DUMP_FOR_HANDLE(jSAPILightWeightSet); break; } case JSType::JS_API_LIGHT_WEIGHT_SET_ITERATOR: { @@ -1118,14 +1146,14 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) JSHandle jSAPILightWeightSetIter = factory->NewJSAPILightWeightSetIterator(NewJSAPILightWeightSet(thread, factory), IterationKind::KEY); - DUMP_FOR_HANDLE(jSAPILightWeightSetIter) + DUMP_FOR_HANDLE(jSAPILightWeightSetIter); break; } case JSType::JS_API_QUEUE: { // 2 : 2 dump fileds number CHECK_DUMP_FIELDS(JSObject::SIZE, JSAPIQueue::SIZE, 2U); JSHandle jsQueue = NewJSAPIQueue(thread, factory, proto); - DUMP_FOR_HANDLE(jsQueue) + DUMP_FOR_HANDLE(jsQueue); break; } case JSType::JS_API_QUEUE_ITERATOR: { @@ -1134,13 +1162,13 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) JSHandle jsQueue = NewJSAPIQueue(thread, factory, proto); JSHandle jsQueueIter = factory->NewJSAPIQueueIterator(jsQueue); - DUMP_FOR_HANDLE(jsQueueIter) + DUMP_FOR_HANDLE(jsQueueIter); break; } case JSType::JS_API_PLAIN_ARRAY: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSAPIPlainArray::SIZE, 3U); JSHandle jSAPIPlainArray = NewJSAPIPlainArray(thread, factory); - DUMP_FOR_HANDLE(jSAPIPlainArray) + DUMP_FOR_HANDLE(jSAPIPlainArray); break; } case JSType::JS_API_PLAIN_ARRAY_ITERATOR: { @@ -1148,21 +1176,21 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) JSHandle jSAPIPlainArray = NewJSAPIPlainArray(thread, factory); JSHandle jSAPIPlainArrayIter = factory->NewJSAPIPlainArrayIterator(jSAPIPlainArray, IterationKind::KEY); - DUMP_FOR_HANDLE(jSAPIPlainArrayIter) + DUMP_FOR_HANDLE(jSAPIPlainArrayIter); break; } case JSType::JS_API_TREE_MAP: { // 1 : 1 dump fileds number CHECK_DUMP_FIELDS(JSObject::SIZE, JSAPITreeMap::SIZE, 1U); JSHandle jsTreeMap = NewJSAPITreeMap(thread, factory); - DUMP_FOR_HANDLE(jsTreeMap) + DUMP_FOR_HANDLE(jsTreeMap); break; } case JSType::JS_API_TREE_SET: { // 1 : 1 dump fileds number CHECK_DUMP_FIELDS(JSObject::SIZE, JSAPITreeSet::SIZE, 1U); JSHandle jsTreeSet = NewJSAPITreeSet(thread, factory); - DUMP_FOR_HANDLE(jsTreeSet) + DUMP_FOR_HANDLE(jsTreeSet); break; } case JSType::JS_API_TREEMAP_ITERATOR: { @@ -1171,7 +1199,7 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) JSHandle jsTreeMap = NewJSAPITreeMap(thread, factory); JSHandle jsTreeMapIter = factory->NewJSAPITreeMapIterator(jsTreeMap, IterationKind::KEY); - DUMP_FOR_HANDLE(jsTreeMapIter) + DUMP_FOR_HANDLE(jsTreeMapIter); break; } case JSType::JS_API_TREESET_ITERATOR: { @@ -1180,60 +1208,60 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) JSHandle jsTreeSet = NewJSAPITreeSet(thread, factory); JSHandle jsTreeSetIter = factory->NewJSAPITreeSetIterator(jsTreeSet, IterationKind::KEY); - DUMP_FOR_HANDLE(jsTreeSetIter) + DUMP_FOR_HANDLE(jsTreeSetIter); break; } case JSType::JS_API_DEQUE: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSAPIDeque::SIZE, 1U); JSHandle jsDeque = NewJSAPIDeque(thread, factory, proto); - DUMP_FOR_HANDLE(jsDeque) + DUMP_FOR_HANDLE(jsDeque); break; } case JSType::JS_API_DEQUE_ITERATOR: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSAPIDequeIterator::SIZE, 2U); JSHandle jsDequeIter = factory->NewJSAPIDequeIterator(NewJSAPIDeque(thread, factory, proto)); - DUMP_FOR_HANDLE(jsDequeIter) + DUMP_FOR_HANDLE(jsDequeIter); break; } case JSType::JS_API_STACK: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSAPIStack::SIZE, 1U); JSHandle jsStack = NewJSAPIStack(factory, proto); - DUMP_FOR_HANDLE(jsStack) + DUMP_FOR_HANDLE(jsStack); break; } case JSType::JS_API_STACK_ITERATOR: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSAPIStackIterator::SIZE, 2U); JSHandle jsStackIter = factory->NewJSAPIStackIterator(NewJSAPIStack(factory, proto)); - DUMP_FOR_HANDLE(jsStackIter) + DUMP_FOR_HANDLE(jsStackIter); break; } case JSType::JS_API_VECTOR: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSAPIVector::SIZE, 1U); JSHandle jsVector = NewJSAPIVector(factory, proto); - DUMP_FOR_HANDLE(jsVector) + DUMP_FOR_HANDLE(jsVector); break; } case JSType::JS_API_VECTOR_ITERATOR: { CHECK_DUMP_FIELDS(JSObject::SIZE, JSAPIVectorIterator::SIZE, 2U); JSHandle jsVectorIter = factory->NewJSAPIVectorIterator(NewJSAPIVector(factory, proto)); - DUMP_FOR_HANDLE(jsVectorIter) + DUMP_FOR_HANDLE(jsVectorIter); break; } case JSType::JS_API_LIST: { // 1 : 1 dump fileds number CHECK_DUMP_FIELDS(JSObject::SIZE, JSAPIList::SIZE, 1U); JSHandle jsAPIList = NewJSAPIList(thread, factory); - DUMP_FOR_HANDLE(jsAPIList) + DUMP_FOR_HANDLE(jsAPIList); break; } case JSType::JS_API_LINKED_LIST: { // 1 : 1 dump fileds number CHECK_DUMP_FIELDS(JSObject::SIZE, JSAPILinkedList::SIZE, 1U); JSHandle jsAPILinkedList = NewJSAPILinkedList(thread, factory); - DUMP_FOR_HANDLE(jsAPILinkedList) + DUMP_FOR_HANDLE(jsAPILinkedList); break; } case JSType::JS_API_LIST_ITERATOR: { @@ -1241,7 +1269,7 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) CHECK_DUMP_FIELDS(JSObject::SIZE, JSAPIListIterator::SIZE, 2U); JSHandle jsAPIList = NewJSAPIList(thread, factory); JSHandle jsAPIListIter = factory->NewJSAPIListIterator(jsAPIList); - DUMP_FOR_HANDLE(jsAPIListIter) + DUMP_FOR_HANDLE(jsAPIListIter); break; } case JSType::JS_API_LINKED_LIST_ITERATOR: { @@ -1250,7 +1278,7 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) JSHandle jsAPILinkedList = NewJSAPILinkedList(thread, factory); JSHandle jsAPILinkedListIter = factory->NewJSAPILinkedListIterator(jsAPILinkedList); - DUMP_FOR_HANDLE(jsAPILinkedListIter) + DUMP_FOR_HANDLE(jsAPILinkedListIter); break; } case JSType::MODULE_RECORD: { @@ -1300,7 +1328,7 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) break; } case JSType::JS_MODULE_NAMESPACE: { - CHECK_DUMP_FIELDS(JSObject::SIZE, ModuleNamespace::SIZE, 2U); + CHECK_DUMP_FIELDS(JSObject::SIZE, ModuleNamespace::SIZE, 3U); JSHandle moduleNamespace = factory->NewModuleNamespace(); DUMP_FOR_HANDLE(moduleNamespace); break; @@ -1338,7 +1366,7 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) case JSType::CLASS_LITERAL: { CHECK_DUMP_FIELDS(TaggedObject::TaggedObjectSize(), ClassLiteral::SIZE, 2U); JSHandle classLiteral = factory->NewClassLiteral(); - DUMP_FOR_HANDLE(classLiteral) + DUMP_FOR_HANDLE(classLiteral); break; } default: diff --git a/ecmascript/tests/ecma_context_test.cpp b/ecmascript/tests/ecma_context_test.cpp index 0e708cca4a07890d8173f3748dcaddb8a6d3c218..6c00c3069c87fbb6d04dcefa957bd4e3165feb52 100644 --- a/ecmascript/tests/ecma_context_test.cpp +++ b/ecmascript/tests/ecma_context_test.cpp @@ -51,51 +51,55 @@ public: HWTEST_F_L0(EcmaContextTest, Create) { - [[maybe_unused]]auto context = EcmaContext::Create(thread); + auto context = EcmaContext::CreateAndInitialize(thread); CVector Cv1 = thread->GetEcmaContexts(); - EXPECT_EQ(Cv1.size(), 1); - auto context2 = EcmaContext::Create(thread); + EXPECT_EQ(Cv1.size(), 2); // 2: size of contexts. + auto context2 = EcmaContext::CreateAndInitialize(thread); Cv1 = thread->GetEcmaContexts(); - EXPECT_EQ(Cv1.size(), 1); - thread->PushContext(context2); + EXPECT_EQ(Cv1.size(), 3); // 3: size of contexts. + EcmaContext::CheckAndDestroy(thread, context); Cv1 = thread->GetEcmaContexts(); EXPECT_EQ(Cv1.size(), 2); // 2: size of contexts. -} - -HWTEST_F_L0(EcmaContextTest, CreatePushContext) -{ - auto context = EcmaContext::Create(thread); - auto context1 = EcmaContext::Create(thread); - thread->PushContext(context1); - CVector context3 = thread->GetEcmaContexts(); - EXPECT_EQ(context3.size(), 2); // 2: size of contexts. - thread->PushContext(context); - context3 = thread->GetEcmaContexts(); - EXPECT_EQ(context3.size(), 3); // 3: size of contexts. - thread->PopContext(); - context3 = thread->GetEcmaContexts(); - EXPECT_EQ(context3.size(), 2); // 2: size of contexts. + EcmaContext::CheckAndDestroy(thread, context2); } HWTEST_F_L0(EcmaContextTest, GetRegExpCache) { EcmaVM *vm = thread->GetEcmaVM(); ObjectFactory *factory = vm->GetFactory(); - auto context = EcmaContext::Create(thread); + auto context = EcmaContext::CreateAndInitialize(thread); JSHandle regexp = factory->NewFromASCII("\\g"); JSHandle value2(regexp); context->SetRegExpCache(value2.GetTaggedValue()); JSHandle res2 = context->GetRegExpCache(); EXPECT_EQ(res2.GetTaggedValue(), value2.GetTaggedValue()); + EcmaContext::CheckAndDestroy(thread, context); } HWTEST_F_L0(EcmaContextTest, AllowAtomicWait) { - auto context = EcmaContext::Create(thread); + auto context = EcmaContext::CreateAndInitialize(thread); bool value = context->GetAllowAtomicWait(); EXPECT_TRUE(value); context->SetAllowAtomicWait(false); bool value2 = context->GetAllowAtomicWait(); EXPECT_FALSE(value2); + EcmaContext::CheckAndDestroy(thread, context); +} + +HWTEST_F_L0(EcmaContextTest, SwitchCurrentContext) +{ + auto context = EcmaContext::CreateAndInitialize(thread); + auto context1 = EcmaContext::CreateAndInitialize(thread); + + CVector contextVector = thread->GetEcmaContexts(); + EXPECT_EQ(contextVector.size(), 3); // 3: size of contexts. + + thread->SwitchCurrentContext(context); + + EcmaContext::CheckAndDestroy(thread, context); + contextVector = thread->GetEcmaContexts(); + EXPECT_EQ(contextVector.size(), 2); // 3: size of contexts. + EcmaContext::CheckAndDestroy(thread, context1); } } // namespace panda::test \ No newline at end of file diff --git a/ecmascript/tests/ecma_string_accessor_test.cpp b/ecmascript/tests/ecma_string_accessor_test.cpp index 6c58d6c9125afea176f2270acb572c9a4af82f51..53fc01b343e2a21a33a0b0a1e26146daebaa35e2 100644 --- a/ecmascript/tests/ecma_string_accessor_test.cpp +++ b/ecmascript/tests/ecma_string_accessor_test.cpp @@ -56,7 +56,7 @@ public: HWTEST_F_L0(LineEcmaStringTest, ComputeSizeUtf8) { uint32_t scale = 3333; - for (uint32_t i = 0x40000000U - 1; i > scale; i = i - scale) { + for (uint32_t i = EcmaString::MAX_STRING_LENGTH - 1; i > scale; i = i - scale) { uint32_t length = i; EXPECT_EQ(LineEcmaString::ComputeSizeUtf8(length), length + LineEcmaString::SIZE); } @@ -71,7 +71,7 @@ HWTEST_F_L0(LineEcmaStringTest, ComputeSizeUtf8) HWTEST_F_L0(LineEcmaStringTest, ComputeSizeUtf16) { uint32_t scale = 3333; - for (uint32_t i = 0x40000000U - 1; i > scale; i = i - scale) { + for (uint32_t i = EcmaString::MAX_STRING_LENGTH - 1; i > scale; i = i - scale) { uint32_t length = i; EXPECT_EQ(LineEcmaString::ComputeSizeUtf16(length), 2 * length + LineEcmaString::SIZE); } @@ -116,9 +116,6 @@ HWTEST_F_L0(EcmaStringAccessorTest, CreateLineString) size_t sizeAllocComp = 5; JSHandle handleEcmaStrAllocComp(thread, EcmaStringAccessor::CreateLineString(ecmaVMPtr, sizeAllocComp, true)); - for (uint32_t i = 0; i < sizeAllocComp; i++) { - EXPECT_EQ(EcmaStringAccessor(handleEcmaStrAllocComp).Get(i), 0U); - } EXPECT_EQ(EcmaStringAccessor(handleEcmaStrAllocComp).GetLength(), sizeAllocComp); EXPECT_TRUE(EcmaStringAccessor(handleEcmaStrAllocComp).IsUtf8()); EXPECT_FALSE(EcmaStringAccessor(handleEcmaStrAllocComp).IsUtf16()); @@ -127,9 +124,6 @@ HWTEST_F_L0(EcmaStringAccessorTest, CreateLineString) size_t sizeAllocNotComp = 5; JSHandle handleEcmaStrAllocNotComp(thread, EcmaStringAccessor::CreateLineString(ecmaVMPtr, sizeAllocNotComp, false)); - for (uint32_t i = 0; i < sizeAllocNotComp; i++) { - EXPECT_EQ(EcmaStringAccessor(handleEcmaStrAllocNotComp).Get(i), 0U); - } EXPECT_EQ(EcmaStringAccessor(handleEcmaStrAllocNotComp).GetLength(), sizeAllocNotComp); EXPECT_FALSE(EcmaStringAccessor(handleEcmaStrAllocNotComp).IsUtf8()); EXPECT_TRUE(EcmaStringAccessor(handleEcmaStrAllocNotComp).IsUtf16()); @@ -834,25 +828,6 @@ HWTEST_F_L0(EcmaStringAccessorTest, GetHashcode_004) EXPECT_EQ(EcmaStringAccessor(handleEcmaStrEmpty).GetHashcode(), 0U); } -/* - * @tc.name: GetHashcode_005 - * @tc.desc: Check whether the value returned through an EcmaString made by CreateLineString(, true/false, ) calling - * GetHashcode function is within expectations. - * @tc.type: FUNC - * @tc.require: - */ -HWTEST_F_L0(EcmaStringAccessorTest, GetHashcode_005) -{ - // GetHashcode(). EcmaString made by CreateLineString(). - size_t sizeAlloc = 5; - JSHandle handleEcmaStrAllocComp(thread, - EcmaStringAccessor::CreateLineString(ecmaVMPtr, sizeAlloc, true)); - JSHandle handleEcmaStrAllocNotComp(thread, - EcmaStringAccessor::CreateLineString(ecmaVMPtr, sizeAlloc, false)); - EXPECT_EQ(EcmaStringAccessor(handleEcmaStrAllocComp).GetHashcode(), 0U); - EXPECT_EQ(EcmaStringAccessor(handleEcmaStrAllocNotComp).GetHashcode(), 0U); -} - /* * @tc.name: ComputeHashcodeUtf8 * @tc.desc: Check whether the value returned through calling ComputeHashcodeUtf8 function with an Array(uint8_t) is @@ -1379,14 +1354,14 @@ HWTEST_F_L0(EcmaStringAccessorTest, StringsAreEqual_006) /* * @tc.name: StringsAreEqualUtf8_001 - * @tc.desc: Check whether the bool returned through calling StringsAreEqualUtf8 function with an EcmaString made by + * @tc.desc: Check whether the bool returned through calling StringIsEqualUint8Data function with an EcmaString made by * CreateFromUtf8() and an Array(uint8_t) is within expectations. * @tc.type: FUNC * @tc.require: */ HWTEST_F_L0(EcmaStringAccessorTest, StringsAreEqualUtf8_001) { - // StringsAreEqualUtf8(). EcmaString made by CreateFromUtf8(), Array:U8. + // StringIsEqualUint8Data(). EcmaString made by CreateFromUtf8(), Array:U8. uint8_t arrayU8No1[4] = {45, 92, 78}; uint8_t arrayU8No2[5] = {45, 92, 78, 24}; uint8_t arrayU8No3[3] = {45, 92}; @@ -1399,26 +1374,26 @@ HWTEST_F_L0(EcmaStringAccessorTest, StringsAreEqualUtf8_001) EcmaStringAccessor::CreateFromUtf8(ecmaVMPtr, &arrayU8No2[0], lengthEcmaStrU8No2, true)); JSHandle handleEcmaStrU8No3(thread, EcmaStringAccessor::CreateFromUtf8(ecmaVMPtr, &arrayU8No3[0], lengthEcmaStrU8No3, true)); - EXPECT_TRUE(EcmaStringAccessor::StringsAreEqualUtf8( + EXPECT_TRUE(EcmaStringAccessor::StringIsEqualUint8Data( *handleEcmaStrU8No1, &arrayU8No1[0], lengthEcmaStrU8No1, true)); - EXPECT_FALSE(EcmaStringAccessor::StringsAreEqualUtf8( + EXPECT_FALSE(EcmaStringAccessor::StringIsEqualUint8Data( *handleEcmaStrU8No1, &arrayU8No1[0], lengthEcmaStrU8No1, false)); - EXPECT_FALSE(EcmaStringAccessor::StringsAreEqualUtf8( + EXPECT_FALSE(EcmaStringAccessor::StringIsEqualUint8Data( *handleEcmaStrU8No2, &arrayU8No1[0], lengthEcmaStrU8No1, true)); - EXPECT_FALSE(EcmaStringAccessor::StringsAreEqualUtf8( + EXPECT_FALSE(EcmaStringAccessor::StringIsEqualUint8Data( *handleEcmaStrU8No3, &arrayU8No1[0], lengthEcmaStrU8No1, true)); } /* * @tc.name: StringsAreEqualUtf8_002 - * @tc.desc: Check whether the bool returned through calling StringsAreEqualUtf8 function with an EcmaString made by + * @tc.desc: Check whether the bool returned through calling StringIsEqualUint8Data function with an EcmaString made by * CreateFromUtf16( , , , true) and an Array(uint8_t) is within expectations. * @tc.type: FUNC * @tc.require: */ HWTEST_F_L0(EcmaStringAccessorTest, StringsAreEqualUtf8_002) { - // StringsAreEqualUtf8(). EcmaString made by CreateFromUtf16( , , , true), Array:U8. + // StringIsEqualUint8Data(). EcmaString made by CreateFromUtf16( , , , true), Array:U8. uint8_t arrayU8No1[4] = {45, 92, 78}; uint16_t arrayU16CompNo1[] = {45, 92, 78}; uint16_t arrayU16CompNo2[] = {45, 92, 78, 24}; @@ -1433,26 +1408,26 @@ HWTEST_F_L0(EcmaStringAccessorTest, StringsAreEqualUtf8_002) EcmaStringAccessor::CreateFromUtf16(ecmaVMPtr, &arrayU16CompNo2[0], lengthEcmaStrU16CompNo2, true)); JSHandle handleEcmaStrU16CompNo3(thread, EcmaStringAccessor::CreateFromUtf16(ecmaVMPtr, &arrayU16CompNo3[0], lengthEcmaStrU16CompNo3, true)); - EXPECT_TRUE(EcmaStringAccessor::StringsAreEqualUtf8( + EXPECT_TRUE(EcmaStringAccessor::StringIsEqualUint8Data( *handleEcmaStrU16CompNo1, &arrayU8No1[0], lengthEcmaStrU8No1, true)); - EXPECT_FALSE(EcmaStringAccessor::StringsAreEqualUtf8( + EXPECT_FALSE(EcmaStringAccessor::StringIsEqualUint8Data( *handleEcmaStrU16CompNo1, &arrayU8No1[0], lengthEcmaStrU8No1, false)); - EXPECT_FALSE(EcmaStringAccessor::StringsAreEqualUtf8( + EXPECT_FALSE(EcmaStringAccessor::StringIsEqualUint8Data( *handleEcmaStrU16CompNo2, &arrayU8No1[0], lengthEcmaStrU8No1, true)); - EXPECT_FALSE(EcmaStringAccessor::StringsAreEqualUtf8( + EXPECT_FALSE(EcmaStringAccessor::StringIsEqualUint8Data( *handleEcmaStrU16CompNo3, &arrayU8No1[0], lengthEcmaStrU8No1, true)); } /* * @tc.name: StringsAreEqualUtf8_003 - * @tc.desc: Check whether the bool returned through calling StringsAreEqualUtf8 function with an EcmaString made by + * @tc.desc: Check whether the bool returned through calling StringIsEqualUint8Data function with an EcmaString made by * CreateFromUtf16( , , , false) and an Array(uint8_t) is within expectations. * @tc.type: FUNC * @tc.require: */ HWTEST_F_L0(EcmaStringAccessorTest, StringsAreEqualUtf8_003) { - // StringsAreEqualUtf8(). EcmaString made by CreateFromUtf16( , , , false), Array:U8. + // StringIsEqualUint8Data(). EcmaString made by CreateFromUtf16( , , , false), Array:U8. uint8_t arrayU8No1[4] = {45, 92, 78}; uint16_t arrayU16NotCompNo1[] = {45, 92, 78}; uint16_t arrayU16NotCompNo2[] = {45, 92, 78, 24}; @@ -1471,15 +1446,15 @@ HWTEST_F_L0(EcmaStringAccessorTest, StringsAreEqualUtf8_003) EcmaStringAccessor::CreateFromUtf16(ecmaVMPtr, &arrayU16NotCompNo3[0], lengthEcmaStrU16NotCompNo3, true)); JSHandle handleEcmaStrU16NotCompNo4(thread, EcmaStringAccessor::CreateFromUtf16(ecmaVMPtr, &arrayU16NotCompNo4[0], lengthEcmaStrU16NotCompNo4, false)); - EXPECT_FALSE(EcmaStringAccessor::StringsAreEqualUtf8( + EXPECT_FALSE(EcmaStringAccessor::StringIsEqualUint8Data( *handleEcmaStrU16NotCompNo1, &arrayU8No1[0], lengthEcmaStrU8No1, false)); - EXPECT_TRUE(EcmaStringAccessor::StringsAreEqualUtf8( + EXPECT_TRUE(EcmaStringAccessor::StringIsEqualUint8Data( *handleEcmaStrU16NotCompNo1, &arrayU8No1[0], lengthEcmaStrU8No1, true)); - EXPECT_FALSE(EcmaStringAccessor::StringsAreEqualUtf8( + EXPECT_FALSE(EcmaStringAccessor::StringIsEqualUint8Data( *handleEcmaStrU16NotCompNo2, &arrayU8No1[0], lengthEcmaStrU8No1, false)); - EXPECT_FALSE(EcmaStringAccessor::StringsAreEqualUtf8( + EXPECT_FALSE(EcmaStringAccessor::StringIsEqualUint8Data( *handleEcmaStrU16NotCompNo3, &arrayU8No1[0], lengthEcmaStrU8No1, false)); - EXPECT_FALSE(EcmaStringAccessor::StringsAreEqualUtf8( + EXPECT_FALSE(EcmaStringAccessor::StringIsEqualUint8Data( *handleEcmaStrU16NotCompNo4, &arrayU8No1[0], lengthEcmaStrU8No1, false)); } diff --git a/ecmascript/tests/ecma_string_test.cpp b/ecmascript/tests/ecma_string_test.cpp index fb0eeb8232e522df0afee0d9980697288eb3940a..6ae3ee27a99cd2cacbd94fedb58f7c0b2badfc2d 100644 --- a/ecmascript/tests/ecma_string_test.cpp +++ b/ecmascript/tests/ecma_string_test.cpp @@ -1244,14 +1244,14 @@ HWTEST_F_L0(EcmaStringTest, StringsAreEqual_006) /* * @tc.name: StringsAreEqualUtf8_001 - * @tc.desc: Check whether the bool returned through calling StringsAreEqualUtf8 function with an EcmaString made by + * @tc.desc: Check whether the bool returned through calling StringIsEqualUint8Data function with an EcmaString made by * CreateFromUtf8() and an Array(uint8_t) is within expectations. * @tc.type: FUNC * @tc.require: */ HWTEST_F_L0(EcmaStringTest, StringsAreEqualUtf8_001) { - // StringsAreEqualUtf8(). EcmaString made by CreateFromUtf8(), Array:U8. + // StringIsEqualUint8Data(). EcmaString made by CreateFromUtf8(), Array:U8. uint8_t arrayU8No1[4] = {45, 92, 78}; uint8_t arrayU8No2[5] = {45, 92, 78, 24}; uint8_t arrayU8No3[3] = {45, 92}; @@ -1264,22 +1264,22 @@ HWTEST_F_L0(EcmaStringTest, StringsAreEqualUtf8_001) EcmaString::CreateFromUtf8(ecmaVMPtr, &arrayU8No2[0], lengthEcmaStrU8No2, true)); JSHandle handleEcmaStrU8No3(thread, EcmaString::CreateFromUtf8(ecmaVMPtr, &arrayU8No3[0], lengthEcmaStrU8No3, true)); - EXPECT_TRUE(EcmaString::StringsAreEqualUtf8(*handleEcmaStrU8No1, &arrayU8No1[0], lengthEcmaStrU8No1, true)); - EXPECT_FALSE(EcmaString::StringsAreEqualUtf8(*handleEcmaStrU8No1, &arrayU8No1[0], lengthEcmaStrU8No1, false)); - EXPECT_FALSE(EcmaString::StringsAreEqualUtf8(*handleEcmaStrU8No2, &arrayU8No1[0], lengthEcmaStrU8No1, true)); - EXPECT_FALSE(EcmaString::StringsAreEqualUtf8(*handleEcmaStrU8No3, &arrayU8No1[0], lengthEcmaStrU8No1, true)); + EXPECT_TRUE(EcmaString::StringIsEqualUint8Data(*handleEcmaStrU8No1, &arrayU8No1[0], lengthEcmaStrU8No1, true)); + EXPECT_FALSE(EcmaString::StringIsEqualUint8Data(*handleEcmaStrU8No1, &arrayU8No1[0], lengthEcmaStrU8No1, false)); + EXPECT_FALSE(EcmaString::StringIsEqualUint8Data(*handleEcmaStrU8No2, &arrayU8No1[0], lengthEcmaStrU8No1, true)); + EXPECT_FALSE(EcmaString::StringIsEqualUint8Data(*handleEcmaStrU8No3, &arrayU8No1[0], lengthEcmaStrU8No1, true)); } /* * @tc.name: StringsAreEqualUtf8_002 - * @tc.desc: Check whether the bool returned through calling StringsAreEqualUtf8 function with an EcmaString made by + * @tc.desc: Check whether the bool returned through calling StringIsEqualUint8Data function with an EcmaString made by * CreateFromUtf16( , , , true) and an Array(uint8_t) is within expectations. * @tc.type: FUNC * @tc.require: */ HWTEST_F_L0(EcmaStringTest, StringsAreEqualUtf8_002) { - // StringsAreEqualUtf8(). EcmaString made by CreateFromUtf16( , , , true), Array:U8. + // StringIsEqualUint8Data(). EcmaString made by CreateFromUtf16( , , , true), Array:U8. uint8_t arrayU8No1[4] = {45, 92, 78}; uint16_t arrayU16CompNo1[] = {45, 92, 78}; uint16_t arrayU16CompNo2[] = {45, 92, 78, 24}; @@ -1294,22 +1294,26 @@ HWTEST_F_L0(EcmaStringTest, StringsAreEqualUtf8_002) EcmaString::CreateFromUtf16(ecmaVMPtr, &arrayU16CompNo2[0], lengthEcmaStrU16CompNo2, true)); JSHandle handleEcmaStrU16CompNo3(thread, EcmaString::CreateFromUtf16(ecmaVMPtr, &arrayU16CompNo3[0], lengthEcmaStrU16CompNo3, true)); - EXPECT_TRUE(EcmaString::StringsAreEqualUtf8(*handleEcmaStrU16CompNo1, &arrayU8No1[0], lengthEcmaStrU8No1, true)); - EXPECT_FALSE(EcmaString::StringsAreEqualUtf8(*handleEcmaStrU16CompNo1, &arrayU8No1[0], lengthEcmaStrU8No1, false)); - EXPECT_FALSE(EcmaString::StringsAreEqualUtf8(*handleEcmaStrU16CompNo2, &arrayU8No1[0], lengthEcmaStrU8No1, true)); - EXPECT_FALSE(EcmaString::StringsAreEqualUtf8(*handleEcmaStrU16CompNo3, &arrayU8No1[0], lengthEcmaStrU8No1, true)); + EXPECT_TRUE(EcmaString::StringIsEqualUint8Data(*handleEcmaStrU16CompNo1, + &arrayU8No1[0], lengthEcmaStrU8No1, true)); + EXPECT_FALSE(EcmaString::StringIsEqualUint8Data(*handleEcmaStrU16CompNo1, + &arrayU8No1[0], lengthEcmaStrU8No1, false)); + EXPECT_FALSE(EcmaString::StringIsEqualUint8Data(*handleEcmaStrU16CompNo2, + &arrayU8No1[0], lengthEcmaStrU8No1, true)); + EXPECT_FALSE(EcmaString::StringIsEqualUint8Data(*handleEcmaStrU16CompNo3, + &arrayU8No1[0], lengthEcmaStrU8No1, true)); } /* * @tc.name: StringsAreEqualUtf8_003 - * @tc.desc: Check whether the bool returned through calling StringsAreEqualUtf8 function with an EcmaString made by + * @tc.desc: Check whether the bool returned through calling StringIsEqualUint8Data function with an EcmaString made by * CreateFromUtf16( , , , false) and an Array(uint8_t) is within expectations. * @tc.type: FUNC * @tc.require: */ HWTEST_F_L0(EcmaStringTest, StringsAreEqualUtf8_003) { - // StringsAreEqualUtf8(). EcmaString made by CreateFromUtf16( , , , false), Array:U8. + // StringIsEqualUint8Data(). EcmaString made by CreateFromUtf16( , , , false), Array:U8. uint8_t arrayU8No1[4] = {45, 92, 78}; uint16_t arrayU16NotCompNo1[] = {45, 92, 78}; uint16_t arrayU16NotCompNo2[] = {45, 92, 78, 24}; @@ -1329,15 +1333,15 @@ HWTEST_F_L0(EcmaStringTest, StringsAreEqualUtf8_003) JSHandle handleEcmaStrU16NotCompNo4(thread, EcmaString::CreateFromUtf16(ecmaVMPtr, &arrayU16NotCompNo4[0], lengthEcmaStrU16NotCompNo4, false)); EXPECT_FALSE( - EcmaString::StringsAreEqualUtf8(*handleEcmaStrU16NotCompNo1, &arrayU8No1[0], lengthEcmaStrU8No1, false)); + EcmaString::StringIsEqualUint8Data(*handleEcmaStrU16NotCompNo1, &arrayU8No1[0], lengthEcmaStrU8No1, false)); EXPECT_TRUE( - EcmaString::StringsAreEqualUtf8(*handleEcmaStrU16NotCompNo1, &arrayU8No1[0], lengthEcmaStrU8No1, true)); + EcmaString::StringIsEqualUint8Data(*handleEcmaStrU16NotCompNo1, &arrayU8No1[0], lengthEcmaStrU8No1, true)); EXPECT_FALSE( - EcmaString::StringsAreEqualUtf8(*handleEcmaStrU16NotCompNo2, &arrayU8No1[0], lengthEcmaStrU8No1, false)); + EcmaString::StringIsEqualUint8Data(*handleEcmaStrU16NotCompNo2, &arrayU8No1[0], lengthEcmaStrU8No1, false)); EXPECT_FALSE( - EcmaString::StringsAreEqualUtf8(*handleEcmaStrU16NotCompNo3, &arrayU8No1[0], lengthEcmaStrU8No1, false)); + EcmaString::StringIsEqualUint8Data(*handleEcmaStrU16NotCompNo3, &arrayU8No1[0], lengthEcmaStrU8No1, false)); EXPECT_FALSE( - EcmaString::StringsAreEqualUtf8(*handleEcmaStrU16NotCompNo4, &arrayU8No1[0], lengthEcmaStrU8No1, false)); + EcmaString::StringIsEqualUint8Data(*handleEcmaStrU16NotCompNo4, &arrayU8No1[0], lengthEcmaStrU8No1, false)); } /* diff --git a/ecmascript/tests/gc_test.cpp b/ecmascript/tests/gc_test.cpp index 8fb29aae4d42441d35ee1e8033f87d36528a039c..d6aa6a8fc362b4e5563b814be39dfe1f52744b48 100644 --- a/ecmascript/tests/gc_test.cpp +++ b/ecmascript/tests/gc_test.cpp @@ -16,7 +16,7 @@ #include "ecmascript/builtins/builtins_ark_tools.h" #include "ecmascript/ecma_vm.h" #include "ecmascript/mem/full_gc.h" -#include "ecmascript/object_factory.h" +#include "ecmascript/object_factory-inl.h" #include "ecmascript/mem/concurrent_marker.h" #include "ecmascript/mem/stw_young_gc.h" #include "ecmascript/mem/partial_gc.h" @@ -182,50 +182,15 @@ HWTEST_F_L0(GCTest, NativeBindingCheckGCTest) { auto heap = const_cast(thread->GetEcmaVM()->GetHeap()); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + heap->CollectGarbage(TriggerGCType::OLD_GC); size_t oldNativeSize = heap->GetNativeBindingSize(); size_t newNativeSize = heap->GetNativeBindingSize(); - { - [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread); - - auto newData = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(1 * 1024 * 1024); - [[maybe_unused]] JSHandle obj = factory->NewJSNativePointer(newData, - NativeAreaAllocator::FreeBufferFunc, nullptr, false, 1 * 1024 * 1024); - newNativeSize = heap->GetNativeBindingSize(); - EXPECT_EQ(newNativeSize - oldNativeSize, 1UL * 1024 * 1024); - - auto newData1 = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(1 * 1024 * 1024); - [[maybe_unused]] JSHandle obj2 = factory->NewJSNativePointer(newData1, - NativeAreaAllocator::FreeBufferFunc, nullptr, false, 1 * 1024 * 1024); - - EXPECT_TRUE(newNativeSize - oldNativeSize > 0); - EXPECT_TRUE(newNativeSize - oldNativeSize <= 2 * 1024 *1024); - for (int i = 0; i < 20; i++) { - auto newData2 = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(1 * 1024 * 1024); - [[maybe_unused]] JSHandle obj3 = factory->NewJSNativePointer(newData2, - NativeAreaAllocator::FreeBufferFunc, nullptr, false, 1 * 1024 * 1024); - } - // Young GC should be trigger here, so the size should be reduced. - EXPECT_TRUE(newNativeSize - oldNativeSize < 22 * 1024 *1024); - } - auto partialGc = heap->GetPartialGC(); - heap->SetMarkType(MarkType::MARK_FULL); - partialGc->RunPhases(); - newNativeSize = heap->GetNativeBindingSize(); - EXPECT_EQ(newNativeSize - oldNativeSize, 0UL); -} - -HWTEST_F_L0(GCTest, NonNewSpaceNativeBindingCheckGCTest) -{ - auto heap = const_cast(thread->GetEcmaVM()->GetHeap()); - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - size_t oldNativeSize = heap->GetNonNewSpaceNativeBindingSize(); - size_t newNativeSize = heap->GetNonNewSpaceNativeBindingSize(); { [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread); auto newData = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(1 * 1024 * 1024); [[maybe_unused]] JSHandle obj = factory->NewJSNativePointer(newData, NativeAreaAllocator::FreeBufferFunc, nullptr, true, 1 * 1024 * 1024); - newNativeSize = heap->GetNonNewSpaceNativeBindingSize(); + newNativeSize = heap->GetNativeBindingSize(); EXPECT_EQ(newNativeSize - oldNativeSize, 1UL * 1024 * 1024); auto newData1 = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(1 * 1024 * 1024); @@ -243,10 +208,8 @@ HWTEST_F_L0(GCTest, NonNewSpaceNativeBindingCheckGCTest) // Old GC should be trigger here, so the size should be reduced. EXPECT_TRUE(newNativeSize - oldNativeSize < 256 * 1024 *1024); } - auto partialGc = heap->GetPartialGC(); - heap->SetMarkType(MarkType::MARK_FULL); - partialGc->RunPhases(); - newNativeSize = heap->GetNonNewSpaceNativeBindingSize(); + heap->CollectGarbage(TriggerGCType::OLD_GC); + newNativeSize = heap->GetNativeBindingSize(); EXPECT_EQ(newNativeSize - oldNativeSize, 0UL); } @@ -292,14 +255,14 @@ HWTEST_F_L0(GCTest, NonNewSpaceNativeGCTestConcurrentMarkDisabled) // Disable concurrent mark. heap->GetConcurrentMarker()->ConfigConcurrentMark(false); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - size_t oldNativeSize = heap->GetNonNewSpaceNativeBindingSize(); - size_t newNativeSize = heap->GetNonNewSpaceNativeBindingSize(); + size_t oldNativeSize = heap->GetNativeBindingSize(); + size_t newNativeSize = heap->GetNativeBindingSize(); { [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread); auto newData = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(1 * 1024 * 1024); [[maybe_unused]] JSHandle obj = factory->NewJSNativePointer(newData, NativeAreaAllocator::FreeBufferFunc, nullptr, true, 1 * 1024 * 1024); - newNativeSize = heap->GetNonNewSpaceNativeBindingSize(); + newNativeSize = heap->GetNativeBindingSize(); EXPECT_EQ(newNativeSize - oldNativeSize, 1UL * 1024 * 1024); auto newData1 = thread->GetEcmaVM()->GetNativeAreaAllocator()->AllocateBuffer(1 * 1024 * 1024); @@ -318,7 +281,7 @@ HWTEST_F_L0(GCTest, NonNewSpaceNativeGCTestConcurrentMarkDisabled) EXPECT_TRUE(newNativeSize - oldNativeSize < 256 * 1024 *1024); } const_cast(thread->GetEcmaVM()->GetHeap())->CollectGarbage(TriggerGCType::OLD_GC); - newNativeSize = heap->GetNonNewSpaceNativeBindingSize(); + newNativeSize = heap->GetNativeBindingSize(); EXPECT_EQ(newNativeSize - oldNativeSize, 0UL); } @@ -344,4 +307,85 @@ HWTEST_F_L0(GCTest, ArkToolsForceFullGC) ASSERT_TRUE(thread->GetEcmaVM()->GetHeap()->GetCommittedSize() < newSize); } +HWTEST_F_L0(GCTest, ColdStartForceExpand) +{ + auto heap = const_cast(thread->GetEcmaVM()->GetHeap()); + size_t originalHeapSize = heap->GetCommittedSize(); + heap->GetConcurrentMarker()->ConfigConcurrentMark(false); + heap->NotifyPostFork(); + heap->NotifyFinishColdStartSoon(); + { + [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread); + for (int i = 0; i < 500; i++) { + [[maybe_unused]] JSHandle array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray( + 10 * 1024, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE); + } + } + size_t expandHeapSize = thread->GetEcmaVM()->GetHeap()->GetCommittedSize(); + usleep(2500000); + { + [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread); + for (int i = 0; i < 100; i++) { + [[maybe_unused]] JSHandle array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray( + 10 * 1024, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE); + } + } + size_t newSize = thread->GetEcmaVM()->GetHeap()->GetCommittedSize(); + EXPECT_TRUE(originalHeapSize < expandHeapSize); + EXPECT_TRUE(expandHeapSize > newSize); +} + +HWTEST_F_L0(GCTest, HighSensitiveForceExpand) +{ + auto heap = const_cast(thread->GetEcmaVM()->GetHeap()); + size_t originalHeapSize = heap->GetCommittedSize(); + heap->GetConcurrentMarker()->ConfigConcurrentMark(false); + heap->NotifyHighSensitive(true); + { + [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread); + for (int i = 0; i < 500; i++) { + [[maybe_unused]] JSHandle array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray( + 10 * 1024, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE); + } + } + size_t expandHeapSize = thread->GetEcmaVM()->GetHeap()->GetCommittedSize(); + const_cast(thread->GetEcmaVM()->GetHeap())->NotifyHighSensitive(false); + { + [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread); + for (int i = 0; i < 100; i++) { + [[maybe_unused]] JSHandle array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray( + 10 * 1024, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE); + } + } + size_t newSize = thread->GetEcmaVM()->GetHeap()->GetCommittedSize(); + EXPECT_TRUE(originalHeapSize < expandHeapSize); + EXPECT_TRUE(expandHeapSize > newSize); +} + +HWTEST_F_L0(GCTest, NoFullConcurrentMarkOldGCTrigger) +{ +#ifdef NDEBUG + auto heap = const_cast(instance->GetHeap()); + heap->CollectGarbage(TriggerGCType::FULL_GC); + { + [[maybe_unused]] ecmascript::EcmaHandleScope baseScope(thread); + + for (int i = 0; i < 1024 * 2; i++) { + [[maybe_unused]] JSHandle obj = + instance->GetFactory()->NewTaggedArray(1024, JSTaggedValue::Hole(), MemSpaceType::NON_MOVABLE); + } + EXPECT_EQ(heap->GetOldSpace()->GetOvershootSize(), + instance->GetEcmaParamConfiguration().GetOldSpaceOvershootSize()); + EXPECT_TRUE(heap->GetOldGCRequested()); + heap->CollectGarbage(TriggerGCType::OLD_GC, GCReason::ALLOCATION_LIMIT); + EXPECT_TRUE(thread->HasPendingException()); + JSType errorType = thread->GetException().GetTaggedObject()->GetClass()->GetObjectType(); + EXPECT_EQ(errorType, JSType::JS_OOM_ERROR); + } + heap->CollectGarbage(TriggerGCType::OLD_GC); + EXPECT_TRUE(heap->GetNonMovableSpace()->GetHeapObjectSize() < MAX_NONMOVABLE_LIVE_OBJ_SIZE); + EXPECT_EQ(heap->GetOldSpace()->GetOvershootSize(), 0); +#endif +} + } // namespace panda::test diff --git a/ecmascript/tests/js_array_buffer_test.cpp b/ecmascript/tests/js_array_buffer_test.cpp index 994a93118cde34078867ce7544f91504d7e01c7d..ba8057b49e3546df3dad3d0f13f02e89b6324eb2 100644 --- a/ecmascript/tests/js_array_buffer_test.cpp +++ b/ecmascript/tests/js_array_buffer_test.cpp @@ -15,6 +15,7 @@ #include "ecmascript/ecma_vm.h" #include "ecmascript/js_arraybuffer.h" +#include "ecmascript/object_factory-inl.h" #include "ecmascript/tests/test_helper.h" using namespace panda::ecmascript; diff --git a/ecmascript/tests/js_date_time_format_test.cpp b/ecmascript/tests/js_date_time_format_test.cpp index f59e6b5ef2f09517af8410d07f852b6440a57a62..bef6fe18e64497c7aeeb82a32b58014bb8c379d6 100644 --- a/ecmascript/tests/js_date_time_format_test.cpp +++ b/ecmascript/tests/js_date_time_format_test.cpp @@ -18,6 +18,7 @@ #include "ecmascript/js_date.h" #include "ecmascript/js_date_time_format.h" #include "ecmascript/js_locale.h" +#include "ecmascript/object_factory-inl.h" #include "ecmascript/tests/test_helper.h" using namespace panda; diff --git a/ecmascript/tests/js_hclass_test.cpp b/ecmascript/tests/js_hclass_test.cpp index 970a966928f9e6ada0cf135dce6d8ca70597c171..0c461761e9d4742838827e715644f42dcd98c12f 100644 --- a/ecmascript/tests/js_hclass_test.cpp +++ b/ecmascript/tests/js_hclass_test.cpp @@ -18,6 +18,7 @@ #include "ecmascript/global_env.h" #include "ecmascript/ic/proto_change_details.h" #include "ecmascript/layout_info.h" +#include "ecmascript/object_factory-inl.h" #include "ecmascript/tagged_dictionary.h" #include "ecmascript/tests/test_helper.h" @@ -120,10 +121,12 @@ HWTEST_F_L0(JSHClassTest, HasReferenceField) JSHandle obj3Class = factory->NewEcmaHClass(TaggedArray::SIZE, JSType::JS_NATIVE_POINTER, nullHandle); JSHandle obj4Class = factory->NewEcmaHClass(TaggedArray::SIZE, JSType::JS_OBJECT, nullHandle); + JSHandle obj5Class = factory->NewEcmaHClass(TaggedArray::SIZE, JSType::SLICED_STRING, nullHandle); EXPECT_FALSE(obj1Class->HasReferenceField()); EXPECT_TRUE(obj2Class->HasReferenceField()); EXPECT_FALSE(obj3Class->HasReferenceField()); EXPECT_TRUE(obj4Class->HasReferenceField()); + EXPECT_TRUE(obj5Class->HasReferenceField()); } HWTEST_F_L0(JSHClassTest, Clone) @@ -228,7 +231,7 @@ HWTEST_F_L0(JSHClassTest, SetPropertyOfObjHClass_001) JSHandle childObj = factory->NewJSObject(childClass); std::vector keyVector; - JSObject::GetAllKeys(childObj, keyVector); + JSObject::GetAllKeysForSerialization(childObj, keyVector); EXPECT_EQ(keyVector.size(), 3U); EXPECT_TRUE(JSObject::HasProperty(thread, childObj, keyHandle0)); EXPECT_TRUE(JSObject::HasProperty(thread, childObj, keyHandle2)); @@ -284,7 +287,7 @@ HWTEST_F_L0(JSHClassTest, AddProperty) } EXPECT_TRUE(objClass1 == objClass); std::vector keyVector; - JSObject::GetAllKeys(Obj, keyVector); + JSObject::GetAllKeysForSerialization(Obj, keyVector); EXPECT_EQ(keyVector.size(), 3U); EXPECT_TRUE(JSObject::HasProperty(thread, Obj, keyHandle0)); EXPECT_TRUE(JSObject::HasProperty(thread, Obj, keyHandle1)); diff --git a/ecmascript/tests/js_list_format_test.cpp b/ecmascript/tests/js_list_format_test.cpp index 3afb07ab899a466d971f979fc9246c9afe1b6cea..c4c3e1b2208be676fadc4dde1cfe3e5649837d70 100644 --- a/ecmascript/tests/js_list_format_test.cpp +++ b/ecmascript/tests/js_list_format_test.cpp @@ -18,6 +18,7 @@ #include "ecmascript/js_array.h" #include "ecmascript/js_list_format.h" #include "ecmascript/js_iterator.h" +#include "ecmascript/object_factory-inl.h" #include "ecmascript/tests/test_helper.h" using namespace panda::ecmascript; diff --git a/ecmascript/tests/js_locale_test.cpp b/ecmascript/tests/js_locale_test.cpp index 44834f82a5bfc70e8a39198e15894f4c659790db..4fca00040dc31110c52a18b06e64aa6a324ed758 100644 --- a/ecmascript/tests/js_locale_test.cpp +++ b/ecmascript/tests/js_locale_test.cpp @@ -18,6 +18,7 @@ #include "ecmascript/js_number_format.h" #include "ecmascript/js_plural_rules.h" #include "ecmascript/global_env.h" +#include "ecmascript/object_factory-inl.h" #include "ecmascript/tests/test_helper.h" using namespace panda::ecmascript; diff --git a/ecmascript/tests/js_object_test.cpp b/ecmascript/tests/js_object_test.cpp index b5650fb20d8c0398dadf0f761e3f800e55807e7e..08a98ffa4637ba172cb471bba282005d55af8bb0 100644 --- a/ecmascript/tests/js_object_test.cpp +++ b/ecmascript/tests/js_object_test.cpp @@ -862,7 +862,7 @@ HWTEST_F_L0(JSObjectTest, FastToSlow) JSHandle value(thread, JSTaggedValue(1)); ecmaVM->SetEnableForceGC(false); - for (uint32_t i = 0; i < PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES; i++) { + for (uint32_t i = 0; i < PropertyAttributes::MAX_FAST_PROPS_CAPACITY; i++) { number.Update(JSTaggedValue(i)); number.Update(JSTaggedValue::ToString(thread, number).GetTaggedValue()); EcmaString *newString = *factory->ConcatFromString(key, JSTaggedValue::ToString(thread, number)); @@ -873,7 +873,7 @@ HWTEST_F_L0(JSObjectTest, FastToSlow) EXPECT_FALSE(TaggedArray::Cast(obj1->GetProperties().GetTaggedObject())->IsDictionaryMode()); - number.Update(JSTaggedValue(PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES)); + number.Update(JSTaggedValue(PropertyAttributes::MAX_FAST_PROPS_CAPACITY)); number.Update(JSTaggedValue::ToString(thread, number).GetTaggedValue()); EcmaString *newString = *factory->ConcatFromString(key, JSTaggedValue::ToString(thread, number)); newkey.Update(JSTaggedValue(newString)); @@ -881,9 +881,9 @@ HWTEST_F_L0(JSObjectTest, FastToSlow) EXPECT_TRUE(TaggedArray::Cast(obj1->GetProperties().GetTaggedObject())->IsDictionaryMode()); NameDictionary *dict = NameDictionary::Cast(obj1->GetProperties().GetTaggedObject()); - EXPECT_EQ(dict->EntriesCount(), static_cast(PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES + 1)); + EXPECT_EQ(dict->EntriesCount(), static_cast(PropertyAttributes::MAX_FAST_PROPS_CAPACITY + 1)); EXPECT_EQ(dict->NextEnumerationIndex(thread), - static_cast(PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES + 1)); + static_cast(PropertyAttributes::MAX_FAST_PROPS_CAPACITY + 1)); } HWTEST_F_L0(JSObjectTest, DeleteMiddle) @@ -1317,6 +1317,8 @@ HWTEST_F_L0(JSObjectTest, UpdateWeakTransitions) // Initialize three objects by hc0 JSHandle hc0 = CreateTestHClass(thread); + JSMutableHandle hca(thread, JSTaggedValue::Undefined()); + JSMutableHandle hcb(thread, JSTaggedValue::Undefined()); [[maybe_unused]] JSHandle obj0 = factory->NewJSObject(hc0); // need it to ensure hc0 not be collected JSHandle obj1 = factory->NewJSObject(hc0); JSHandle obj2 = factory->NewJSObject(hc0); @@ -1337,6 +1339,8 @@ HWTEST_F_L0(JSObjectTest, UpdateWeakTransitions) EXPECT_TRUE(hc0->GetTransitions().IsTaggedArray()); EXPECT_EQ(hc0->FindTransitions(keyA.GetTaggedValue(), attr.GetTaggedValue()), obj1->GetClass()); EXPECT_EQ(hc0->FindTransitions(keyB.GetTaggedValue(), attr.GetTaggedValue()), obj2->GetClass()); + hca.Update(JSTaggedValue(obj1->GetClass())); + hcb.Update(JSTaggedValue(obj2->GetClass())); // / hc1 --> hc3 (obj1) // hc0 (obj0) @@ -1348,9 +1352,8 @@ HWTEST_F_L0(JSObjectTest, UpdateWeakTransitions) // collect hc1, hc2 vm->CollectGarbage(TriggerGCType::FULL_GC); - - EXPECT_EQ(hc0->FindTransitions(keyA.GetTaggedValue(), attr.GetTaggedValue()), nullptr); - EXPECT_EQ(hc0->FindTransitions(keyB.GetTaggedValue(), attr.GetTaggedValue()), nullptr); + EXPECT_EQ(hc0->FindTransitions(keyA.GetTaggedValue(), attr.GetTaggedValue()), hca.GetObject()); + EXPECT_EQ(hc0->FindTransitions(keyB.GetTaggedValue(), attr.GetTaggedValue()), hcb.GetObject()); JSHandle obj3 = factory->NewJSObject(hc0); JSHandle obj4 = factory->NewJSObject(hc0); @@ -1364,5 +1367,7 @@ HWTEST_F_L0(JSObjectTest, UpdateWeakTransitions) EXPECT_TRUE(hc0->GetTransitions().IsTaggedArray()); EXPECT_EQ(hc0->FindTransitions(keyA.GetTaggedValue(), attr.GetTaggedValue()), obj3->GetClass()); EXPECT_EQ(hc0->FindTransitions(keyB.GetTaggedValue(), attr.GetTaggedValue()), obj4->GetClass()); + EXPECT_EQ(obj3->GetClass(), hca.GetObject()); + EXPECT_EQ(obj4->GetClass(), hcb.GetObject()); } } // namespace panda::test diff --git a/ecmascript/tests/js_serializer_test.cpp b/ecmascript/tests/js_serializer_test.cpp index 1b134abaaac91d9eaa353a7bd83d142b19c3a7a1..0b16a1e5b7caf6b0ad27db53931d4d54e3556417 100644 --- a/ecmascript/tests/js_serializer_test.cpp +++ b/ecmascript/tests/js_serializer_test.cpp @@ -148,6 +148,26 @@ public: Destroy(); } + void JSPlainObjectTest3(std::pair data) + { + Init(); + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle objValue = deserializer.Deserialize(); + + JSHandle retObj = JSHandle::Cast(objValue); + EXPECT_FALSE(retObj.IsEmpty()); + + std::vector keyVector; + JSObject::GetAllKeysForSerialization(retObj, keyVector); + EXPECT_EQ(keyVector.size(), 2U); // 2 : test case + + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle key(factory->NewFromASCII("y")); + int value = JSObject::GetProperty(thread, JSHandle(retObj), key).GetValue()->GetInt(); + EXPECT_EQ(value, 10U); // 10 : test case + Destroy(); + } + void NativeBindingObjectTest1(std::pair data) { Init(); @@ -730,8 +750,8 @@ public: EXPECT_FALSE(method->IsNativeWithCallField()); JSHandle constpool(thread, method->GetConstantPool()); - EXPECT_EQ(constpool->GetLength(), 3U); - EXPECT_EQ(constpool->GetCacheLength(), 1U); + EXPECT_EQ(constpool->GetLength(), 4U); + EXPECT_EQ(constpool->GetCacheLength(), 2U); const JSPandaFile *jsPandaFile = constpool->GetJSPandaFile(); EXPECT_TRUE(jsPandaFile != nullptr); const CString &desc = jsPandaFile->GetJSPandaFileDesc(); @@ -739,6 +759,23 @@ public: Destroy(); } + void AOTMethodTest(std::pair data) + { + Init(); + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle res = deserializer.Deserialize(); + EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize Method fail"; + EXPECT_TRUE(res.GetTaggedValue().IsMethod()) << "[NotMethod] Deserialize Method fail"; + + JSHandle method = JSHandle::Cast(res); + EXPECT_FALSE(method->IsNativeWithCallField()); + EXPECT_TRUE(method->IsAotWithCallField()); + EXPECT_TRUE(method->IsFastCall()); + uintptr_t codeEntry = method->GetCodeEntryOrLiteral(); + EXPECT_EQ(codeEntry, 0x1234); + Destroy(); + } + void ConcurrentFunctionTest(std::pair data) { Init(); @@ -893,7 +930,26 @@ public: JSDeserializer deserializer(thread, data.first, data.second); JSHandle res = deserializer.Deserialize(); EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize TransferJSArrayBuffer4 fail"; - EXPECT_TRUE(res->IsJSInt8Array()) << "[NotJSArrayBuffer] Deserialize TransferJSArrayBuffer4 fail"; + EXPECT_TRUE(res->IsArrayBuffer()) << "[NotJSArrayBuffer] Deserialize TransferJSArrayBuffer4 fail"; + + JSHandle arrBuf = JSHandle::Cast(res); + EXPECT_EQ(arrBuf->GetArrayBufferByteLength(), 5); // 5: bufferLength + JSHandle nativePtr(thread, arrBuf->GetArrayBufferData()); + EXPECT_TRUE(nativePtr->IsJSNativePointer()) << "[NotJSNativePointer] Deserialize TransferJSArrayBuffer4 fail"; + JSHandle np = JSHandle::Cast(nativePtr); + uintptr_t bufferAddr = reinterpret_cast(np->GetExternalPointer()); + // The deserialized C buffer pointer shall be same to the original one + EXPECT_EQ(static_cast(bufferAddr), static_cast(bufferAddrCheck)); + Destroy(); + } + + void TransferJSArrayBufferTest5(std::pair data, uintptr_t bufferAddrCheck) + { + Init(); + JSDeserializer deserializer(thread, data.first, data.second); + JSHandle res = deserializer.Deserialize(); + EXPECT_TRUE(!res.IsEmpty()) << "[Empty] Deserialize TransferJSArrayBuffer5 fail"; + EXPECT_TRUE(res->IsJSInt8Array()) << "[NotJSArrayBuffer] Deserialize TransferJSArrayBuffer5 fail"; JSHandle resJSInt8Array = JSHandle::Cast(res); @@ -920,7 +976,7 @@ public: EXPECT_EQ(static_cast(resBuffer)[i], i) << "Not same viewedBuffer"; } JSHandle nativePtr(thread, resJSArrayBuffer->GetArrayBufferData()); - EXPECT_TRUE(nativePtr->IsJSNativePointer()) << "[NotJSNativePointer] Deserialize TransferJSArrayBuffer4 fail"; + EXPECT_TRUE(nativePtr->IsJSNativePointer()) << "[NotJSNativePointer] Deserialize TransferJSArrayBuffer5 fail"; JSHandle np = JSHandle::Cast(nativePtr); uintptr_t bufferAddr = reinterpret_cast(np->GetExternalPointer()); // The deserialized C buffer pointer shall be same to the original one @@ -1074,6 +1130,46 @@ HWTEST_F_L0(JSSerializerTest, SerializeJSPlainObject2) delete serializer; }; +HWTEST_F_L0(JSSerializerTest, SerializeJSPlainObject3) +{ + ObjectFactory *factory = ecmaVm->GetFactory(); + const uint32_t numOfProps = 2; + JSHandle layout = factory->CreateLayoutInfo(numOfProps); + + JSHandle key0(factory->NewFromASCII("x")); + PropertyAttributes attributes0 = PropertyAttributes::Default(); + attributes0.SetIsInlinedProps(true); + attributes0.SetRepresentation(Representation::NONE); + attributes0.SetOffset(0); + layout->AddKey(thread, 0, key0.GetTaggedValue(), attributes0); + + JSHandle key1(factory->NewFromASCII("y")); + PropertyAttributes attributes1 = PropertyAttributes::Default(); + attributes1.SetIsInlinedProps(true); + attributes1.SetRepresentation(Representation::NONE); + attributes1.SetOffset(1); + layout->AddKey(thread, 1, key1.GetTaggedValue(), attributes1); + + JSHandle hclass = factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, numOfProps); + hclass->SetLayout(thread, layout); + hclass->SetNumberOfProps(numOfProps); + hclass->SetTS(true); + JSHandle obj = factory->NewJSObject(hclass); + + JSHandle value1(thread, JSTaggedValue(10)); + JSObject::SetProperty(thread, JSHandle(obj), key1, value1); + + JSSerializer *serializer = new JSSerializer(thread); + bool success = serializer->SerializeJSTaggedValue(JSHandle::Cast(obj)); + + EXPECT_TRUE(success); + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::JSPlainObjectTest3, jsDeserializerTest, data); + t1.join(); + delete serializer; +} + static void* detach(void *param1, void *param2, void *hint) { GTEST_LOG_(INFO) << "detach is running"; @@ -1712,7 +1808,7 @@ HWTEST_F_L0(JSSerializerTest, SerializeJSTypedArray2) int byteArrayLength = 10; // 10: arrayLength JSHandle byteArray = factory->NewByteArray(byteArrayLength, sizeof(value)); for (int i = 0; i < byteArrayLength; i++) { - byteArray->Set(i, DataViewType::UINT8, val); + byteArray->Set(thread, i, DataViewType::UINT8, val); } int8Array->SetViewedArrayBufferOrByteArray(thread, byteArray); int byteLength = 10; @@ -1829,6 +1925,46 @@ HWTEST_F_L0(JSSerializerTest, SerializeMethod2) delete serializer; }; +HWTEST_F_L0(JSSerializerTest, SerializeAOTMethod) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + const char *source = R"( + .function void foo() { + lda.str "hello aot" + returnundefined + } + )"; + const CString fileName = "test_aot_method.pa"; + uint32_t methodOffset = 0; + std::shared_ptr pf = CreateJSPandaFile(source, fileName, &methodOffset); + MethodLiteral *methodLiteral = new MethodLiteral(EntityId(methodOffset)); + methodLiteral->SetAotCodeBit(true); + methodLiteral->SetIsFastCall(true); + pf->SetMethodLiteralToMap(methodLiteral); + + JSHandle method = factory->NewMethod(methodLiteral); + EXPECT_TRUE(method.GetTaggedValue().IsMethod()); + JSPandaFileManager::GetInstance()->AddJSPandaFileVm(thread->GetEcmaVM(), pf); + EXPECT_TRUE(pf != nullptr); + + JSHandle constPool = factory->NewConstantPool(4); + constPool->SetJSPandaFile(pf.get()); + EXPECT_TRUE(constPool.GetTaggedValue().IsConstantPool()); + method->SetConstantPool(thread, constPool.GetTaggedValue()); + + uintptr_t codeEntry = 0x1234; + method->SetCodeEntryAndMarkAOT(codeEntry); + + JSSerializer *serializer = new JSSerializer(thread); + bool success = serializer->SerializeJSTaggedValue(JSHandle::Cast(method)); + EXPECT_TRUE(success); + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::AOTMethodTest, jsDeserializerTest, data); + t1.join(); + delete serializer; +}; + // support concurrent function HWTEST_F_L0(JSSerializerTest, SerializeConcurrentFunction) { @@ -2066,6 +2202,37 @@ HWTEST_F_L0(JSSerializerTest, TransferJSArrayBuffer3) EXPECT_FALSE(arrBuf->IsDetach()); }; +// Test default transfer JSArrayBuffer +HWTEST_F_L0(JSSerializerTest, TransferJSArrayBuffer4) +{ + ObjectFactory *factory = ecmaVm->GetFactory(); + + // create a JSArrayBuffer + size_t length = 5; + uint8_t value = 100; + void *buffer = ecmaVm->GetNativeAreaAllocator()->AllocateBuffer(length); + if (memset_s(buffer, length, value, length) != EOK) { + LOG_ECMA(FATAL) << "this branch is unreachable"; + UNREACHABLE(); + } + JSHandle arrBuf = factory->NewJSArrayBuffer(buffer, + length, NativeAreaAllocator::FreeBufferFunc, ecmaVm->GetNativeAreaAllocator()); + JSHandle arrBufTag = JSHandle::Cast(arrBuf); + + JSSerializer *serializer = new JSSerializer(thread); + serializer->SetDefaultTransfer(); + bool success = serializer->SerializeJSTaggedValue(arrBufTag); + EXPECT_TRUE(success); + std::pair data = serializer->ReleaseBuffer(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::TransferJSArrayBufferTest4, jsDeserializerTest, data, + reinterpret_cast(buffer)); + t1.join(); + delete serializer; + // test if detached + EXPECT_TRUE(arrBuf->IsDetach()); +} + // Test JSArrayBuffer transfer mixed with other properties in a typedArray HWTEST_F_L0(JSSerializerTest, TransferJSArrayBufferInTypedArray) { @@ -2097,7 +2264,7 @@ HWTEST_F_L0(JSSerializerTest, TransferJSArrayBufferInTypedArray) EXPECT_TRUE(success); std::pair data = serializer->ReleaseBuffer(); JSDeserializerTest jsDeserializerTest; - std::thread t1(&JSDeserializerTest::TransferJSArrayBufferTest4, jsDeserializerTest, data, buffer); + std::thread t1(&JSDeserializerTest::TransferJSArrayBufferTest5, jsDeserializerTest, data, buffer); t1.join(); delete serializer; // test if detached diff --git a/ecmascript/tests/js_stable_array_test.cpp b/ecmascript/tests/js_stable_array_test.cpp index f9edeccf32e4c241ea933616ef977c9ea10adffc..1161e36e0a662048917e04ff38906f5c9393967c 100644 --- a/ecmascript/tests/js_stable_array_test.cpp +++ b/ecmascript/tests/js_stable_array_test.cpp @@ -18,6 +18,19 @@ using namespace panda; using namespace panda::ecmascript; +constexpr uint32_t ARRAY_LENGTH_4 = 4; +constexpr int32_t INT_VALUE_0 = 0; +constexpr int32_t INT_VALUE_1 = 1; +constexpr int32_t INT_VALUE_2 = 2; +constexpr int32_t INT_VALUE_3 = 3; +constexpr int32_t INT_VALUE_666 = 666; + +enum class StableArrayIndex { + STABLE_ARRAY_INDEX_0, + STABLE_ARRAY_INDEX_1, + STABLE_ARRAY_INDEX_2, + STABLE_ARRAY_INDEX_3 +}; namespace panda::test { class JSStableArrayTest : public testing::Test { @@ -342,4 +355,153 @@ HWTEST_F_L0(JSStableArrayTest, Join_StringElements_DefinedSep) EXPECT_STREQ(EcmaStringAccessor(handleEcmaStrRet).ToCString().c_str(), "a <> a <> a <> a <> a <> a <> a <> a <> a <> a"); } -} // namespace panda::test \ No newline at end of file + +/** + * @tc.name: At + * @tc.desc: Create a source Array whose elements are Numbers and an EcmaRuntimeCallInfo, define the first arg of the + EcmaRuntimeCallInfo an number as the index, check whether the element returned through calling + At function with the source Array and the EcmaRuntimeCallInfo is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +HWTEST_F_L0(JSStableArrayTest, At_NUMBER_INDEX) +{ + ObjectFactory *objFactory = thread->GetEcmaVM()->GetFactory(); + + int32_t lengthArr = 10; + JSHandle handleTagArr(objFactory->NewTaggedArray(lengthArr)); + for (int i = 0; i < lengthArr; i++) { + handleTagArr->Set(thread, i, JSTaggedValue(i)); + } + JSHandle handleArr(JSArray::CreateArrayFromList(thread, handleTagArr)); + auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0)); + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo); + + JSTaggedValue thisTagValue = JSStableArray::At(handleArr, ecmaRuntimeCallInfo); + TestHelper::TearDownFrame(thread, prev); + + EXPECT_EQ(thisTagValue.GetNumber(), 0); + + ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(9)); + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo); + + thisTagValue = JSStableArray::At(handleArr, ecmaRuntimeCallInfo); + TestHelper::TearDownFrame(thread, prev); + + EXPECT_EQ(thisTagValue.GetNumber(), 9); + + ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(-1)); + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo); + + thisTagValue = JSStableArray::At(handleArr, ecmaRuntimeCallInfo); + TestHelper::TearDownFrame(thread, prev); + + EXPECT_EQ(thisTagValue.GetNumber(), 9); + + ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); + ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined()); + ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(10)); + prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo); + + thisTagValue = JSStableArray::At(handleArr, ecmaRuntimeCallInfo); + TestHelper::TearDownFrame(thread, prev); + + EXPECT_EQ(thisTagValue, JSTaggedValue::Undefined()); +} + +/** + * @tc.name: With + * @tc.desc: Create a source Array whose elements are Numbers, define the first arg a thread, + * define the second arg as the source Array, define the third arg an number as the length of source Array + * define the forth arg an number as the index, define the fifth args an number as the value + * check whether the value returned through calling With function with the source Array + * and the args is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +HWTEST_F_L0(JSStableArrayTest, With) +{ + ObjectFactory *objFactory = thread->GetEcmaVM()->GetFactory(); + int32_t lengthArr = ARRAY_LENGTH_4; + JSHandle handleTagArr(objFactory->NewTaggedArray(lengthArr)); + for (int i = 0; i < lengthArr; i++) { + handleTagArr->Set(thread, i, JSTaggedValue(i)); + } + JSHandle handleArr(JSArray::CreateArrayFromList(thread, handleTagArr)); + + int64_t arrayLength = ARRAY_LENGTH_4; + int64_t index = static_cast(StableArrayIndex::STABLE_ARRAY_INDEX_2); + JSTaggedValue resultArr = JSStableArray::With(thread, handleArr, + arrayLength, index, + JSHandle(thread, JSTaggedValue(INT_VALUE_666))); + JSHandle destTaggedValue(thread, resultArr); + JSHandle destArr(destTaggedValue); + JSHandle destTaggedArr(thread, TaggedArray::Cast(destArr->GetElements().GetTaggedObject())); + EXPECT_EQ(handleTagArr->Get( + static_cast(StableArrayIndex::STABLE_ARRAY_INDEX_0)).GetNumber(), INT_VALUE_0); + EXPECT_EQ(handleTagArr->Get( + static_cast(StableArrayIndex::STABLE_ARRAY_INDEX_1)).GetNumber(), INT_VALUE_1); + EXPECT_EQ(handleTagArr->Get( + static_cast(StableArrayIndex::STABLE_ARRAY_INDEX_2)).GetNumber(), INT_VALUE_2); + EXPECT_EQ(handleTagArr->Get( + static_cast(StableArrayIndex::STABLE_ARRAY_INDEX_3)).GetNumber(), INT_VALUE_3); + EXPECT_EQ(destTaggedArr->Get( + static_cast(StableArrayIndex::STABLE_ARRAY_INDEX_0)).GetNumber(), INT_VALUE_0); + EXPECT_EQ(destTaggedArr->Get( + static_cast(StableArrayIndex::STABLE_ARRAY_INDEX_1)).GetNumber(), INT_VALUE_1); + EXPECT_EQ(destTaggedArr->Get( + static_cast(StableArrayIndex::STABLE_ARRAY_INDEX_2)).GetNumber(), INT_VALUE_666); + EXPECT_EQ(destTaggedArr->Get( + static_cast(StableArrayIndex::STABLE_ARRAY_INDEX_3)).GetNumber(), INT_VALUE_3); +} + +/** + * @tc.name: ToReversed + * @tc.desc: Create a source Array whose elements are Numbers and an EcmaRuntimeCallInfo, check whether the + value returned through calling ToReversed function with the source Array is within expectations. + * @tc.type: FUNC + * @tc.require: + */ +HWTEST_F_L0(JSStableArrayTest, ToReversed) +{ + ObjectFactory *objFactory = thread->GetEcmaVM()->GetFactory(); + int32_t lengthArr = ARRAY_LENGTH_4; + JSHandle handleTagArr(objFactory->NewTaggedArray(lengthArr)); + for (int i = 0; i < lengthArr; i++) { + handleTagArr->Set(thread, i, JSTaggedValue(i)); + } + JSHandle handleArr(JSArray::CreateArrayFromList(thread, handleTagArr)); + JSTaggedValue resultArr = + JSStableArray::ToReversed(thread, handleArr, ARRAY_LENGTH_4); + JSHandle destTaggedValue(thread, resultArr); + JSHandle destArr(destTaggedValue); + JSHandle destTaggedArr(thread, TaggedArray::Cast(destArr->GetElements().GetTaggedObject())); + + EXPECT_EQ(handleTagArr->Get( + static_cast(StableArrayIndex::STABLE_ARRAY_INDEX_0)).GetNumber(), INT_VALUE_0); + EXPECT_EQ(handleTagArr->Get( + static_cast(StableArrayIndex::STABLE_ARRAY_INDEX_1)).GetNumber(), INT_VALUE_1); + EXPECT_EQ(handleTagArr->Get( + static_cast(StableArrayIndex::STABLE_ARRAY_INDEX_2)).GetNumber(), INT_VALUE_2); + EXPECT_EQ(handleTagArr->Get( + static_cast(StableArrayIndex::STABLE_ARRAY_INDEX_3)).GetNumber(), INT_VALUE_3); + EXPECT_EQ(destTaggedArr->Get( + static_cast(StableArrayIndex::STABLE_ARRAY_INDEX_0)).GetNumber(), INT_VALUE_3); + EXPECT_EQ(destTaggedArr->Get( + static_cast(StableArrayIndex::STABLE_ARRAY_INDEX_1)).GetNumber(), INT_VALUE_2); + EXPECT_EQ(destTaggedArr->Get( + static_cast(StableArrayIndex::STABLE_ARRAY_INDEX_2)).GetNumber(), INT_VALUE_1); + EXPECT_EQ(destTaggedArr->Get( + static_cast(StableArrayIndex::STABLE_ARRAY_INDEX_3)).GetNumber(), INT_VALUE_0); +} +} // namespace panda::test diff --git a/ecmascript/tests/js_tagged_number_test.cpp b/ecmascript/tests/js_tagged_number_test.cpp index 22ba97f2ab8447149015a542e98a33b0bb4ebc6c..4df0ae967e5ab0fe1d02737288fe7f7ef676eecd 100644 --- a/ecmascript/tests/js_tagged_number_test.cpp +++ b/ecmascript/tests/js_tagged_number_test.cpp @@ -68,9 +68,7 @@ HWTEST_F_L0(JSTaggedNumberTest, TaggedNumber_Minus) EXPECT_EQ(result, JSTaggedNumber(static_cast(246.912))); // number is NAN JSTaggedNumber number5(NAN_VALUE); - JSTaggedNumber number6(-NAN_VALUE); - result = number5 - number6; - EXPECT_EQ(result, JSTaggedNumber(NAN_VALUE * 2)); + EXPECT_FALSE(number5.IsImpureNaN(number5.GetNumber())); } HWTEST_F_L0(JSTaggedNumberTest, TaggedNumber_Multiply) diff --git a/ecmascript/tests/layout_info_test.cpp b/ecmascript/tests/layout_info_test.cpp index 9608dc3ca3b67a8506e6b4b2d381863fb779041a..d00436cbac7b008de63d538b7198b55c724fcf53 100644 --- a/ecmascript/tests/layout_info_test.cpp +++ b/ecmascript/tests/layout_info_test.cpp @@ -154,7 +154,7 @@ HWTEST_F_L0(LayoutInfoTest, GetAllKeys) layoutInfoHandle->AddKey(thread, i, elementsKey.GetTaggedValue(), defaultAttr); } layoutInfoHandle->GetAllKeys(thread, infoLength, 0, *keyArray, objectHandle); // 0: offset - layoutInfoHandle->GetAllKeys(infoLength, keyVector, objectHandle); + layoutInfoHandle->GetAllKeysForSerialization(infoLength, keyVector); EXPECT_EQ(keyArray->GetLength(), keyVector.size()); for (int i = 0;i < infoLength; i++) { @@ -193,4 +193,4 @@ HWTEST_F_L0(LayoutInfoTest, GetAllEnumKeys) EXPECT_EQ(keyArray->Get(0), key3.GetTaggedValue()); EXPECT_EQ(keys, 1U); } -} // namespace panda::ecmascript \ No newline at end of file +} // namespace panda::ecmascript diff --git a/ecmascript/tests/object_operator_test.cpp b/ecmascript/tests/object_operator_test.cpp index a6ea62d25216b27ff1f89ceb5ed5ab5fce569f62..c4720dbb532ee05377160de15754fe8c26d13026 100644 --- a/ecmascript/tests/object_operator_test.cpp +++ b/ecmascript/tests/object_operator_test.cpp @@ -876,7 +876,6 @@ HWTEST_F_L0(ObjectOperatorTest, Property_Add_002) TaggedArray *resultArray = TaggedArray::Cast(handleObject1->GetElements().GetTaggedObject()); EXPECT_EQ(resultArray->Get(elementIndex).GetInt(), 3); EXPECT_EQ(resultArray->GetLength(), 7U); - EXPECT_EQ(handleObject1->GetJSHClass()->GetElementRepresentation(), Representation::INT); // object is not DictionaryMode and not DefaultAttr JSHandle handleObject2 = factory->NewJSObjectByConstructor(JSHandle(objFunc), objFunc); for (int i = 0; i< 4; i++) { diff --git a/ecmascript/tests/test_helper.h b/ecmascript/tests/test_helper.h index 761b3f2ce798aa7a25a8eb0892ddc81164071f9c..372eb765c24fa901b831977c2a199b8ef522dc98 100644 --- a/ecmascript/tests/test_helper.h +++ b/ecmascript/tests/test_helper.h @@ -18,12 +18,13 @@ #include "ecmascript/interpreter/interpreter.h" #include "ecmascript/ecma_runtime_call_info.h" +#include "ecmascript/ecma_string-inl.h" #include "ecmascript/ecma_vm.h" #include "ecmascript/js_function.h" #include "ecmascript/js_handle.h" #include "ecmascript/mem/mem_common.h" #include "ecmascript/napi/include/jsnapi.h" -#include "ecmascript/object_factory.h" +#include "ecmascript/object_factory-inl.h" #include "gtest/gtest.h" namespace panda::test { @@ -101,13 +102,16 @@ public: // If you want to call once create, you can refer to BuiltinsMathTest for detail. static void CreateEcmaVMWithScope(EcmaVM *&instance, JSThread *&thread, EcmaHandleScope *&scope, - bool tryLoadStubFile = false) + bool tryLoadStubFile = false, bool useCInterpreter = false) { JSRuntimeOptions options; options.SetEnableForceGC(true); if (tryLoadStubFile) { options.SetEnableAsmInterpreter(true); } + if (useCInterpreter) { + options.SetEnableAsmInterpreter(false); + } instance = JSNApi::CreateEcmaVM(options); instance->SetEnableForceGC(true); ASSERT_TRUE(instance != nullptr) << "Cannot create EcmaVM"; diff --git a/ecmascript/tests/throw_oom_error_test.cpp b/ecmascript/tests/throw_oom_error_test.cpp index 69bc3b6b9cd804ee9fa884679d440b82dd7286c5..c699edd8076fa304f3a5cc9f5bfd307afee9d22d 100644 --- a/ecmascript/tests/throw_oom_error_test.cpp +++ b/ecmascript/tests/throw_oom_error_test.cpp @@ -55,9 +55,10 @@ public: HWTEST_F_L0(ThrowOOMErrorTest, ThrowNonMovableOOMError) { +#ifdef NDEBUG static constexpr size_t SIZE = 100_KB / 8; [[maybe_unused]] ecmascript::EcmaHandleScope scope(thread); - for (int i = 0; i < 82; i++) { + for (int i = 0; i < 200; i++) { [[maybe_unused]] JSHandle array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(SIZE, JSTaggedValue::Hole(), MemSpaceType::NON_MOVABLE); } @@ -65,6 +66,7 @@ HWTEST_F_L0(ThrowOOMErrorTest, ThrowNonMovableOOMError) EXPECT_TRUE(thread->HasPendingException()); JSType errorType = thread->GetException().GetTaggedObject()->GetClass()->GetObjectType(); EXPECT_EQ(errorType, JSType::JS_OOM_ERROR); +#endif } HWTEST_F_L0(ThrowOOMErrorTest, ThrowOldSpaceMergeOOMError) diff --git a/ecmascript/ts_types/global_ts_type_ref.h b/ecmascript/ts_types/global_ts_type_ref.h index 2e2309011834e1d55520c406d63dbfdd2b09be94..0ce046064e802cd812bdeb81bd0bbdd4da036719 100644 --- a/ecmascript/ts_types/global_ts_type_ref.h +++ b/ecmascript/ts_types/global_ts_type_ref.h @@ -169,12 +169,12 @@ public: FIRST_BIT_FIELD(Type, LocalId, uint32_t, LOCAL_ID_BITS); NEXT_BIT_FIELD(Type, ModuleId, uint32_t, MODULE_ID_BITS, LocalId); - static inline bool IsVaildLocalId(uint32_t localId) + static inline bool IsValidLocalId(uint32_t localId) { return (static_cast(localId) & ~MAX_LOCAL_ID) == 0; } - static inline bool IsVaildModuleId(uint32_t moduleId) + static inline bool IsValidModuleId(uint32_t moduleId) { return (static_cast(moduleId) & ~MAX_MODULE_ID) == 0; } diff --git a/ecmascript/ts_types/global_type_info.h b/ecmascript/ts_types/global_type_info.h new file mode 100644 index 0000000000000000000000000000000000000000..68677e54018d422b087589e06811c67d4aac99b2 --- /dev/null +++ b/ecmascript/ts_types/global_type_info.h @@ -0,0 +1,127 @@ +/* + * 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_TS_TYPES_GLOBAL_TYPE_INFO_H +#define ECMASCRIPT_TS_TYPES_GLOBAL_TYPE_INFO_H + +#include "ecmascript/jspandafile/js_pandafile.h" + +namespace panda::ecmascript { +using PGOSampleType = pgo::PGOSampleType; +using PGORWOpType = pgo::PGORWOpType; +// a unique ID to determine whether the corresponding GT has been generated +class GlobalTypeID { +public: + explicit GlobalTypeID(const JSPandaFile *jsPandaFile, PGOSampleType pgoTypeId) + : jsPandaFile_(jsPandaFile), typeId_(0), pgoTypeId_(pgoTypeId) {} + + explicit GlobalTypeID(const JSPandaFile *jsPandaFile, uint32_t typeId) + : jsPandaFile_(jsPandaFile), typeId_(typeId), pgoTypeId_() {} + + bool operator==(const GlobalTypeID &id) const + { + return jsPandaFile_ == id.jsPandaFile_ && + typeId_ == id.typeId_ && + pgoTypeId_ == id.pgoTypeId_; + } + + const JSPandaFile *GetJSPandaFile() const + { + return jsPandaFile_; + } + + uint32_t GetTypeId() const + { + return typeId_; + } + + PGOSampleType GetPGOTypeId() const + { + return pgoTypeId_; + } + + bool IsPGOType() const + { + return !pgoTypeId_.IsNone(); + } + +private: + const JSPandaFile *jsPandaFile_; + uint32_t typeId_; // the information is recording in the typeliteral + PGOSampleType pgoTypeId_; // the information is recording in the '.ap' file +}; + +struct HashGlobalTypeID { + size_t operator()(const GlobalTypeID &id) const + { + if (id.IsPGOType()) { + return std::hash()(id.GetJSPandaFile()) ^ + std::hash()(id.GetPGOTypeId().GetProfileType().GetRaw()); + } + return std::hash()(id.GetJSPandaFile()) ^ + std::hash()(id.GetTypeId()); + } +}; + +// this class records information about definition points related to class and object types. +class TypeLocation { +public: + explicit TypeLocation(const JSPandaFile *jsPandaFile, uint32_t methodOffset, + int32_t bcIdx) + : jsPandaFile_(jsPandaFile), methodOffset_(methodOffset), bcIdx_(bcIdx) {} + + bool operator==(const TypeLocation &loc) const + { + return jsPandaFile_ == loc.jsPandaFile_ && + methodOffset_ == loc.methodOffset_ && + bcIdx_ == loc.bcIdx_; + } + + int32_t GetBcIdx() const + { + return bcIdx_; + } + + void SetBcIdx(int32_t bcIdx) + { + bcIdx_ = bcIdx; + } + + const JSPandaFile *GetJSPandaFile() const + { + return jsPandaFile_; + } + + uint32_t GetMethodOffset() const + { + return methodOffset_; + } + +private: + const JSPandaFile *jsPandaFile_; + uint32_t methodOffset_; + int32_t bcIdx_; +}; + +struct HashTypeLocation { + size_t operator()(const TypeLocation &loc) const + { + return std::hash()(loc.GetJSPandaFile()) ^ + std::hash()(loc.GetMethodOffset()) ^ + std::hash()(loc.GetBcIdx()); + } +}; +} // panda::ecmascript +#endif // ECMASCRIPT_TS_TYPES_GLOBAL_TYPE_INFO_H diff --git a/ecmascript/ts_types/lib_ark_builtins.d.ts b/ecmascript/ts_types/lib_ark_builtins.d.ts index 86a8f26de83f17ef3637a9329801c7663e085a40..d81c14e1b458e763bbdb05dc184bccf9a5352941 100644 --- a/ecmascript/ts_types/lib_ark_builtins.d.ts +++ b/ecmascript/ts_types/lib_ark_builtins.d.ts @@ -615,7 +615,7 @@ declare class String extends Object { lastIndexOf(searchString: string, position?: number): number; - localeCompare(that: string): number; + localeCompare(that: string, locale?: string, options?: any): number; match(regexp: string | RegExp): RegExpMatchArray | null; diff --git a/ecmascript/ts_types/ts_manager.cpp b/ecmascript/ts_types/ts_manager.cpp index 3c1b42bb243cfa4e431af2c35e2ae79da9c4cea1..5d2ada68742ce6ba37ed84ae5f01ac7a32a50475 100644 --- a/ecmascript/ts_types/ts_manager.cpp +++ b/ecmascript/ts_types/ts_manager.cpp @@ -16,11 +16,13 @@ #include "ecmascript/ts_types/ts_manager.h" #include "ecmascript/compiler/aot_file/aot_file_manager.h" +#include "ecmascript/global_env_constants-inl.h" #include "ecmascript/jspandafile/class_literal.h" #include "ecmascript/jspandafile/js_pandafile_manager.h" #include "ecmascript/jspandafile/program_object.h" #include "ecmascript/jspandafile/type_literal_extractor.h" #include "ecmascript/ts_types/ts_type_table_generator.h" +#include "ecmascript/ts_types/ts_type_parser.h" #include "ecmascript/vtable.h" namespace panda::ecmascript { @@ -38,6 +40,36 @@ void TSManager::Initialize() tableGenerator.GenerateDefaultTSTypeTables(); } +int TSManager::GetElementsIndexByArrayType(const kungfu::GateType &gateType, + const panda_file::File::EntityId id) +{ + // make sure already setting correct curCP_ and curCPID_ before calling this method + if (!IsArrayTypeKind(gateType)) { + return -1; + } + return GetElementsIndex(id); +} + +int TSManager::GetElementsKindIndexByArrayType(const kungfu::GateType &gateType, + const panda_file::File::EntityId id) +{ + // make sure already setting correct curCP_ and curCPID_ before calling this method + if (!IsArrayTypeKind(gateType)) { + return -1; + } + return GetElementsKindIndex(id); +} + +int TSManager::GetHClassIndexByObjectType(const kungfu::GateType &gateType) +{ + // make sure already setting correct curCP_ and curCPID_ before calling this method + if (!IsObjectTypeKind(gateType)) { + return -1; + } + GlobalTSTypeRef objectGT = gateType.GetGTRef(); + return GetHClassIndex(objectGT); +} + int TSManager::GetHClassIndexByInstanceGateType(const kungfu::GateType &gateType) { // make sure already setting correct curCP_ and curCPID_ before calling this method @@ -69,6 +101,72 @@ int TSManager::GetConstructorHClassIndexByClassGateType(const kungfu::GateType & return GetHClassIndex(classGT, true); } +int TSManager::GetElementsIndex(panda_file::File::EntityId id) +{ + // make sure already setting correct curCP_ and curCPID_ before calling this method + auto elementMap = jsArrayData_.GetElmMap(); + auto iter = elementMap.find(id); + auto endIter = elementMap.end(); + if (iter == endIter) { + return -1; + } else { + std::unordered_map &cpIndexMap = iter->second.GetCPIndexMap(); + auto indexIter = cpIndexMap.find(curCPID_); + if (indexIter == cpIndexMap.end()) { + // This elm is used in the current constantpool, but has not yet been recorded + return RecordElmToVecAndIndexMap(iter->second); + } + return indexIter->second; + } +} + +uint32_t TSManager::RecordElmToVecAndIndexMap(ElementData &elmData) +{ + // make sure already setting correct curCP_ and curCPID_ before calling this method + JSHandle constantPool(GetConstantPool()); + CVector &elmVec = snapshotData_.GetSnapshotValVector(curCPID_); + elmVec.emplace_back(elmData.GetELM()); + + uint32_t index = constantPool->GetCacheLength() + elmVec.size() - 1; + std::unordered_map &cpIndexMap = elmData.GetCPIndexMap(); + cpIndexMap[curCPID_] = index; + + return index; +} + +int TSManager::GetElementsKindIndex(panda_file::File::EntityId id) +{ + // make sure already setting correct curCP_ and curCPID_ before calling this method + auto elementKindMap = jsArrayData_.GetElmKindMap(); + auto iter = elementKindMap.find(id); + auto endIter = elementKindMap.end(); + if (iter == endIter) { + return -1; + } else { + std::unordered_map &cpIndexMap = iter->second.GetCPIndexMap(); + auto indexIter = cpIndexMap.find(curCPID_); + if (indexIter == cpIndexMap.end()) { + // This elmKind is used in the current constantpool, but has not yet been recorded + return RecordElmKindToVecAndIndexMap(iter->second); + } + return indexIter->second; + } +} + +uint32_t TSManager::RecordElmKindToVecAndIndexMap(ElementKindData &elmKindData) +{ + // make sure already setting correct curCP_ and curCPID_ before calling this method + JSHandle constantPool(GetConstantPool()); + CVector &kindVec = snapshotData_.GetSnapshotValVector(curCPID_); + kindVec.emplace_back(elmKindData.GetElmKind()); + + uint32_t index = constantPool->GetCacheLength() + kindVec.size() - 1; + std::unordered_map &cpIndexMap = elmKindData.GetCPIndexMap(); + cpIndexMap[curCPID_] = index; + + return index; +} + int TSManager::GetHClassIndex(GlobalTSTypeRef classGT, bool isConstructor) { if (HasOffsetFromGT(classGT)) { @@ -104,7 +202,7 @@ uint32_t TSManager::RecordIhcToVecAndIndexMap(IHClassData &ihcData) { // make sure already setting correct curCP_ and curCPID_ before calling this method JSHandle constantPool(GetConstantPool()); - CVector &hcVec = snapshotData_.GetSnapshotHCVector(curCPID_); + CVector &hcVec = snapshotData_.GetSnapshotValVector(curCPID_); hcVec.emplace_back(ihcData.GetIHC()); uint32_t index = constantPool->GetCacheLength() + hcVec.size() - 1; @@ -114,12 +212,12 @@ uint32_t TSManager::RecordIhcToVecAndIndexMap(IHClassData &ihcData) return index; } -JSTaggedValue TSManager::GetHClassFromCache(uint32_t index) +JSTaggedValue TSManager::GetValueFromCache(uint32_t index) { // make sure already setting correct curCP_ and curCPID_ before calling this method JSHandle constantPool(GetConstantPool()); - const CVector &hcVec = snapshotData_.GetSnapshotHCVector(curCPID_); - return JSTaggedValue(hcVec[index - constantPool->GetCacheLength()]); + const CVector &valVec = snapshotData_.GetSnapshotValVector(curCPID_); + return JSTaggedValue(valVec[index - constantPool->GetCacheLength()]); } JSHandle TSManager::GetExtendedClassType(JSHandle classType) const @@ -205,6 +303,25 @@ bool TSManager::IsStaticFunc(GlobalTSTypeRef gt) const return functionType->GetStatic(); } +bool TSManager::GetSuperGateType(kungfu::GateType &gateType) const +{ + JSHandle type = GetTSType(gateType.GetGTRef()); + if (type->IsTSObjectType()) { + return false; + } + if (type->IsTSClassType()) { + JSHandle classType(type); + if (classType->IsBaseClassType()) { + return false; + } + gateType = kungfu::GateType(classType->GetExtensionGT()); + return true; + } else { + LOG_ECMA(FATAL) << "this branch is unreachable"; + UNREACHABLE(); + } +} + GlobalTSTypeRef TSManager::GetSuperPropType(GlobalTSTypeRef gt, JSHandle propertyName, PropertyType propType) const { @@ -362,7 +479,7 @@ GlobalTSTypeRef TSManager::FindIteratorInstanceInInferTable(GlobalTSTypeRef kind GlobalTSTypeRef TSManager::AddTSTypeToTypeTable(const JSHandle &type, int tableId) const { JSHandle iTable = GetTSTypeTable(tableId); - if (UNLIKELY(!GlobalTSTypeRef::IsVaildLocalId(iTable->GetNumberOfTypes() + 1))) { + if (UNLIKELY(!GlobalTSTypeRef::IsValidLocalId(iTable->GetNumberOfTypes() + 1))) { LOG_COMPILER(DEBUG) << "The maximum number of TSTypes in TSTypeTable " << tableId << " is reached. "; return GlobalTSTypeRef::Default(); } @@ -423,7 +540,8 @@ void TSManager::Iterate(const RootVisitor &v) v(Root::ROOT_VM, ObjectSlot(reinterpret_cast(&globalModuleTable_))); v(Root::ROOT_VM, ObjectSlot(reinterpret_cast(&curCP_))); snapshotData_.Iterate(v); - for (auto iter : gtIhcMap_) { + jsArrayData_.Iterate(v); + for (auto &iter : gtIhcMap_) { iter.second.Iterate(v); } for (auto &exportTable : resolvedExportTable_) { @@ -540,6 +658,17 @@ bool TSManager::CanFastCall(GlobalTSTypeRef gt) const return functionType->GetIsFastCall(); } +bool TSManager::IsNoGC(GlobalTSTypeRef gt) const +{ + if (!IsFunctionTypeKind(gt)) { + return false; + } + JSHandle tsType = GetTSType(gt); + ASSERT(tsType->IsTSFunctionType()); + JSHandle functionType(tsType); + return functionType->GetIsNoGC(); +} + bool TSManager::MethodOffsetIsVaild(GlobalTSTypeRef gt) const { if (!IsFunctionTypeKind(gt)) { @@ -603,6 +732,12 @@ GlobalTSTypeRef TSManager::CreateClassInstanceType(GlobalTSTypeRef gt) return AddTSTypeToInferredTable(JSHandle(classInstanceType)); } +GlobalTSTypeRef TSManager::CreateArrayType() +{ + JSHandle arrayType = factory_->NewTSArrayType(); + return AddTSTypeToInferredTable(JSHandle(arrayType)); +} + GlobalTSTypeRef TSManager::CreateNamespaceType() { JSHandle namespaceType = factory_->NewTSNamespaceType(); @@ -636,6 +771,27 @@ GlobalTSTypeRef TSManager::GetArrayParameterTypeGT(GlobalTSTypeRef gt) const return arrayType->GetElementGT(); } +void TSManager::AddArrayTSElements(panda_file::File::EntityId id, JSTaggedValue elements) +{ + JSHandle elementsHandle(thread_, elements); + ElementData elementData = ElementData(elementsHandle.GetTaggedType()); + jsArrayData_.AddElmMap(id, elementData); +} + +void TSManager::AddArrayTSElementsKind(panda_file::File::EntityId id, JSTaggedValue kind) +{ + JSHandle kindHandle(thread_, kind); + ElementKindData elementKindData = ElementKindData(kindHandle.GetTaggedType()); + jsArrayData_.AddElmKindMap(id, elementKindData); +} + +void TSManager::AddArrayTSConstantIndex(uint64_t bcAbsoluteOffset, JSTaggedValue index) +{ + JSHandle indexHandle(thread_, index); + ConstantIndexData constantIndexData = ConstantIndexData(indexHandle.GetTaggedType()); + jsArrayData_.AddOffConstIndexMap(bcAbsoluteOffset, constantIndexData); +} + void TSManager::AddInstanceTSHClass(GlobalTSTypeRef gt, JSHandle &ihclass) { IHClassData ihcData = IHClassData(ihclass.GetTaggedType()); @@ -650,7 +806,7 @@ void TSManager::AddConstructorTSHClass(GlobalTSTypeRef gt, JSHandle &c JSTaggedValue TSManager::GetTSHClass(const kungfu::GateType &gateType) const { - if (!IsClassTypeKind(gateType)) { + if (!IsClassTypeKind(gateType) && !IsObjectTypeKind(gateType)) { return JSTaggedValue::Hole(); } GlobalTSTypeRef classGT = gateType.GetGTRef(); @@ -694,25 +850,46 @@ JSHandle TSManager::GetTSType(const GlobalTSTypeRef >) const return tsType; } -bool TSManager::IsBuiltinArrayType(kungfu::GateType gateType) const +bool TSManager::IsBuiltinClassType(BuiltinTypeId id, kungfu::GateType gateType) const +{ + if (!IsClassTypeKind(gateType)) { + return false; + } + auto classGT = gateType.GetGTRef(); + if (UNLIKELY(!IsBuiltinsDTSEnabled())) { + uint32_t localId = classGT.GetLocalId(); + return classGT.IsBuiltinModule() && (localId == static_cast(id)); + } + + const JSPandaFile *builtinPandaFile = GetBuiltinPandaFile(); + uint32_t typeOffset = GetBuiltinOffset(static_cast(id)); + GlobalTypeID gId(builtinPandaFile, typeOffset); + if (HasCreatedGT(gId)) { + auto gt = GetGTByGlobalTypeID(gId); + return (gt == classGT); + } + return false; +} + +bool TSManager::IsBuiltinInstanceType(BuiltinTypeId id, kungfu::GateType gateType) const { if (!IsClassInstanceTypeKind(gateType)) { return false; } - const GlobalTSTypeRef gateGT = GlobalTSTypeRef(gateType.Value()); - GlobalTSTypeRef classGT = GetClassType(gateGT); - if (IsBuiltinsDTSEnabled()) { - uint32_t idx = static_cast(BuiltinTypeId::ARRAY); - const JSPandaFile *builtinPandaFile = GetBuiltinPandaFile(); - uint32_t arrayOffset = GetBuiltinOffset(idx); - bool hasCreatedGT = HasCreatedGT(builtinPandaFile, arrayOffset); - if (hasCreatedGT) { - auto gt = GetGTFromOffset(builtinPandaFile, arrayOffset); - return (gt == classGT); - } + auto classGT = GetClassType(gateType.GetGTRef()); + if (UNLIKELY(!IsBuiltinsDTSEnabled())) { + uint32_t localId = classGT.GetLocalId(); + return classGT.IsBuiltinModule() && (localId == static_cast(id)); } - uint32_t l = classGT.GetLocalId(); - return classGT.IsBuiltinModule() && (l == static_cast(BuiltinTypeId::ARRAY)); + + const JSPandaFile *builtinPandaFile = GetBuiltinPandaFile(); + uint32_t typeOffset = GetBuiltinOffset(static_cast(id)); + GlobalTypeID gId(builtinPandaFile, typeOffset); + if (HasCreatedGT(gId)) { + auto gt = GetGTByGlobalTypeID(gId); + return (gt == classGT); + } + return false; } bool TSManager::IsTypedArrayType(kungfu::GateType gateType) const @@ -725,8 +902,9 @@ bool TSManager::IsTypedArrayType(kungfu::GateType gateType) const if (IsBuiltinsDTSEnabled()) { for (uint32_t i = static_cast(BuiltinTypeId::TYPED_ARRAY_FIRST); i <= static_cast(BuiltinTypeId::TYPED_ARRAY_LAST); i++) { - bool hasCreatedGT = HasCreatedGT(GetBuiltinPandaFile(), GetBuiltinOffset(i)); - if (hasCreatedGT && (GetGTFromOffset(GetBuiltinPandaFile(), GetBuiltinOffset(i)) == classGT)) { + GlobalTypeID gId(GetBuiltinPandaFile(), GetBuiltinOffset(i)); + bool hasCreatedGT = HasCreatedGT(gId); + if (hasCreatedGT && (GetGTByGlobalTypeID(gId) == classGT)) { return true; } } @@ -738,20 +916,128 @@ bool TSManager::IsTypedArrayType(kungfu::GateType gateType) const (l <= static_cast(BuiltinTypeId::TYPED_ARRAY_LAST)); } -bool TSManager::IsFloat32ArrayType(kungfu::GateType gateType) const +const std::vector &TSManager::GetValidTypedArrayIds() +{ + static const std::vector validTypedArrayIds = { + BuiltinTypeId::INT8_ARRAY, + BuiltinTypeId::UINT8_ARRAY, + BuiltinTypeId::UINT8_CLAMPED_ARRAY, + BuiltinTypeId::INT16_ARRAY, + BuiltinTypeId::UINT16_ARRAY, + BuiltinTypeId::INT32_ARRAY, + BuiltinTypeId::UINT32_ARRAY, + BuiltinTypeId::FLOAT32_ARRAY, + BuiltinTypeId::FLOAT64_ARRAY + }; + return validTypedArrayIds; +} + +BuiltinTypeId TSManager::GetBuiltinTypeIdByJSType(JSType jsType) +{ + static const std::map jsTypeToBultinTypeID = { + {JSType::JS_INT8_ARRAY, BuiltinTypeId::INT8_ARRAY}, + {JSType::JS_UINT8_ARRAY, BuiltinTypeId::UINT8_ARRAY}, + {JSType::JS_UINT8_CLAMPED_ARRAY, BuiltinTypeId::UINT8_CLAMPED_ARRAY}, + {JSType::JS_INT16_ARRAY, BuiltinTypeId::INT16_ARRAY}, + {JSType::JS_UINT16_ARRAY, BuiltinTypeId::UINT16_ARRAY}, + {JSType::JS_INT32_ARRAY, BuiltinTypeId::INT32_ARRAY}, + {JSType::JS_UINT32_ARRAY, BuiltinTypeId::UINT32_ARRAY}, + {JSType::JS_FLOAT32_ARRAY, BuiltinTypeId::FLOAT32_ARRAY}, + {JSType::JS_FLOAT64_ARRAY, BuiltinTypeId::FLOAT64_ARRAY}, + }; + + auto it = jsTypeToBultinTypeID.find(jsType); + if (it != jsTypeToBultinTypeID.end()) { + return it->second; + } + + return BuiltinTypeId::NUM_INDEX_IN_SUMMARY; +} + +const kungfu::GateType TSManager::GetBuiltinsGateTypeByPt(ProfileType pgoType) +{ + ASSERT(pgoType.IsBuiltinsType()); + JSType jsType = static_cast(pgoType.GetId()); + + auto it = pgoBuiltinGTCache_.find(jsType); + if (it != pgoBuiltinGTCache_.end()) { + return kungfu::GateType(it->second); + } + + BuiltinTypeId bTypeId = GetBuiltinTypeIdByJSType(jsType); + // If some builtin types sampled by pgo are not supported in AOT, AnyType will be returned + if (bTypeId == BuiltinTypeId::NUM_INDEX_IN_SUMMARY) { + return kungfu::GateType::AnyType(); + } + + TSTypeParser parser(this); + GlobalTSTypeRef gt = parser.CreateGT(GetBuiltinPandaFile(), GetBuiltinRecordName(), + static_cast(bTypeId)); + GlobalTSTypeRef instanceGT; + + if (UNLIKELY(!IsBuiltinsDTSEnabled())) { + JSHandle classInstanceType = factory_->NewTSClassInstanceType(); + classInstanceType->SetClassGT(gt); + instanceGT = AddTSTypeToInferredTable(JSHandle(classInstanceType)); + } else { + instanceGT = CreateClassInstanceType(gt); + } + + pgoBuiltinGTCache_.insert({jsType, instanceGT}); + return kungfu::GateType(instanceGT); +} + +BuiltinTypeId TSManager::GetTypedArrayBuiltinId(kungfu::GateType gateType) const { if (!IsClassInstanceTypeKind(gateType)) { - return false; + return BuiltinTypeId::NUM_INDEX_IN_SUMMARY; } const GlobalTSTypeRef gateGT = GlobalTSTypeRef(gateType.Value()); GlobalTSTypeRef classGT = GetClassType(gateGT); - if (IsBuiltinsDTSEnabled()) { - uint32_t idx = static_cast(BuiltinTypeId::FLOAT32_ARRAY); - return (HasCreatedGT(GetBuiltinPandaFile(), GetBuiltinOffset(idx))) && - (GetGTFromOffset(GetBuiltinPandaFile(), GetBuiltinOffset(idx)) == classGT); + const auto pandaFile = GetBuiltinPandaFile(); + for (uint32_t i = static_cast(BuiltinTypeId::TYPED_ARRAY_FIRST); + i <= static_cast(BuiltinTypeId::TYPED_ARRAY_LAST); i++) { + if (IsBuiltinsDTSEnabled()) { + const auto offset = GetBuiltinOffset(i); + GlobalTypeID gId(pandaFile, offset); + if ((HasCreatedGT(gId)) && + (GetGTByGlobalTypeID(gId) == classGT)) { + return static_cast(i); + } + } + uint32_t l = classGT.GetLocalId(); + if (classGT.IsBuiltinModule() && (l == i)) { + return static_cast(i); + } } - uint32_t l = classGT.GetLocalId(); - return classGT.IsBuiltinModule() && (l == static_cast(BuiltinTypeId::FLOAT32_ARRAY)); + return BuiltinTypeId::NUM_INDEX_IN_SUMMARY; +} + +bool TSManager::IsValidTypedArrayType(kungfu::GateType gateType) const +{ + std::vector ids = GetValidTypedArrayIds(); + for (const auto &id : ids) { + if (IsBuiltinInstanceType(id, gateType)) { + return true; + } + } + return false; +} + +bool TSManager::IsIntTypedArrayType(kungfu::GateType gateType) const +{ + return IsBuiltinInstanceType(BuiltinTypeId::INT8_ARRAY, gateType) || + IsBuiltinInstanceType(BuiltinTypeId::UINT8_ARRAY, gateType) || + IsBuiltinInstanceType(BuiltinTypeId::UINT8_CLAMPED_ARRAY, gateType) || + IsBuiltinInstanceType(BuiltinTypeId::INT16_ARRAY, gateType) || + IsBuiltinInstanceType(BuiltinTypeId::UINT16_ARRAY, gateType) || + IsBuiltinInstanceType(BuiltinTypeId::INT32_ARRAY, gateType); +} + +bool TSManager::IsDoubleTypedArrayType(kungfu::GateType gateType) const +{ + return IsBuiltinInstanceType(BuiltinTypeId::FLOAT32_ARRAY, gateType) || + IsBuiltinInstanceType(BuiltinTypeId::FLOAT64_ARRAY, gateType); } std::string TSManager::GetBuiltinsName(uint32_t index) const @@ -893,8 +1179,9 @@ uint32_t TSManager::GetBuiltinIndex(GlobalTSTypeRef builtinGT) const if (IsBuiltinsDTSEnabled()) { for (uint32_t idx = static_cast(BuiltinTypeId::FUNCTION); idx <= static_cast(BuiltinTypeId::INTL); idx++) { - if ((HasCreatedGT(GetBuiltinPandaFile(), GetBuiltinOffset(idx))) && - (GetGTFromOffset(GetBuiltinPandaFile(), GetBuiltinOffset(idx)) == builtinGT)) { + GlobalTypeID gId(GetBuiltinPandaFile(), GetBuiltinOffset(idx)); + if ((HasCreatedGT(gId)) && + (GetGTByGlobalTypeID(gId) == builtinGT)) { return idx; } } @@ -1022,11 +1309,11 @@ std::string TSManager::GetPrimitiveStr(const GlobalTSTypeRef >) const void TSManager::SetCurConstantPool(const JSPandaFile *jsPandaFile, uint32_t methodOffset) { - curCPID_ = GetOldConstantPoolIDByMethodOffset(jsPandaFile, methodOffset); + curCPID_ = GetConstantPoolIDByMethodOffset(jsPandaFile, methodOffset); curCP_ = vm_->GetJSThread()->GetCurrentEcmaContext()->FindConstpool(jsPandaFile, curCPID_); } -int32_t TSManager::GetOldConstantPoolIDByMethodOffset(const JSPandaFile *jsPandaFile, uint32_t methodOffset) +int32_t TSManager::GetConstantPoolIDByMethodOffset(const JSPandaFile *jsPandaFile, uint32_t methodOffset) { panda_file::IndexAccessor indexAccessor(*jsPandaFile->GetPandaFile(), panda_file::File::EntityId(methodOffset)); @@ -1047,7 +1334,7 @@ void TSManager::ProcessSnapshotConstantPool(kungfu::BytecodeInfoCollector *bcInf GenerateSnapshotConstantPoolList(cpListIndexMap, oldCPValues); FillSnapshotConstantPoolList(cpListIndexMap, bcInfoCollector); - AddHClassToSnapshotConstantPoolList(cpListIndexMap, bcInfoCollector); + AddValueToSnapshotConstantPoolList(cpListIndexMap, bcInfoCollector); } void TSManager::GenerateSnapshotConstantPoolList(std::map &cpListIndexMap, @@ -1064,14 +1351,26 @@ void TSManager::GenerateSnapshotConstantPoolList(std::map &cp int32_t oldCPID = iter.first; oldCP.Update(iter.second); uint32_t cpSize = oldCP->GetCacheLength(); - const CVector &hcVec = snapshotData_.GetSnapshotHCVector(oldCPID); - uint32_t hcVecSize = hcVec.size(); + const CVector &valVec = snapshotData_.GetSnapshotValVector(oldCPID); + uint32_t valVecSize = valVec.size(); if (vm_->GetJSOptions().IsEnableCompilerLogSnapshot()) { LOG_COMPILER(INFO) << "[aot-snapshot] constantPoolID: " << oldCPID; LOG_COMPILER(INFO) << "[aot-snapshot] constantPoolSize: " << cpSize; - LOG_COMPILER(INFO) << "[aot-snapshot] hclassSize: " << hcVecSize; + LOG_COMPILER(INFO) << "[aot-snapshot] valueSize: " << valVecSize; + } + // 1: constIndexInfo TaggedArray + JSHandle newCp = factory_->NewConstantPool(cpSize + valVecSize); + + auto offConstIndexMap = jsArrayData_.GetOffConstIndexMap(); + // 2: each item need store (bcAbsoluteOffset, constIndex) + JSHandle constIndexInfoList = + factory_->NewTaggedArray(offConstIndexMap.size() * SnapshotData::SNAPSHOT_CP_LIST_ITEM_SIZE); + uint32_t idx = 0; + for (auto &x : offConstIndexMap) { + constIndexInfoList->Set(thread_, idx++, JSTaggedValue(static_cast(x.first))); + constIndexInfoList->Set(thread_, idx++, JSTaggedValue(x.second.GetIndex())); } - JSHandle newCp = factory_->NewConstantPool(cpSize + hcVecSize); + newCp->SetObjectToCache(thread_, cpSize + valVecSize, constIndexInfoList.GetTaggedValue()); snapshotCPList->Set(thread_, pos++, JSTaggedValue(oldCPID)); cpListIndexMap[oldCPID] = pos; @@ -1079,6 +1378,23 @@ void TSManager::GenerateSnapshotConstantPoolList(std::map &cp } } +void TSManager::TryGetIhcAndChc(GlobalTSTypeRef gt, JSHandle &ihc, JSHandle &chc) +{ + if (gt.IsDefault()) { + return; + } + auto ihcIt = gtIhcMap_.find(gt); + if (ihcIt != gtIhcMap_.end()) { + IHClassData ihcData = ihcIt->second; + ihc = JSHandle(thread_, JSTaggedValue(ihcData.GetIHC())); + } + auto chcIt = gtConstructorhcMap_.find(gt); + if (chcIt != gtConstructorhcMap_.end()) { + IHClassData chcData = chcIt->second; + chc = JSHandle(thread_, JSTaggedValue(chcData.GetIHC())); + } +} + void TSManager::FillSnapshotConstantPoolList(const std::map &cpListIndexMap, kungfu::BytecodeInfoCollector *bcInfoCollector) { @@ -1086,7 +1402,7 @@ void TSManager::FillSnapshotConstantPoolList(const std::map & bcInfoCollector->IterateConstantPoolInfo(kungfu::ConstantPoolInfo::ItemType::STRING, [this, jsPandaFile, &cpListIndexMap] (const kungfu::ConstantPoolInfo::ItemData &data) { - int32_t oldCPID = GetOldConstantPoolIDByMethodOffset(jsPandaFile, data.outerMethodOffset); + int32_t oldCPID = GetConstantPoolIDByMethodOffset(jsPandaFile, data.outerMethodOffset); JSTaggedValue oldCP = vm_->GetJSThread()->GetCurrentEcmaContext()->FindConstpool(jsPandaFile, oldCPID); JSTaggedValue str = ConstantPool::GetStringFromCache(thread_, oldCP, data.index); @@ -1098,7 +1414,7 @@ void TSManager::FillSnapshotConstantPoolList(const std::map & bcInfoCollector->IterateConstantPoolInfo(kungfu::ConstantPoolInfo::ItemType::METHOD, [this, jsPandaFile, &cpListIndexMap, bcInfoCollector] (const kungfu::ConstantPoolInfo::ItemData &data) { - int32_t oldCPID = GetOldConstantPoolIDByMethodOffset(jsPandaFile, data.outerMethodOffset); + int32_t oldCPID = GetConstantPoolIDByMethodOffset(jsPandaFile, data.outerMethodOffset); JSHandle oldCP(thread_, vm_->GetJSThread()->GetCurrentEcmaContext()->FindConstpool(jsPandaFile, oldCPID)); @@ -1114,8 +1430,9 @@ void TSManager::FillSnapshotConstantPoolList(const std::map & }); bcInfoCollector->IterateConstantPoolInfo(kungfu::ConstantPoolInfo::ItemType::CLASS_LITERAL, - [this, jsPandaFile, &cpListIndexMap, bcInfoCollector] (const kungfu::ConstantPoolInfo::ItemData &data) { - int32_t oldCPID = GetOldConstantPoolIDByMethodOffset(jsPandaFile, data.outerMethodOffset); + [this, jsPandaFile, &cpListIndexMap, bcInfoCollector] + (const kungfu::ConstantPoolInfo::ItemData &data) { + int32_t oldCPID = GetConstantPoolIDByMethodOffset(jsPandaFile, data.outerMethodOffset); JSHandle oldCP(thread_, vm_->GetJSThread()->GetCurrentEcmaContext()->FindConstpool(jsPandaFile, oldCPID)); @@ -1125,14 +1442,20 @@ void TSManager::FillSnapshotConstantPoolList(const std::map & uint32_t cpListIndex = cpListIndexMap.at(oldCPID); JSHandle newCP = GetSnapshotConstantPool(cpListIndex); - CollectLiteralInfo(arrayHandle, data.index, newCP, bcInfoCollector); + JSHandle ihc = thread_->GlobalConstants()->GetHandledUndefined(); + JSHandle chc = thread_->GlobalConstants()->GetHandledUndefined(); + TypeLocation loc(jsPandaFile, data.outerMethodOffset, data.bcIndex); + GlobalTSTypeRef gt = GetLiteralGT(loc); + ASSERT(gt.IsDefault() || IsUserDefinedClassTypeKind(gt)); + TryGetIhcAndChc(gt, ihc, chc); + CollectLiteralInfo(arrayHandle, data.index, newCP, bcInfoCollector, ihc, chc); snapshotData_.AddIndexInfoToRecordInfo(SnapshotData::RecordType::LITERAL, std::make_pair(cpListIndex, data.index)); }); bcInfoCollector->IterateConstantPoolInfo(kungfu::ConstantPoolInfo::ItemType::OBJECT_LITERAL, [this, jsPandaFile, &cpListIndexMap, bcInfoCollector] (const kungfu::ConstantPoolInfo::ItemData &data) { - int32_t oldCPID = GetOldConstantPoolIDByMethodOffset(jsPandaFile, data.outerMethodOffset); + int32_t oldCPID = GetConstantPoolIDByMethodOffset(jsPandaFile, data.outerMethodOffset); JSHandle oldCP(thread_, vm_->GetJSThread()->GetCurrentEcmaContext()->FindConstpool(jsPandaFile, oldCPID)); @@ -1144,14 +1467,20 @@ void TSManager::FillSnapshotConstantPoolList(const std::map & uint32_t cpListIndex = cpListIndexMap.at(oldCPID); JSHandle newCP = GetSnapshotConstantPool(cpListIndex); - CollectLiteralInfo(properties, data.index, newCP, bcInfoCollector); + JSHandle ihc = thread_->GlobalConstants()->GetHandledUndefined(); + JSHandle chc = thread_->GlobalConstants()->GetHandledUndefined(); + TypeLocation loc(jsPandaFile, data.outerMethodOffset, data.bcIndex); + GlobalTSTypeRef gt = GetLiteralGT(loc); + ASSERT(gt.IsDefault() || IsObjectTypeKind(gt)); + TryGetIhcAndChc(gt, ihc, chc); + CollectLiteralInfo(properties, data.index, newCP, bcInfoCollector, ihc, chc); snapshotData_.AddIndexInfoToRecordInfo(SnapshotData::RecordType::LITERAL, std::make_pair(cpListIndex, data.index)); }); bcInfoCollector->IterateConstantPoolInfo(kungfu::ConstantPoolInfo::ItemType::ARRAY_LITERAL, [this, jsPandaFile, &cpListIndexMap, bcInfoCollector] (const kungfu::ConstantPoolInfo::ItemData &data) { - int32_t oldCPID = GetOldConstantPoolIDByMethodOffset(jsPandaFile, data.outerMethodOffset); + int32_t oldCPID = GetConstantPoolIDByMethodOffset(jsPandaFile, data.outerMethodOffset); JSHandle oldCP(thread_, vm_->GetJSThread()->GetCurrentEcmaContext()->FindConstpool(jsPandaFile, oldCPID)); @@ -1161,14 +1490,16 @@ void TSManager::FillSnapshotConstantPoolList(const std::map & uint32_t cpListIndex = cpListIndexMap.at(oldCPID); JSHandle newCP = GetSnapshotConstantPool(cpListIndex); - CollectLiteralInfo(literal, data.index, newCP, bcInfoCollector); + JSHandle ihc = thread_->GlobalConstants()->GetHandledUndefined(); + JSHandle chc = thread_->GlobalConstants()->GetHandledUndefined(); + CollectLiteralInfo(literal, data.index, newCP, bcInfoCollector, ihc, chc); snapshotData_.AddIndexInfoToRecordInfo(SnapshotData::RecordType::LITERAL, std::make_pair(cpListIndex, data.index)); }); } -void TSManager::AddHClassToSnapshotConstantPoolList(const std::map &cpListIndexMap, - kungfu::BytecodeInfoCollector *bcInfoCollector) +void TSManager::AddValueToSnapshotConstantPoolList(const std::map &cpListIndexMap, + kungfu::BytecodeInfoCollector *bcInfoCollector) { const JSPandaFile *jsPandaFile = bcInfoCollector->GetJSPandaFile(); JSMutableHandle oldCP(thread_, thread_->GlobalConstants()->GetUndefined()); @@ -1181,17 +1512,18 @@ void TSManager::AddHClassToSnapshotConstantPoolList(const std::map &hcVec = snapshotData_.GetSnapshotHCVector(oldCPID); - uint32_t hcVecSize = hcVec.size(); - for (uint32_t i = 0; i < hcVecSize; ++i) { - newCP->SetObjectToCache(thread_, constantPoolSize + i, JSTaggedValue(hcVec[i])); + const CVector &valVec = snapshotData_.GetSnapshotValVector(oldCPID); + uint32_t valVecSize = valVec.size(); + for (uint32_t i = 0; i < valVecSize; ++i) { + newCP->SetObjectToCache(thread_, constantPoolSize + i, JSTaggedValue(valVec[i])); } } } void TSManager::CollectLiteralInfo(JSHandle array, uint32_t constantPoolIndex, JSHandle snapshotConstantPool, - kungfu::BytecodeInfoCollector *bcInfoCollector) + kungfu::BytecodeInfoCollector *bcInfoCollector, + JSHandle ihc, JSHandle chc) { JSMutableHandle valueHandle(thread_, JSTaggedValue::Undefined()); uint32_t len = array->GetLength(); @@ -1212,7 +1544,15 @@ void TSManager::CollectLiteralInfo(JSHandle array, uint32_t constan JSHandle aotLiteralInfo = factory_->NewAOTLiteralInfo(methodSize); for (uint32_t i = 0; i < methodSize; ++i) { auto methodOffset = methodOffsetVec[i]; - aotLiteralInfo->Set(thread_, i, JSTaggedValue(methodOffset)); + aotLiteralInfo->SetObjectToCache(thread_, i, JSTaggedValue(methodOffset)); + } + + if (!ihc->IsUndefined()) { + aotLiteralInfo->SetIhc(ihc.GetTaggedValue()); + } + + if (!chc->IsUndefined()) { + aotLiteralInfo->SetChc(chc.GetTaggedValue()); } snapshotConstantPool->SetObjectToCache(thread_, constantPoolIndex, aotLiteralInfo.GetTaggedValue()); @@ -1231,8 +1571,10 @@ void TSManager::ResolveSnapshotConstantPool(const std::map & if (vm_->GetJSOptions().IsEnableCompilerLogSnapshot()) { LOG_COMPILER(INFO) << "[aot-snapshot] store AOT entry index of method (offset: " << methodOffset << ") "; } - uint32_t entryIndex = methodToEntryIndexMap.at(methodOffset); - newCP->SetObjectToCache(thread_, methodIndex, JSTaggedValue(entryIndex)); + if (methodToEntryIndexMap.find(methodOffset) != methodToEntryIndexMap.end()) { + uint32_t entryIndex = methodToEntryIndexMap.at(methodOffset); + newCP->SetObjectToCache(thread_, methodIndex, JSTaggedValue(entryIndex)); + } } auto &recordLiteralInfo = snapshotData_.GetRecordInfo(SnapshotData::RecordType::LITERAL); @@ -1243,9 +1585,9 @@ void TSManager::ResolveSnapshotConstantPool(const std::map & JSTaggedValue val = newCP->GetObjectFromCache(literalIndex); AOTLiteralInfo *aotLiteralInfo = AOTLiteralInfo::Cast(val.GetTaggedObject()); - uint32_t aotLiteralInfoLen = aotLiteralInfo->GetLength(); + uint32_t aotLiteralInfoLen = aotLiteralInfo->GetCacheLength(); for (uint32_t i = 0; i < aotLiteralInfoLen; ++i) { - JSTaggedValue methodOffsetVal = aotLiteralInfo->Get(i); + JSTaggedValue methodOffsetVal = aotLiteralInfo->GetObjectFromCache(i); if (methodOffsetVal.GetInt() == -1) { continue; } @@ -1255,7 +1597,7 @@ void TSManager::ResolveSnapshotConstantPool(const std::map & << methodOffset << ") "; } uint32_t entryIndex = methodToEntryIndexMap.at(methodOffset); - aotLiteralInfo->Set(thread_, i, JSTaggedValue(entryIndex)); + aotLiteralInfo->SetObjectToCache(thread_, i, JSTaggedValue(entryIndex)); } } } @@ -1330,7 +1672,7 @@ JSHandle TSManager::GenerateExportTableFromLiteral(const JSPandaFil return typeOfExportedSymbols; } -bool TSManager::IsBuiltinMath(kungfu::GateType funcType) const +bool TSManager::IsBuiltinObjectMethod(BuiltinTypeId id, kungfu::GateType funcType) const { DISALLOW_GARBAGE_COLLECTION; GlobalTSTypeRef funcGT = funcType.GetGTRef(); @@ -1339,17 +1681,18 @@ bool TSManager::IsBuiltinMath(kungfu::GateType funcType) const } if (IsBuiltinsDTSEnabled()) { - uint32_t idx = static_cast(BuiltinTypeId::MATH); + uint32_t idx = static_cast(id); const JSPandaFile *builtinPandaFile = GetBuiltinPandaFile(); - uint32_t mathOffset = GetBuiltinOffset(idx); - bool hasCreatedGT = HasCreatedGT(builtinPandaFile, mathOffset); + uint32_t builtinOffset = GetBuiltinOffset(idx); + GlobalTypeID gId(builtinPandaFile, builtinOffset); + bool hasCreatedGT = HasCreatedGT(gId); if (hasCreatedGT) { JSHandle funcTsType = GetTSType(funcGT); ASSERT(funcTsType->IsTSFunctionType()); JSHandle functionType = JSHandle(funcTsType); auto name = functionType->GetName(); - auto gt = GetGTFromOffset(builtinPandaFile, mathOffset); + auto gt = GetGTByGlobalTypeID(gId); auto tsType = GetTSType(gt); ASSERT(tsType->IsTSClassType()); JSHandle classType(tsType); @@ -1358,20 +1701,22 @@ bool TSManager::IsBuiltinMath(kungfu::GateType funcType) const TSObjLayoutInfo *itLayout = TSObjLayoutInfo::Cast(layout.GetTaggedObject()); int index = itLayout->GetElementIndexByKey(name); if (index != -1) { - auto mathFuncGt = GlobalTSTypeRef(itLayout->GetTypeId(index).GetInt()); - return mathFuncGt == funcGT; + auto builtinFuncGt = GlobalTSTypeRef(itLayout->GetTypeId(index).GetInt()); + return builtinFuncGt == funcGT; + } + JSHandle prototypeType(thread_, classType->GetPrototypeType()); + JSTaggedValue prototypeLayout = prototypeType->GetObjLayoutInfo(); + TSObjLayoutInfo *pPrototypeLayout = TSObjLayoutInfo::Cast(prototypeLayout.GetTaggedObject()); + index = pPrototypeLayout->GetElementIndexByKey(name); + if (index != -1) { + auto builtinFuncGt = GlobalTSTypeRef(pPrototypeLayout->GetTypeId(index).GetInt()); + return builtinFuncGt == funcGT; } } } return false; } -bool TSManager::IsBuiltin(kungfu::GateType funcType) const -{ - GlobalTSTypeRef funcGt = funcType.GetGTRef(); - return funcGt.IsBuiltinModule(); -} - void TSManager::GenerateBuiltinSummary() { ASSERT(IsBuiltinsDTSEnabled()); diff --git a/ecmascript/ts_types/ts_manager.h b/ecmascript/ts_types/ts_manager.h index 0c3b28a7575ed11d784db56c14c290e0114200ce..08036c3ad9d843ad428a65f2db252936dbb6e88f 100644 --- a/ecmascript/ts_types/ts_manager.h +++ b/ecmascript/ts_types/ts_manager.h @@ -22,8 +22,10 @@ #include "ecmascript/js_tagged_value-inl.h" #include "ecmascript/ts_types/global_ts_type_ref.h" #include "ecmascript/ts_types/ts_obj_layout_info.h" +#include "ecmascript/ts_types/global_type_info.h" namespace panda::ecmascript { +using ProfileType = pgo::ProfileType; enum class PropertyType : uint8_t { NORMAL = 0, STATIC, @@ -265,6 +267,8 @@ public: bool PUBLIC_API IsStaticFunc(GlobalTSTypeRef gt) const; + bool PUBLIC_API GetSuperGateType(kungfu::GateType &gateType) const; + GlobalTSTypeRef PUBLIC_API GetSuperPropType(GlobalTSTypeRef gt, JSHandle propertyName, PropertyType propType) const; @@ -295,6 +299,7 @@ public: bool IsMethodSignature(GlobalTSTypeRef gt) const; bool CanFastCall(GlobalTSTypeRef gt) const; + bool IsNoGC(GlobalTSTypeRef gt) const; bool MethodOffsetIsVaild(GlobalTSTypeRef gt) const; bool FastCallFlagIsVaild(GlobalTSTypeRef gt) const; @@ -339,6 +344,12 @@ public: return CString(fileName); } + void AddArrayTSElements(panda_file::File::EntityId id, JSTaggedValue elements); + + void AddArrayTSElementsKind(panda_file::File::EntityId id, JSTaggedValue kind); + + void AddArrayTSConstantIndex(uint64_t bcAbsoluteOffset, JSTaggedValue index); + void AddInstanceTSHClass(GlobalTSTypeRef gt, JSHandle &ihclass); void AddConstructorTSHClass(GlobalTSTypeRef gt, JSHandle &constructorHClass); @@ -353,6 +364,14 @@ public: std::string PUBLIC_API GetTypeStr(kungfu::GateType gateType) const; + int PUBLIC_API GetElementsIndexByArrayType(const kungfu::GateType &gateType, + const panda_file::File::EntityId id); + + int PUBLIC_API GetElementsKindIndexByArrayType(const kungfu::GateType &gateType, + const panda_file::File::EntityId id); + + int PUBLIC_API GetHClassIndexByObjectType(const kungfu::GateType &gateType); + int PUBLIC_API GetHClassIndexByInstanceGateType(const kungfu::GateType &gateType); int PUBLIC_API GetConstructorHClassIndexByClassGateType(const kungfu::GateType &gateType); @@ -361,7 +380,9 @@ public: JSTaggedValue GetTSHClass(const kungfu::GateType &gateType) const; - JSTaggedValue PUBLIC_API GetHClassFromCache(uint32_t index); + JSTaggedValue PUBLIC_API GetValueFromCache(uint32_t index); + + GlobalTSTypeRef PUBLIC_API CreateArrayType(); GlobalTSTypeRef PUBLIC_API CreateNamespaceType(); @@ -432,38 +453,50 @@ public: IS_TSTYPEKIND_METHOD_LIST(IS_TSTYPEKIND) #undef IS_TSTYPEKIND - bool PUBLIC_API IsBuiltinArrayType(kungfu::GateType gateType) const; + bool PUBLIC_API IsBuiltinClassType(BuiltinTypeId id, kungfu::GateType gateType) const; + + bool PUBLIC_API IsBuiltinInstanceType(BuiltinTypeId id, kungfu::GateType gateType) const; bool PUBLIC_API IsTypedArrayType(kungfu::GateType gateType) const; - bool PUBLIC_API IsFloat32ArrayType(kungfu::GateType gateType) const; + bool PUBLIC_API IsValidTypedArrayType(kungfu::GateType gateType) const; - inline void AddElementToLiteralOffsetGTMap(const JSPandaFile *jsPandaFile, uint32_t offset, - const CString &recordName, GlobalTSTypeRef gt, - bool isImportType = false) + inline bool PUBLIC_API IsBuiltinObjectType(kungfu::GateType gateType) const { - auto key = std::make_pair(jsPandaFile, offset); - if (literalOffsetGTMap_.find(key) != literalOffsetGTMap_.end()) { - literalOffsetGTMap_[key] = gt; + return gateType.GetGTRef().IsBuiltinModule() && IsClassTypeKind(gateType); + } + + bool PUBLIC_API IsIntTypedArrayType(kungfu::GateType gateType) const; + + bool PUBLIC_API IsDoubleTypedArrayType(kungfu::GateType gateType) const; + + BuiltinTypeId PUBLIC_API GetTypedArrayBuiltinId(kungfu::GateType gateType) const; + + static const std::vector &GetValidTypedArrayIds(); + + inline void AddElementToIdGTMap(const GlobalTypeID &id, GlobalTSTypeRef gt, + const CString &recordName = "", bool isImportType = false) + { + auto it = idGTMap_.find(id); + if (it != idGTMap_.end()) { + it->second = gt; } else { - literalOffsetGTMap_.emplace(key, gt); + idGTMap_.emplace(id, gt); } - if (!isImportType) { - auto value = std::make_pair(recordName, offset); + if (!isImportType && !id.IsPGOType()) { + auto value = std::make_pair(recordName, id.GetTypeId()); gtLiteralOffsetMap_.emplace(gt, value); } } - inline bool HasCreatedGT(const JSPandaFile *jsPandaFile, uint32_t offset) const + inline bool HasCreatedGT(const GlobalTypeID &id) const { - auto key = std::make_pair(jsPandaFile, offset); - return literalOffsetGTMap_.find(key) != literalOffsetGTMap_.end(); + return idGTMap_.find(id) != idGTMap_.end(); } - inline GlobalTSTypeRef GetGTFromOffset(const JSPandaFile *jsPandaFile, uint32_t offset) const + inline GlobalTSTypeRef GetGTByGlobalTypeID(const GlobalTypeID &id) const { - auto key = std::make_pair(jsPandaFile, offset); - return literalOffsetGTMap_.at(key); + return idGTMap_.at(id); } inline bool HasOffsetFromGT(GlobalTSTypeRef gt) const @@ -538,6 +571,8 @@ public: void PUBLIC_API SetCurConstantPool(const JSPandaFile *jsPandaFile, uint32_t methodOffset); + int32_t PUBLIC_API GetConstantPoolIDByMethodOffset(const JSPandaFile *jsPandaFile, uint32_t methodOffset); + JSHandle PUBLIC_API GetConstantPool() const { return JSHandle(uintptr_t(&curCP_)); @@ -551,9 +586,7 @@ public: return EcmaStringAccessor(str).ToStdString(StringConvertedUsage::LOGICOPERATION); } - bool PUBLIC_API IsBuiltin(kungfu::GateType funcType) const; - - bool PUBLIC_API IsBuiltinMath(kungfu::GateType funcType) const; + bool PUBLIC_API IsBuiltinObjectMethod(BuiltinTypeId id, kungfu::GateType funcType) const; inline const JSPandaFile *GetBuiltinPandaFile() const { @@ -575,6 +608,78 @@ public: bcInfoCollector_ = bcInfoCollector; } + class ElementData { + public: + explicit ElementData(JSTaggedType element) : element_(element) {} + + void Iterate(const RootVisitor &v) + { + v(Root::ROOT_VM, ObjectSlot(reinterpret_cast(&element_))); + } + + std::unordered_map& GetCPIndexMap() + { + return cpIndexMap_; + } + + JSTaggedType GetELM() const + { + return element_; + } + + private: + JSTaggedType element_ {0}; + std::unordered_map cpIndexMap_ {}; + }; + + class ElementKindData { + public: + explicit ElementKindData(JSTaggedType kind) : kind_(kind) {} + + void Iterate(const RootVisitor &v) + { + v(Root::ROOT_VM, ObjectSlot(reinterpret_cast(&kind_))); + } + + std::unordered_map& GetCPIndexMap() + { + return cpIndexMap_; + } + + JSTaggedType GetElmKind() const + { + return kind_; + } + + private: + JSTaggedType kind_ {0}; + std::unordered_map cpIndexMap_ {}; + }; + + class ConstantIndexData { + public: + explicit ConstantIndexData(JSTaggedType index) : index_(index) {} + + void Iterate(const RootVisitor &v) + { + v(Root::ROOT_VM, ObjectSlot(reinterpret_cast(&index_))); + } + + std::unordered_map& GetCPIndexMap() + { + return cpIndexMap_; + } + + JSTaggedType GetIndex() const + { + return index_; + } + + private: + JSTaggedType index_ {0}; + std::unordered_map cpIndexMap_ {}; + }; + class IHClassData { public: explicit IHClassData(JSTaggedType ihc) : ihc_(ihc) {} @@ -599,6 +704,62 @@ public: std::unordered_map cpIndexMap_ {}; }; + // for jsarray + class JSArrayData { + public: + explicit JSArrayData() {} + + void Iterate(const RootVisitor &v) + { + // Use reference! + // Otherwise slotaddress will change! + for (auto &iter : idElmMap_) { + iter.second.Iterate(v); + } + for (auto &iter : idElmKindMap_) { + iter.second.Iterate(v); + } + for (auto &iter : offConstIndexMap_) { + iter.second.Iterate(v); + } + } + + std::map& GetElmMap() + { + return idElmMap_; + } + + std::map& GetElmKindMap() + { + return idElmKindMap_; + } + + std::map& GetOffConstIndexMap() + { + return offConstIndexMap_; + } + + void AddElmMap(panda_file::File::EntityId id, ElementData data) + { + idElmMap_.insert({id, data}); + } + + void AddElmKindMap(panda_file::File::EntityId id, ElementKindData data) + { + idElmKindMap_.insert({id, data}); + } + + void AddOffConstIndexMap(uint64_t bcAbsoluteOffset, ConstantIndexData data) + { + offConstIndexMap_.insert({bcAbsoluteOffset, data}); + } + + private: + std::map idElmMap_ {}; + std::map idElmKindMap_ {}; + std::map offConstIndexMap_ {}; + }; + // for snapshot class SnapshotData { public: @@ -620,6 +781,11 @@ public: void Iterate(const RootVisitor &v) { v(Root::ROOT_VM, ObjectSlot(reinterpret_cast(&snapshotCPList_))); + for (auto &iter : snapshotVals_) { + for (size_t i = 0; i < iter.second.size(); i++) { + v(Root::ROOT_VM, ObjectSlot(reinterpret_cast(&iter.second[i]))); + } + } } void SetSnapshotCPList(JSTaggedValue snapshotCPList) @@ -632,9 +798,9 @@ public: return snapshotCPList_; } - CVector& GetSnapshotHCVector(int32_t cpID) + CVector& GetSnapshotValVector(int32_t cpID) { - return snapshotHCs_[cpID]; + return snapshotVals_[cpID]; } void AddIndexInfoToRecordInfo(RecordType type, std::pair indexInfo) @@ -652,8 +818,8 @@ public: private: JSTaggedValue snapshotCPList_ {JSTaggedValue::Hole()}; - // key: constantpoolnum, value: store hclass which produced from static type info - CMap> snapshotHCs_ {}; + // key: constantpoolnum, value: store hclass or element which produced from static type info + CMap> snapshotVals_ {}; // used to record the data that needs to be modified into the aot code entry index std::vector recordInfo_ {}; @@ -705,6 +871,47 @@ public: return collectedGT_; } + inline void InsertLiteralGTMap(TypeLocation &loc, GlobalTSTypeRef gt) + { + literalGTMap_[loc] = gt; + } + + inline GlobalTSTypeRef GetLiteralGT(TypeLocation &loc) + { + auto it = literalGTMap_.find(loc); + if (it != literalGTMap_.end()) { + return it->second; + } + return GlobalTSTypeRef::Default(); + } + + inline void InsertPtToGtMap(ProfileType pgoType, const kungfu::GateType &gateType) + { + ptToGtMap_.emplace(pgoType, gateType); + } + + inline const kungfu::GateType GetGateTypeByPt(ProfileType pgoType) + { + if (pgoType.IsBuiltinsType()) { + return GetBuiltinsGateTypeByPt(pgoType); + } + auto it = ptToGtMap_.find(pgoType); + if (it != ptToGtMap_.end()) { + return it->second; + } + return kungfu::GateType::AnyType(); + } + + inline void AddToSkipTrackFieldSet(ProfileType type) + { + skipTrackFieldSet_.insert(type); + } + + inline bool IsInSkipTrackFieldSet(ProfileType type) + { + return skipTrackFieldSet_.find(type) != skipTrackFieldSet_.end(); + } + void PrintNumOfTypes() const; void PrintTypeInfo(const JSPandaFile *jsPandaFile) const; @@ -713,8 +920,29 @@ public: JSHandle GetExportTableFromLiteral(const JSPandaFile *jsPandaFile, const CString &recordName); + int GetElementsIndex(panda_file::File::EntityId id); + + int GetElementsKindIndex(panda_file::File::EntityId id); + int GetHClassIndex(GlobalTSTypeRef classGT, bool isConstructor = false); + uint32_t GetPGOGTCountByRecordName(const CString &recordName) const + { + if (bcInfoCollector_ == nullptr) { + return 0; + } + kungfu::PGOBCInfo *bcInfo = bcInfoCollector_->GetPGOBCInfo(); + return bcInfo->GetPGOExtendGTCount(recordName); + } + + inline std::string GetBuiltinsName(GlobalTSTypeRef builtinGT) const + { + uint32_t index = GetBuiltinIndex(builtinGT); + return GetBuiltinsName(index); + } + + TSTypeKind PUBLIC_API GetTypeKind(const GlobalTSTypeRef >) const; + #define TSTYPETABLE_ACCESSOR_LIST(V) \ V(Builtin, ModuleTableIdx::BUILTIN) \ V(Inferred, ModuleTableIdx::INFERRED) \ @@ -754,8 +982,6 @@ private: GlobalTSTypeRef PUBLIC_API GetPropType(GlobalTSTypeRef gt, JSHandle propertyName) const; - TSTypeKind PUBLIC_API GetTypeKind(const GlobalTSTypeRef >) const; - std::string GetClassTypeStr(GlobalTSTypeRef gt) const; std::string GetClassInstanceTypeStr(GlobalTSTypeRef gt) const; @@ -766,6 +992,10 @@ private: std::string GetPrimitiveStr(const GlobalTSTypeRef >) const; + uint32_t RecordElmToVecAndIndexMap(ElementData &elmData); + + uint32_t RecordElmKindToVecAndIndexMap(ElementKindData &elmKindData); + uint32_t RecordIhcToVecAndIndexMap(IHClassData &ihcData); uint32_t GetBuiltinIndex(GlobalTSTypeRef builtinGT) const; @@ -774,7 +1004,8 @@ private: void CollectLiteralInfo(JSHandle array, uint32_t constantPoolIndex, JSHandle snapshotConstantPool, - kungfu::BytecodeInfoCollector *bcInfoCollector); + kungfu::BytecodeInfoCollector *bcInfoCollector, + JSHandle ihc, JSHandle chc); inline void SetBuiltinPandaFile(JSPandaFile *jsPandaFile) { @@ -786,26 +1017,36 @@ private: builtinsRecordName_ = builtinsRecordName; } - // for snapshot - int32_t GetOldConstantPoolIDByMethodOffset(const JSPandaFile *jsPandaFile, uint32_t methodOffset); + // for jsarray + void TryGetElmsKind(panda_file::File::EntityId id, JSHandle &ekd); void GenerateSnapshotConstantPoolList(std::map &cpListIndexMap, const CMap &oldCPValues); + void TryGetIhcAndChc(GlobalTSTypeRef gt, JSHandle &ihc, JSHandle &chc); + void FillSnapshotConstantPoolList(const std::map &cpListIndexMap, kungfu::BytecodeInfoCollector *bcInfoCollector); - void AddHClassToSnapshotConstantPoolList(const std::map &cpListIndexMap, + void AddValueToSnapshotConstantPoolList(const std::map &cpListIndexMap, kungfu::BytecodeInfoCollector *bcInfoCollector); JSHandle GetSnapshotConstantPool(uint32_t cpListIndex); + const kungfu::GateType GetBuiltinsGateTypeByPt(ProfileType pgoType); + + BuiltinTypeId GetBuiltinTypeIdByJSType(JSType jsType); + EcmaVM *vm_ {nullptr}; JSThread *thread_ {nullptr}; ObjectFactory *factory_ {nullptr}; JSTaggedValue globalModuleTable_ {JSTaggedValue::Hole()}; + CMap ptToGtMap_ {}; + std::set skipTrackFieldSet_ {}; std::map gtIhcMap_ {}; std::map gtConstructorhcMap_ {}; + std::unordered_map literalGTMap_ {}; + std::map pgoBuiltinGTCache_ {}; bool assertTypes_ {false}; double typeThreshold_ {-1}; @@ -814,10 +1055,13 @@ private: JSTaggedValue curCP_ {JSTaggedValue::Hole()}; int32_t curCPID_ {0}; + // for jsarray + JSArrayData jsArrayData_ {}; + // for snapshot SnapshotData snapshotData_ {}; - std::map, GlobalTSTypeRef> literalOffsetGTMap_ {}; + std::unordered_map idGTMap_ {}; std::map> gtLiteralOffsetMap_ {}; std::vector builtinOffsets_ {}; JSPandaFile *builtinPandaFile_ {nullptr}; diff --git a/ecmascript/ts_types/ts_obj_layout_info.cpp b/ecmascript/ts_types/ts_obj_layout_info.cpp index 5ceab2d55da784598a850d2385e39fdd1d1981d8..581e827b4c061d666888816cee265add0cbd9190 100644 --- a/ecmascript/ts_types/ts_obj_layout_info.cpp +++ b/ecmascript/ts_types/ts_obj_layout_info.cpp @@ -55,6 +55,21 @@ int TSObjLayoutInfo::GetElementIndexByKey(JSTaggedValue key) const return INVALID_INDEX; } +void TSObjLayoutInfo::GetAccessorIndexByKey(JSTaggedValue key, std::vector &vec) +{ + [[maybe_unused]] EcmaString *str = EcmaString::Cast(key.GetTaggedObject()); + ASSERT_PRINT(EcmaStringAccessor(str).IsInternString(), "TS class field key is not an intern string"); + + uint32_t length = GetNumOfProperties(); + for (uint32_t i = 0; i < length; ++i) { + JSTaggedValue keyVal = GetKey(i); + ASSERT_PRINT(keyVal.IsString(), "TS class field key is not a string"); + if (keyVal == key) { + vec.emplace_back(i); + } + } +} + JSTaggedValue TSObjLayoutInfo::TryGetTypeByIndexSign(const uint32_t keyType) { uint32_t length = GetNumOfProperties(); diff --git a/ecmascript/ts_types/ts_obj_layout_info.h b/ecmascript/ts_types/ts_obj_layout_info.h index 1c13bfc59a6388c9e4a0bb974bb143dfc46030b0..262881ac7568b53cac3227752b7f38793dcf87ff 100644 --- a/ecmascript/ts_types/ts_obj_layout_info.h +++ b/ecmascript/ts_types/ts_obj_layout_info.h @@ -30,7 +30,7 @@ namespace panda::ecmascript { class TSObjLayoutInfo : private TaggedArray { public: static constexpr int MIN_PROPERTIES_LENGTH = JSObject::MIN_PROPERTIES_LENGTH; - static constexpr int MAX_PROPERTIES_LENGTH = PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES; + static constexpr int MAX_PROPERTIES_LENGTH = PropertyAttributes::MAX_FAST_PROPS_CAPACITY; static constexpr int ELEMENTS_COUNT_INDEX = 0; static constexpr int ELEMENTS_START_INDEX = 1; static constexpr int ENTRY_SIZE = 3; @@ -103,6 +103,7 @@ public: bool Find(JSTaggedValue key) const; int GetElementIndexByKey(JSTaggedValue key) const; + void GetAccessorIndexByKey(JSTaggedValue key, std::vector &vec); JSTaggedValue TryGetTypeByIndexSign(const uint32_t typeId); diff --git a/ecmascript/ts_types/ts_type.cpp b/ecmascript/ts_types/ts_type.cpp index 21785dcef605d29415de45dad606418888e040c5..d8976d5fb7e7a150b4b8442db03cc0e44a8aded7 100644 --- a/ecmascript/ts_types/ts_type.cpp +++ b/ecmascript/ts_types/ts_type.cpp @@ -113,9 +113,7 @@ GlobalTSTypeRef TSClassType::GetNonStaticPropTypeGT(JSThread *thread, JSHandleGetCurrentEcmaContext()->GetTSManager(); - JSHandle instanceType(thread, classType->GetInstanceType()); - GlobalTSTypeRef propTypeGT = TSObjectType::GetPropTypeGT(thread, instanceType, propName); if (!propTypeGT.IsDefault()) { return propTypeGT; @@ -141,6 +139,38 @@ GlobalTSTypeRef TSClassType::GetNonStaticPropTypeGT(JSThread *thread, JSHandle classType, + JSHandle propName, GlobalTSTypeRef newGT) +{ + DISALLOW_GARBAGE_COLLECTION; + JSHandle instanceType(thread, classType->GetInstanceType()); + TSObjectType::UpdatePropTypeGT(thread, instanceType, propName, newGT); +} + +void TSClassType::UpdateStaticPropTypeGT(JSThread *thread, JSHandle classType, + JSHandle propName, GlobalTSTypeRef newGT) +{ + DISALLOW_GARBAGE_COLLECTION; + TSManager *tsManager = thread->GetCurrentEcmaContext()->GetTSManager(); + JSMutableHandle mutableClassType(thread, classType.GetTaggedValue()); + JSMutableHandle mutableConstructorType(thread, mutableClassType->GetConstructorType()); + bool hasUpdate = false; + while (!hasUpdate) { + hasUpdate = TSObjectType::UpdatePropTypeGT(thread, mutableConstructorType, propName, newGT); + GlobalTSTypeRef classTypeGT = mutableClassType->GetExtensionGT(); + if (classTypeGT.IsDefault()) { + break; + } + + JSTaggedValue tmpType = tsManager->GetTSType(classTypeGT).GetTaggedValue(); + if (tmpType.IsUndefined()) { + break; + } + mutableClassType.Update(tmpType); + mutableConstructorType.Update(mutableClassType->GetConstructorType()); + } +} + GlobalTSTypeRef TSClassInstanceType::GetPropTypeGT(JSThread *thread, JSHandle classInstanceType, JSHandle propName) { @@ -204,6 +234,23 @@ GlobalTSTypeRef TSObjectType::GetPropTypeGT(JSThread *thread, JSHandle objectType, + JSHandle propName, GlobalTSTypeRef newGT) +{ + DISALLOW_GARBAGE_COLLECTION; + JSHandle layout(thread, objectType->GetObjLayoutInfo().GetTaggedObject()); + int propIdx = layout->GetElementIndexByKey(propName.GetTaggedValue()); + if (!TSObjLayoutInfo::IsValidIndex(propIdx)) { + return false; + } + uint32_t gtRawData = static_cast(layout->GetTypeId(propIdx).GetInt()); + if (GlobalTSTypeRef(gtRawData).IsDefault()) { + layout->SetTypeId(thread, propIdx, JSTaggedValue(newGT.GetType())); + return true; + } + return false; +} + GlobalTSTypeRef TSObjectType::GetIndexSignType(JSThread *thread, const JSHandle &objectType, const uint32_t typeId) { diff --git a/ecmascript/ts_types/ts_type.h b/ecmascript/ts_types/ts_type.h index e07aea83402d7cd243eab56cffc0ec840ce75b32..ed63bfb33be085d4add900fcf153d3fc9cf50ccf 100644 --- a/ecmascript/ts_types/ts_type.h +++ b/ecmascript/ts_types/ts_type.h @@ -56,6 +56,9 @@ public: static GlobalTSTypeRef GetPropTypeGT(JSThread *thread, JSHandle objectType, JSHandle propName); + static bool UpdatePropTypeGT(JSThread *thread, JSHandle objectType, + JSHandle propName, GlobalTSTypeRef newGT); + static GlobalTSTypeRef GetIndexSignType(JSThread *thread, const JSHandle &objectType, const uint32_t typeId); @@ -81,6 +84,12 @@ public: static GlobalTSTypeRef GetNonStaticPropTypeGT(JSThread *thread, JSHandle classType, JSHandle propName); + static void UpdateNonStaticPropTypeGT(JSThread *thread, JSHandle classType, + JSHandle propName, GlobalTSTypeRef newGT); + + static void UpdateStaticPropTypeGT(JSThread *thread, JSHandle classType, + JSHandle propName, GlobalTSTypeRef newGT); + ACCESSORS(InstanceType, INSTANCE_TYPE_OFFSET, CONSTRUCTOR_TYPE_OFFSET); ACCESSORS(ConstructorType, CONSTRUCTOR_TYPE_OFFSET, PROTOTYPE_TYPE_OFFSET); ACCESSORS(PrototypeType, PROTOTYPE_TYPE_OFFSET, NAME_OFFSET); @@ -197,6 +206,7 @@ public: NEXT_BIT_FIELD(BitField, IsFastCall, bool, ONE_BIT, IsSignature); NEXT_BIT_FIELD(BitField, IsFastCallVaild, bool, ONE_BIT, IsFastCall); NEXT_BIT_FIELD(BitField, IsMethodOffsetVaild, bool, ONE_BIT, IsFastCallVaild); + NEXT_BIT_FIELD(BitField, IsNoGC, bool, ONE_BIT, IsMethodOffsetVaild); DECL_VISIT_OBJECT(NAME_OFFSET, RETURN_GT_OFFSET) DECL_DUMP() diff --git a/ecmascript/ts_types/ts_type_accessor.cpp b/ecmascript/ts_types/ts_type_accessor.cpp index 3008b986a8748bfaf6b38654b3e0e64908f68863..a090e30cf77761d3caf6d9d7d94eef59c4c81480 100644 --- a/ecmascript/ts_types/ts_type_accessor.cpp +++ b/ecmascript/ts_types/ts_type_accessor.cpp @@ -28,6 +28,54 @@ void TSTypeAccessor::MarkPropertyInitialized(JSTaggedValue key) } } +void TSTypeAccessor::UpdateNonStaticProp(JSTaggedValue key, GlobalTSTypeRef newGT) +{ + ASSERT(tsManager_->IsClassTypeKind(gt_)); + JSHandle classType = GetClassType(); + JSHandle propName(thread_, key); + TSClassType::UpdateNonStaticPropTypeGT(thread_, classType, propName, newGT); +} + +void TSTypeAccessor::UpdateStaticProp(JSTaggedValue key, GlobalTSTypeRef newGT) +{ + ASSERT(tsManager_->IsClassTypeKind(gt_)); + JSHandle classType = GetClassType(); + JSHandle propName(thread_, key); + TSClassType::UpdateStaticPropTypeGT(thread_, classType, propName, newGT); +} + +void TSTypeAccessor::UpdateForEachCBPara(kungfu::GateType targetType) +{ + if (!tsManager_->IsFunctionTypeKind(gt_)) { + return; + } + JSHandle callbackType = GetFunctionType(); + uint32_t paraSz = callbackType->GetLength(); + JSHandle parameterTypes(thread_, callbackType->GetParameterTypes()); + const uint32_t maxParaSz = 3; // elementValue, index and array + if (paraSz > maxParaSz) { + return; + } + GlobalTSTypeRef indeGT = kungfu::GateType::IntType().GetGTRef(); + GlobalTSTypeRef elementGT = GlobalTSTypeRef::Default(); + GlobalTSTypeRef targetGT = targetType.GetGTRef(); + if (tsManager_->IsArrayTypeKind(targetType)) { + elementGT = tsManager_->GetArrayParameterTypeGT(targetType); + } + if (tsManager_->IsTypedArrayType(targetType)) { + elementGT = kungfu::GateType::NumberType().GetGTRef(); + } + if (elementGT.IsDefault()) { + return; + } + std::vector tempGTs{elementGT, indeGT, targetGT}; + for (uint32_t i = 0; i < paraSz; i++) { + if (!parameterTypes->Get(i).GetInt()) { + parameterTypes->Set(thread_, i, JSTaggedValue(tempGTs[i].GetType())); + } + } +} + JSTaggedValue TSTypeAccessor::MarkInitialized(JSTaggedValue attribute) { ASSERT(attribute.IsInt()); @@ -75,6 +123,14 @@ std::string TSTypeAccessor::GetClassTypeName() const return tsManager_->GetClassTypeStr(gt_); } +std::string TSTypeAccessor::GetFunctionName() const +{ + JSHandle funcType = GetFunctionType(); + EcmaStringAccessor acc(funcType->GetName()); + std::string nameStr = acc.ToStdString(); + return nameStr; +} + JSHandle TSTypeAccessor::GetInstanceTypeLayout() const { ASSERT(tsManager_->IsClassTypeKind(gt_)); @@ -83,4 +139,35 @@ JSHandle TSTypeAccessor::GetInstanceTypeLayout() const JSHandle layout(thread_, instanceType->GetObjLayoutInfo()); return layout; } + +JSHandle TSTypeAccessor::GetPrototypeTypeLayout() const +{ + ASSERT(tsManager_->IsClassTypeKind(gt_)); + JSHandle classType = GetClassType(); + JSHandle prototypeType(thread_, classType->GetPrototypeType()); + JSHandle layout(thread_, prototypeType->GetObjLayoutInfo()); + return layout; +} + +GlobalTSTypeRef TSTypeAccessor::GetAccessorGT(JSTaggedValue key, bool isSetter) const +{ + ASSERT(tsManager_->IsClassTypeKind(gt_)); + JSHandle layout = GetPrototypeTypeLayout(); + std::vector vec; + layout->GetAccessorIndexByKey(key, vec); + if (vec.empty()) { + return GlobalTSTypeRef(); + } + + for (size_t i = 0; i < vec.size(); i++) { + int index = vec[i]; + ASSERT(TSObjLayoutInfo::IsValidIndex(index)); + auto gt = GlobalTSTypeRef(layout->GetTypeId(index).GetInt()); + uint32_t parameterLength = tsManager_->GetFunctionTypeLength(gt); + if (IsSetterGT(parameterLength, isSetter) || IsGetterGT(parameterLength, isSetter)) { + return gt; + } + } + return GlobalTSTypeRef(); +} } // namespace panda::ecmascript diff --git a/ecmascript/ts_types/ts_type_accessor.h b/ecmascript/ts_types/ts_type_accessor.h index dc3711d7e4cb8218c68b0f325f5b344c88c28112..98a3dec872573a6b56ebedb3dd76cfdf0cd179e5 100644 --- a/ecmascript/ts_types/ts_type_accessor.h +++ b/ecmascript/ts_types/ts_type_accessor.h @@ -76,12 +76,22 @@ public: void PUBLIC_API MarkPropertyInitialized(JSTaggedValue key); + void PUBLIC_API UpdateNonStaticProp(JSTaggedValue key, GlobalTSTypeRef newGT); + + void PUBLIC_API UpdateStaticProp(JSTaggedValue key, GlobalTSTypeRef newGT); + + void PUBLIC_API UpdateForEachCBPara(kungfu::GateType targetType); + bool PUBLIC_API IsPropertyInitialized(JSTaggedValue key) const; std::string PUBLIC_API GetInitializedProperties() const; std::string PUBLIC_API GetClassTypeName() const; + std::string PUBLIC_API GetFunctionName() const; + + GlobalTSTypeRef PUBLIC_API GetAccessorGT(JSTaggedValue key, bool isSetter) const; + #define CLASS_TYPE_BITFIELD_ACCESSOR(NAME) \ inline void PUBLIC_API MarkClass##NAME() \ { \ @@ -115,6 +125,17 @@ private: JSTaggedValue MarkInitialized(JSTaggedValue flag); JSHandle GetInstanceTypeLayout() const; + JSHandle GetPrototypeTypeLayout() const; + + bool IsGetterGT(size_t parameterLength, bool isSetter) const + { + return parameterLength == 0 && !isSetter; + } + + bool IsSetterGT(size_t parameterLength, bool isSetter) const + { + return parameterLength == 1 && isSetter; + } TSManager *tsManager_ {nullptr}; JSThread *thread_ {nullptr}; diff --git a/ecmascript/ts_types/ts_type_parser.cpp b/ecmascript/ts_types/ts_type_parser.cpp index 453ca757b98499206508770e803aa9f7e39c9a59..7f82083b9c7fd5ff0f3fcca10c065166d4fcd83e 100644 --- a/ecmascript/ts_types/ts_type_parser.cpp +++ b/ecmascript/ts_types/ts_type_parser.cpp @@ -15,9 +15,12 @@ #include "ecmascript/ts_types/ts_type_parser.h" -#include "ecmascript/base/path_helper.h" +#include "ecmascript/pgo_profiler/pgo_profiler_layout.h" +#include "ecmascript/subtyping_operator.h" #include "ecmascript/jspandafile/js_pandafile_manager.h" #include "ecmascript/module/js_module_manager.h" +#include "ecmascript/module/module_path_helper.h" +#include "ecmascript/jspandafile/program_object.h" namespace panda::ecmascript { // For each property of one class, object or interface, it's name and typeIndex are recorded in order, @@ -26,6 +29,8 @@ static constexpr uint32_t FIELD_LENGTH = 4; static constexpr uint32_t METHOD_LENGTH = 2; static constexpr uint32_t INDEX_OCCUPIED_OFFSET = 1; +using PGOHandler = pgo::PGOHandler; + TSTypeParser::TSTypeParser(TSManager *tsManager) : tsManager_(tsManager), vm_(tsManager->GetEcmaVM()), thread_(vm_->GetJSThread()), factory_(vm_->GetFactory()), @@ -52,8 +57,9 @@ GlobalTSTypeRef TSTypeParser::CreateGT(const JSPandaFile *jsPandaFile, const CSt return EncodeParaType(typeId); } - if (tsManager_->HasCreatedGT(jsPandaFile, typeId)) { - return tsManager_->GetGTFromOffset(jsPandaFile, typeId); + GlobalTypeID gId(jsPandaFile, typeId); + if (tsManager_->HasCreatedGT(gId)) { + return tsManager_->GetGTByGlobalTypeID(gId); } return ParseType(jsPandaFile, recordName, typeId); } @@ -83,7 +89,7 @@ GlobalTSTypeRef TSTypeParser::ParseType(const JSPandaFile *jsPandaFile, const CS } uint32_t moduleId = tableGenerator_.TryGetModuleId(recordName); - if (UNLIKELY(!GlobalTSTypeRef::IsVaildModuleId(moduleId))) { + if (UNLIKELY(!GlobalTSTypeRef::IsValidModuleId(moduleId))) { LOG_COMPILER(DEBUG) << "The maximum number of TSTypeTables is reached. All TSTypes in the record " << recordName << " will not be parsed and will be treated as any."; return GetAndStoreGT(jsPandaFile, typeId, recordName); @@ -91,7 +97,7 @@ GlobalTSTypeRef TSTypeParser::ParseType(const JSPandaFile *jsPandaFile, const CS JSHandle table = tableGenerator_.GetOrGenerateTSTypeTable(jsPandaFile, recordName, moduleId); uint32_t localId = tableGenerator_.TryGetLocalId(table); - if (UNLIKELY(!GlobalTSTypeRef::IsVaildLocalId(localId))) { + if (UNLIKELY(!GlobalTSTypeRef::IsValidLocalId(localId))) { LOG_COMPILER(DEBUG) << "The maximum number of TSTypes in TSTypeTable " << moduleId << " is reached. " << "The TSType with typeId " << typeId << " in the record " << recordName << " will not be parsed and will be treated as any."; @@ -142,13 +148,13 @@ GlobalTSTypeRef TSTypeParser::ResolveImportType(const JSPandaFile *jsPandaFile, JSHandle relativePath = GenerateImportRelativePath(importVarNamePath); CString cstringRelativePath = ConvertToString(*relativePath); // skip @ohos:|@app:|@native: prefixed imports - if (base::PathHelper::IsNativeModuleRequest(cstringRelativePath)) { + if (ModulePathHelper::IsNativeModuleRequest(cstringRelativePath)) { return GetAndStoreGT(jsPandaFile, typeId, recordName); } CString baseFileName = jsPandaFile->GetJSPandaFileDesc(); CString entryPoint = - base::PathHelper::ConcatFileNameWithMerge(thread_, jsPandaFile, baseFileName, recordName, cstringRelativePath); + ModulePathHelper::ConcatFileNameWithMerge(thread_, jsPandaFile, baseFileName, recordName, cstringRelativePath); if (entryPoint.empty()) { LOG_COMPILER(DEBUG) << "EntryPoint is empty. Please check whether concating file name is correct or " "whether the module request recorded in the import-type literal is correct."; @@ -159,7 +165,7 @@ GlobalTSTypeRef TSTypeParser::ResolveImportType(const JSPandaFile *jsPandaFile, } uint32_t moduleId = tableGenerator_.TryGetModuleId(entryPoint); - if (UNLIKELY(!GlobalTSTypeRef::IsVaildModuleId(moduleId))) { + if (UNLIKELY(!GlobalTSTypeRef::IsValidModuleId(moduleId))) { LOG_COMPILER(DEBUG) << "The maximum number of TSTypeTables is reached. All TSTypes in the record " << entryPoint << " will not be parsed and will be treated as any."; return GetAndStoreGT(jsPandaFile, typeId, recordName); @@ -217,9 +223,7 @@ JSHandle TSTypeParser::ParseNonImportType(const JSPandaFile *jsPa return JSHandle(classType); } case TSTypeKind::CLASS_INSTANCE: { - JSHandle classInstanceType = - ParseClassInstanceType(jsPandaFile, recordName, typeLiteralExtractor); - return JSHandle(classInstanceType); + return ParseClassInstanceType(jsPandaFile, recordName, typeLiteralExtractor); } case TSTypeKind::INTERFACE: { JSHandle interfaceType = ParseInterfaceType(jsPandaFile, recordName, typeLiteralExtractor); @@ -242,6 +246,7 @@ JSHandle TSTypeParser::ParseNonImportType(const JSPandaFile *jsPa JSHandle objectType = ParseObjectType(jsPandaFile, recordName, typeLiteralExtractor); return JSHandle(objectType); } + case TSTypeKind::BUILTIN_INSTANCE: case TSTypeKind::GENERIC_INSTANCE: { return ParseGenericsInstanceType(jsPandaFile, recordName, typeLiteralExtractor); } @@ -293,17 +298,23 @@ JSHandle TSTypeParser::ParseClassType(const JSPandaFile *jsPandaFil return classType; } -JSHandle TSTypeParser::ParseClassInstanceType(const JSPandaFile *jsPandaFile, - const CString &recordName, - TypeLiteralExtractor *typeLiteralExtractor) +JSHandle TSTypeParser::ParseClassInstanceType(const JSPandaFile *jsPandaFile, + const CString &recordName, + TypeLiteralExtractor *typeLiteralExtractor) { ASSERT(typeLiteralExtractor->GetTypeKind() == TSTypeKind::CLASS_INSTANCE); - JSHandle classInstanceType = factory_->NewTSClassInstanceType(); // classTypeId is stored in the first position uint32_t classTypeId = typeLiteralExtractor->GetIntValue(DEFAULT_INDEX); + if (classTypeId == static_cast(BuiltinTypeId::ARRAY)) { + return JSHandle(factory_->NewTSArrayType()); + } auto classGT = CreateGT(jsPandaFile, recordName, classTypeId); + if (tsManager_->IsArrayTypeKind(classGT)) { + return tsManager_->GetTSType(classGT); + } + JSHandle classInstanceType = factory_->NewTSClassInstanceType(); classInstanceType->SetClassGT(classGT); - return classInstanceType; + return JSHandle(classInstanceType); } JSHandle TSTypeParser::ParseInterfaceType(const JSPandaFile *jsPandaFile, const CString &recordName, @@ -512,9 +523,10 @@ void TSTypeParser::StoreMethodOffset(const JSHandle &functionTyp functionType->SetMethodOffset(methodOffset); functionType->SetIsMethodOffsetVaild(true); bool isVaild; - bool canFastCall = bcInfo_->IterateMethodOffsetToCanFastCall(methodOffset, &isVaild); + kungfu::FastCallInfo info = bcInfo_->IterateMethodOffsetToFastCallInfo(methodOffset, &isVaild); functionType->SetIsFastCallVaild(isVaild); - functionType->SetIsFastCall(canFastCall); + functionType->SetIsFastCall(info.canFastCall_); + functionType->SetIsNoGC(info.isNoGC_); } else { functionType->SetIsMethodOffsetVaild(false); functionType->SetIsFastCallVaild(false); @@ -598,7 +610,7 @@ GlobalTSTypeRef TSTypeParser::IterateStarExport(JSHandle target, con continue; } uint32_t starModuleId = tableGenerator_.TryGetModuleId(star); - if (UNLIKELY(!GlobalTSTypeRef::IsVaildModuleId(starModuleId))) { + if (UNLIKELY(!GlobalTSTypeRef::IsValidModuleId(starModuleId))) { continue; } JSHandle table = tableGenerator_.GetOrGenerateTSTypeTable(jsPandaFile, star, starModuleId); @@ -629,8 +641,14 @@ JSHandle TSTypeParser::ParseGenericsInstanceType(const JSPandaFil const CString &recordName, TypeLiteralExtractor *typeLiteralExtractor) { - ASSERT(typeLiteralExtractor->GetTypeKind() == TSTypeKind::GENERIC_INSTANCE); - GlobalTSTypeRef genericsGT = CreateGT(jsPandaFile, recordName, typeLiteralExtractor->GetIntValue(DEFAULT_INDEX)); + ASSERT(typeLiteralExtractor->GetTypeKind() == TSTypeKind::BUILTIN_INSTANCE || + typeLiteralExtractor->GetTypeKind() == TSTypeKind::GENERIC_INSTANCE); + GlobalTSTypeRef genericsGT; + if (IsGenericsArrayType(typeLiteralExtractor)) { + genericsGT = tsManager_->CreateArrayType(); + } else { + genericsGT = CreateGT(jsPandaFile, recordName, typeLiteralExtractor->GetIntValue(DEFAULT_INDEX)); + } JSHandle genericsType = tsManager_->GetTSType(genericsGT); std::vector paras {}; typeLiteralExtractor->EnumerateElements(NUM_GENERICS_PARA_INDEX, @@ -657,6 +675,11 @@ JSHandle TSTypeParser::InstantiateGenericsType(const JSHandleIsTSObjectType()) { JSHandle objectType = InstantiateObjGenericsType(JSHandle(genericsType), paras); return JSHandle(objectType); + } else if (genericsType->IsTSArrayType()) { + ASSERT(paras.size() == 1); + JSHandle arrayType(genericsType); + arrayType->SetElementGT(paras[0]); + return JSHandle(arrayType); } LOG_COMPILER(DEBUG) << "Unsupport GenericsType Instantiate: " << static_cast(genericsType->GetTaggedObject()->GetClass()->GetObjectType()); @@ -691,7 +714,7 @@ JSHandle TSTypeParser::InstantiateClassGenericsType(const JSHandle< const std::vector ¶s) { JSHandle classType = factory_->NewTSClassType(); - classType->SetName(thread_, genericsType->GetName()); + CopyClassName(genericsType, classType); classType->SetExtensionGT(genericsType->GetExtensionGT()); classType->SetHasLinked(genericsType->GetHasLinked()); @@ -709,6 +732,18 @@ JSHandle TSTypeParser::InstantiateClassGenericsType(const JSHandle< return classType; } +void TSTypeParser::CopyClassName(const JSHandle &genericsType, const JSHandle &classType) +{ + auto gt = genericsType->GetGT(); + if (gt.IsBuiltinModule()) { + const std::string name = tsManager_->GetBuiltinsName(gt); + JSHandle ecmaStr = factory_->NewFromStdString(name); + classType->SetName(thread_, ecmaStr); + } else { + classType->SetName(thread_, genericsType->GetName()); + } +} + JSHandle TSTypeParser::InstantiateInterfaceGenericsType(const JSHandle &genericsType, const std::vector ¶s) { @@ -767,6 +802,128 @@ GlobalTSTypeRef TSTypeParser::TryReplaceTypePara(GlobalTSTypeRef gt, const std:: return gt; } +GlobalTSTypeRef TSTypeParser::CreatePGOGT(PGOInfo info) +{ + GlobalTypeID gId(info.jsPandaFile, info.pgoType); + if (tsManager_->HasCreatedGT(gId)) { + return tsManager_->GetGTByGlobalTypeID(gId); + } + return ParsePGOType(info); +} + +GlobalTSTypeRef TSTypeParser::ParsePGOType(PGOInfo &info) +{ + uint32_t moduleId = tableGenerator_.TryGetModuleId(info.recordName); + if (UNLIKELY(!GlobalTSTypeRef::IsValidModuleId(moduleId))) { + LOG_COMPILER(DEBUG) << "The maximum number of TSTypeTables is reached. All TSTypes in the record " + << info.recordName << " will not be parsed and will be treated as any."; + return GetAndStoreGT(info.jsPandaFile, info.pgoType); + } + + JSHandle table = tableGenerator_.GetOrGenerateTSTypeTable(info.jsPandaFile, info.recordName, + moduleId); + uint32_t localId = tableGenerator_.TryGetLocalId(table); + if (UNLIKELY(!GlobalTSTypeRef::IsValidLocalId(localId))) { + LOG_COMPILER(DEBUG) << "The maximum number of TSTypes in TSTypeTable " << moduleId << " is reached. " + << "The objLiteral with constantpool index " << info.cpIdx << " in the record " + << info.recordName << " will not be parsed and will be treated as any."; + return GetAndStoreGT(info.jsPandaFile, info.pgoType); + } + + table->SetNumberOfTypes(thread_, localId); + GlobalTSTypeRef gt = GetAndStoreGT(info.jsPandaFile, info.pgoType, moduleId, localId); + JSHandle parseType = ParseNonImportPGOType(gt, info); + if (UNLIKELY(parseType->IsUndefined())) { + return GetAndStoreGT(info.jsPandaFile, info.pgoType); + } + SetTSType(table, parseType, gt); + return gt; +} + +JSHandle TSTypeParser::ParseNonImportPGOType(GlobalTSTypeRef gt, PGOInfo &info) +{ + switch (info.type) { + case kungfu::PGOBCInfo::Type::OBJ_LITERAL: { + return ParseObjectPGOType(gt, info); + } + default: + LOG_COMPILER(DEBUG) << "Do not support parse extend types with kind " << static_cast(info.type); + return thread_->GlobalConstants()->GetHandledUndefined(); + } +} + +JSHandle TSTypeParser::ParseObjectPGOType(GlobalTSTypeRef gt, PGOInfo &info) +{ + JSHandle constpoolHandle(tsManager_->GetConstantPool()); + JSTaggedValue obj = ConstantPool::GetLiteralFromCache( + thread_, constpoolHandle.GetTaggedValue(), info.cpIdx, info.recordName); + JSHandle objHandle(thread_, obj); + + if (info.enableOptTrackField) { + ASSERT(info.pgoType.IsProfileType()); + PGOHClassLayoutDesc *desc; + if (info.decoder->GetHClassLayoutDesc(info.pgoType, &desc)) { + if (!VerifyObjIhcPGOType(objHandle, *desc)) { + LOG_COMPILER(DEBUG) << "Verify ihc type failed"; + return thread_->GlobalConstants()->GetHandledUndefined(); + } + } + } + + JSHandle oldHClass(thread_, objHandle->GetClass()); + if (oldHClass->IsDictionaryMode()) { + return thread_->GlobalConstants()->GetHandledUndefined(); + } + JSHandle hclass = JSHClass::Clone(thread_, oldHClass); + ObjectFactory *factory = vm_->GetFactory(); + JSHandle newLayout = factory->CopyLayoutInfo(JSHandle(thread_, hclass->GetLayout())); + hclass->SetLayout(thread_, newLayout); + + hclass->SetTS(true); + JSHandle objectType = factory_->NewTSObjectType(0); + tsManager_->AddInstanceTSHClass(gt, hclass); + return JSHandle(objectType); +} + +bool TSTypeParser::VerifyObjIhcPGOType(JSHandle obj, const PGOHClassLayoutDesc &desc) +{ + auto hclass = obj->GetClass(); + LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject()); + uint32_t numOfProps = hclass->NumberOfProps(); + for (uint32_t i = 0; i < numOfProps; i++) { + auto key = layoutInfo->GetKey(i); + if (!key.IsString()) { + continue; + } + + auto attr = layoutInfo->GetAttr(i); + if (!attr.IsInlinedProps()) { + continue; + } + JSTaggedValue value = obj->GetPropertyInlinedProps(i); + + auto keyString = EcmaStringAccessor(key).ToCString(); + PGOHandler newHandler; + if (!desc.FindDescWithKey(keyString, newHandler)) { + continue; + } + PropertyAttributes newAttr; + if (!newHandler.SetAttribute(newAttr)) { + continue; + } + if (newAttr.IsDoubleRep()) { + if (!value.IsNumber()) { + return false; + } + } else if (newAttr.IsIntRep()) { + if (!value.IsInt()) { + return false; + } + } + } + return true; +} + static uint32_t CalculateNextNumIndex(const TypeLiteralExtractor *typeLiteralExtractor, uint32_t startIndex = 0, uint32_t gap = 1) diff --git a/ecmascript/ts_types/ts_type_parser.h b/ecmascript/ts_types/ts_type_parser.h index 633ce4a7963c84d3ca1a449695aa863c26b49bd9..aad1fdba565f2019e3f3dd4816d72eae69fbb82f 100644 --- a/ecmascript/ts_types/ts_type_parser.h +++ b/ecmascript/ts_types/ts_type_parser.h @@ -17,9 +17,12 @@ #define ECMASCRIPT_TS_TYPES_TS_TYPE_PARSER_H #include "ecmascript/jspandafile/type_literal_extractor.h" +#include "ecmascript/pgo_profiler/pgo_profiler_decoder.h" #include "ecmascript/ts_types/ts_type_table_generator.h" namespace panda::ecmascript { +using PGOHClassLayoutDesc = pgo::PGOHClassLayoutDesc; +using PGOProfilerDecoder = pgo::PGOProfilerDecoder; /* TSTypeParser parses types recorded in abc files into TSTypes. VM uses TSTypeTables to * store TSTypes. Each TSTypeTable is used to store all types from the same record. * Since VM can only record types in GlobalTSTypeRef::MAX_MODULE_ID records and @@ -32,16 +35,35 @@ namespace panda::ecmascript { */ class TSTypeParser { public: + struct PGOInfo { + const JSPandaFile *jsPandaFile; + const CString &recordName; + uint32_t methodOffset; + uint32_t cpIdx; + PGOSampleType pgoType; + kungfu::PGOBCInfo::Type type; + PGOProfilerDecoder *decoder; + bool enableOptTrackField; + }; + explicit TSTypeParser(TSManager *tsManager); ~TSTypeParser() = default; GlobalTSTypeRef PUBLIC_API CreateGT(const JSPandaFile *jsPandaFile, const CString &recordName, uint32_t typeId); + GlobalTSTypeRef PUBLIC_API CreatePGOGT(PGOInfo info); + inline static bool IsUserDefinedType(const uint32_t typeId) { return typeId > USER_DEFINED_TYPE_OFFSET; } + inline const std::unordered_map &GetMethodList() const + { + ASSERT(bcInfo_ != nullptr); + return bcInfo_->GetMethodList(); + } + private: static constexpr size_t BUILDIN_TYPE_OFFSET = 20; static constexpr size_t USER_DEFINED_TYPE_OFFSET = 100; @@ -54,14 +76,25 @@ private: uint32_t moduleId = 0, uint32_t localId = 0) { GlobalTSTypeRef gt(moduleId, localId); - tsManager_->AddElementToLiteralOffsetGTMap(jsPandaFile, typeId, recordName, gt); + GlobalTypeID gId(jsPandaFile, typeId); + tsManager_->AddElementToIdGTMap(gId, gt, recordName); + return gt; + } + + inline GlobalTSTypeRef GetAndStoreGT(const JSPandaFile *jsPandaFile, PGOSampleType pgoTypeId, + uint32_t moduleId = 0, uint32_t localId = 0) + { + GlobalTSTypeRef gt(moduleId, localId); + GlobalTypeID gId(jsPandaFile, pgoTypeId); + tsManager_->AddElementToIdGTMap(gId, gt); return gt; } inline GlobalTSTypeRef GetAndStoreImportGT(const JSPandaFile *jsPandaFile, uint32_t typeId, const CString &recordName, GlobalTSTypeRef gt) { - tsManager_->AddElementToLiteralOffsetGTMap(jsPandaFile, typeId, recordName, gt, true); + GlobalTypeID gId(jsPandaFile, typeId); + tsManager_->AddElementToIdGTMap(gId, gt, recordName, true); return gt; } @@ -104,6 +137,12 @@ private: return gt.IsPrimitiveModule() && (gt.GetLocalId() >= GENERICS_PARA_OFFSET); } + inline bool IsGenericsArrayType(TypeLiteralExtractor *typeLiteralExtractor) const + { + return typeLiteralExtractor->GetTypeKind() == TSTypeKind::BUILTIN_INSTANCE && + typeLiteralExtractor->GetIntValue(DEFAULT_INDEX) == static_cast(BuiltinTypeId::ARRAY); + } + GlobalTSTypeRef ParseType(const JSPandaFile *jsPandaFile, const CString &recordName, uint32_t typeId); GlobalTSTypeRef ParseBuiltinObjType(uint32_t typeId); @@ -123,8 +162,8 @@ private: JSHandle ParseClassType(const JSPandaFile *jsPandaFile, const CString &recordName, TypeLiteralExtractor *typeLiteralExtractor); - JSHandle ParseClassInstanceType(const JSPandaFile *jsPandaFile, const CString &recordName, - TypeLiteralExtractor *typeLiteralExtractor); + JSHandle ParseClassInstanceType(const JSPandaFile *jsPandaFile, const CString &recordName, + TypeLiteralExtractor *typeLiteralExtractor); JSHandle ParseInterfaceType(const JSPandaFile *jsPandaFile, const CString &recordName, TypeLiteralExtractor *typeLiteralExtractor); @@ -141,6 +180,14 @@ private: JSHandle ParseObjectType(const JSPandaFile *jsPandaFile, const CString &recordName, TypeLiteralExtractor *typeLiteralExtractor); + GlobalTSTypeRef ParsePGOType(PGOInfo &info); + + JSHandle ParseNonImportPGOType(GlobalTSTypeRef gt, PGOInfo &info); + + JSHandle ParseObjectPGOType(GlobalTSTypeRef gt, PGOInfo &info); + + bool VerifyObjIhcPGOType(JSHandle obj, const PGOHClassLayoutDesc &desc); + void FillPropTypes(const JSPandaFile *jsPandaFile, const CString &recordName, const JSHandle &objectType, @@ -199,6 +246,8 @@ private: JSHandle InstantiateClassGenericsType(const JSHandle &genericsType, const std::vector ¶s); + void CopyClassName(const JSHandle &genericsType, const JSHandle &classType); + JSHandle InstantiateInterfaceGenericsType(const JSHandle &genericsType, const std::vector ¶s); diff --git a/ecmascript/ts_types/ts_type_table.cpp b/ecmascript/ts_types/ts_type_table.cpp index 571327b94741c0241e8eecac37e9ba72fa412f45..3ec80444a749687f4b46104011034dd2025c0f51 100644 --- a/ecmascript/ts_types/ts_type_table.cpp +++ b/ecmascript/ts_types/ts_type_table.cpp @@ -18,7 +18,7 @@ namespace panda::ecmascript { JSHandle TSTypeTable::GetExportValueTable(JSThread *thread, JSHandle typeTable) { - int index = static_cast(typeTable->GetLength()) - 1; + uint32_t index = typeTable->GetLength() - 1; JSHandle exportValueTable(thread, typeTable->Get(index)); return exportValueTable; } diff --git a/ecmascript/ts_types/ts_type_table_generator.cpp b/ecmascript/ts_types/ts_type_table_generator.cpp index b445cb6835ad8b1d10208f6f37c813d9ebce83b5..dd6a078425f8dafd2e22c3076cdabb3506c212de 100644 --- a/ecmascript/ts_types/ts_type_table_generator.cpp +++ b/ecmascript/ts_types/ts_type_table_generator.cpp @@ -15,6 +15,7 @@ #include "ecmascript/ts_types/ts_type_table_generator.h" +#include "ecmascript/global_env_constants-inl.h" #include "ecmascript/jspandafile/js_pandafile_manager.h" #include "ecmascript/jspandafile/type_literal_extractor.h" @@ -58,8 +59,14 @@ JSHandle TSTypeTableGenerator::GetOrGenerateTSTypeTable(const JSPan return tsManager_->GetTSTypeTable(moduleId); } JSHandle recordNameStr = factory_->NewFromUtf8(recordName); - TypeSummaryExtractor summExtractor(jsPandaFile, recordName); - JSHandle table = AddTypeTable(recordNameStr, summExtractor.GetNumOfTypes()); + // when PGO is enabled, no matter whether the abc file is a '.js' or a '.ts' file, it may contain PGO GT + uint32_t typeNum = tsManager_->GetPGOGTCountByRecordName(recordName); + if (jsPandaFile->HasTSTypes(recordName)) { + // only '.ts' file has type literal and can get number of type from it + TypeSummaryExtractor summExtractor(jsPandaFile, recordName); + typeNum += summExtractor.GetNumOfTypes(); + } + JSHandle table = AddTypeTable(recordNameStr, typeNum); return table; } diff --git a/ecmascript/vtable.cpp b/ecmascript/vtable.cpp index 0bae73df6fb572c206f2da0d9bbe21b8ff847cae..0083b3cfca2be24d9bf68e77d7fab23110ddc9f2 100644 --- a/ecmascript/vtable.cpp +++ b/ecmascript/vtable.cpp @@ -14,6 +14,8 @@ */ #include "ecmascript/vtable.h" +#include "ecmascript/js_hclass-inl.h" +#include "ecmascript/layout_info-inl.h" namespace panda::ecmascript { VTable::Tuple VTable::CreateTuple(const JSThread *thread, JSTaggedValue phc, diff --git a/ecmascript/waiter_list.h b/ecmascript/waiter_list.h index 9bd28c419d34a58dd1a281ff7b1ea1e87f56e355..ad91762fd4b3307aca1c9621feda3270f860d7e3 100644 --- a/ecmascript/waiter_list.h +++ b/ecmascript/waiter_list.h @@ -19,12 +19,9 @@ #include "ecmascript/ecma_macros.h" #include "ecmascript/mem/c_containers.h" -#include "libpandabase/os/mutex.h" +#include "ecmascript/platform/mutex.h" namespace panda::ecmascript { -using Mutex = os::memory::Mutex; -using LockHolder = os::memory::LockHolder; - class WaiterListNode { public: WaiterListNode() = default; @@ -36,7 +33,7 @@ public: WaiterListNode *prev_ {nullptr}; WaiterListNode *next_ {nullptr}; // Used to call wait or Signal() to unlock wait and wake up - os::memory::ConditionVariable cond_; + ConditionVariable cond_; // Managed Arraybuffer or SharedArrayBuffer memory data void *date_ {nullptr}; @@ -88,20 +85,19 @@ private: Singleton() = default; }; -class SCOPED_CAPABILITY MutexGuard -{ +class MutexGuard { public: explicit MutexGuard(Mutex *mutex) : mutex_(mutex), lockHolder_(*mutex) {} - void Unlock() RELEASE() + void Unlock() { mutex_->Unlock(); } - void Lock() ACQUIRE() + void Lock() { mutex_->Lock(); } - +private: Mutex *mutex_; LockHolder lockHolder_; }; diff --git a/js_runtime_config.gni b/js_runtime_config.gni index e403fd34fff55d0bd148bd5ba467a7199c4f1eb2..a8a6cbb448b5c468434b110ed2fa1ddb111a607c 100644 --- a/js_runtime_config.gni +++ b/js_runtime_config.gni @@ -36,6 +36,7 @@ ark_root = "//arkcompiler/runtime_core" js_root = "//arkcompiler/ets_runtime" global_root = "//base/global/i18n" hilog_root = "//base/hiviewdfx/hilog/interfaces/native/innerkits" +qos_root = "//foundation/resourceschedule/frame_aware_sched" compile_llvm_online = false run_with_asan = false enable_leak_check = false @@ -113,3 +114,5 @@ enable_local_code_sign = false if (have_local_code_sign && enable_target_compilation) { enable_local_code_sign = true } + +ets_runtime_output_path = "ets_runtime/ets_runtime" diff --git a/libark_jsruntime.map b/libark_jsruntime.map index a94f739e026be097f8a87a9709212724c3ae2355..7693e5fa0f8cf15d3abd612934ee495b569e6ea3 100644 --- a/libark_jsruntime.map +++ b/libark_jsruntime.map @@ -41,6 +41,7 @@ panda::RegExpRef::*; panda::SetIteratorRef::*; panda::SetRef::*; + panda::ProxyRef::*; panda::StringRef::*; panda::SymbolRef::*; panda::TypedArrayRef::*; @@ -48,7 +49,10 @@ panda::Uint32ArrayRef::*; panda::Uint8ArrayRef::*; panda::Uint8ClampedArrayRef::*; + panda::WeakMapRef::*; + panda::WeakSetRef::*; + panda::TryCatch::*; panda::JSNApi::*; panda::DFXJSNApi::*; panda::ecmascript::tooling::DebuggerApi::*; @@ -77,6 +81,13 @@ panda::os::unix::memory::*; panda::ecmascript::JSHClass::*; panda::ecmascript::EcmaHandleScope::*; + panda::ecmascript::Mutex::*; + panda::ecmascript::RecursiveMutex::*; + panda::ecmascript::RWLock::*; + panda::ecmascript::ConditionVariable::*; + panda::ecmascript::LockHolder::*; + panda::ecmascript::ReadLockHolder::*; + panda::ecmascript::WriteLockHolder::*; }; extern "C" { get_ark_js_heap_crash_info; diff --git a/script/run_ark_executable.py b/script/run_ark_executable.py index b7c24b169b69af2278013d3a5287b8b4e29e61fe..c2dc61c5c79ea0efdd0d05542bd79cb19f3b34c5 100755 --- a/script/run_ark_executable.py +++ b/script/run_ark_executable.py @@ -122,7 +122,7 @@ def judge_output(args: object): """run executable and judge is success or not.""" start_time = time.time() [cmd, subp] = process_open(args) - timeout_limit = int(args.timeout_limit) if args.timeout_limit else 120 # units: s + timeout_limit = int(args.timeout_limit) if args.timeout_limit else 150 # units: s try: out, err = subp.communicate(timeout=timeout_limit) diff --git a/test/aottest/BUILD.gn b/test/aottest/BUILD.gn index fdda67d609d29fc17afcc871867538ea6189427c..74a95c9a70b306e81d2ccc6c545923595d90aa24 100644 --- a/test/aottest/BUILD.gn +++ b/test/aottest/BUILD.gn @@ -18,11 +18,10 @@ group("ark_aot_js_test") { "bigint_typed_array_constructors", "call_default_args", "constructor_returns_non_object", - + "tryldglobalbyname", "dynamicimport", "emptyif", "formatrangetoparts", - "module", "undefined", "cjs", @@ -30,7 +29,10 @@ group("ark_aot_js_test") { deps = [] foreach(test, test_list) { - deps += [ "${test}:${test}AotAction" ] + deps += [ + "${test}:${test}AotAction", + "${test}:${test}AotContextAction", + ] } } @@ -39,6 +41,7 @@ group("ark_aot_ts_test") { test_list = [ "add", "and", + "array", "ashr", "asyncgenerator", "asyncgeneratormultiloop", @@ -48,6 +51,7 @@ group("ark_aot_ts_test") { "await", "await_loop", "bind", + "binaryop_special_value", "builtinmath", "call_same_bytecode_func", "callithisrange", @@ -55,9 +59,10 @@ group("ark_aot_ts_test") { "classstatic", "class_method_signature", "closeiterator", - + "compiler_test", "continue_from_finally", "copyrestargs", + "createarrayimm16", "createarraywithbuffer", "createemptyarray", "createemptyobject", @@ -69,6 +74,7 @@ group("ark_aot_ts_test") { "dec", "defineasyncfunc", "defineclasswithbuffer", + "defineclass", "definefunc", "definefunc_variable_args", "definegeneratorfunc", @@ -82,10 +88,12 @@ group("ark_aot_ts_test") { "div", "duplicatefunctions", "duplicatekey", + "elements_kind", "exception_case1", "exception_case10", "exception_case11", "exception_case12", + "exception_case13", "exception_case2", "exception_case3", "exception_case4", @@ -95,6 +103,8 @@ group("ark_aot_ts_test") { "exception_case8", "exception_case9", "exp", + "fast_call_builtins", + "frame_states", "forloop", "framestatesasync", "framestatesphi", @@ -116,6 +126,7 @@ group("ark_aot_ts_test") { "instanceof", "isfalse", "isin", + "isomorphism", "istrue", "jsonstringify", "large_func", @@ -129,9 +140,11 @@ group("ark_aot_ts_test") { "ldstlexvar", "ldsuperbyname", "logic_op", + "loop_peeling", "loop_phi", "loop_with_variable_exchange", "loops", + "map", "mod", "modules", "mul", @@ -142,16 +155,23 @@ group("ark_aot_ts_test") { "newobjrange", "newobjspread", "not", + "numberspeculativeretype", + "operations_stub_test", "optimization", + "optimized_call", "or", + "pgo_call", "poplexenv", "proxy", "resumegenerator", + "rodata", "setobjectwithproto", + "set", "shl", "shr", "specialloops", "starrayspread", + "statesplit", "stclasstoglobalrecord", "stconsttoglobalrecord", "stglobalvar", @@ -166,6 +186,8 @@ group("ark_aot_ts_test") { "stownbyvaluewithnameset", "strictequal", "strictnotequal", + "string", + "string_equal", "stsuperbyname", "sub", "supercall", @@ -182,21 +204,46 @@ group("ark_aot_ts_test") { "throwundefindeifhole", "tonumber", "tonumeric", + "try", "try_catch_finally", "trystglobalbynameprefid32", + "ts_hclass_generator", "ts_inline", + "ts_inline_accessor", + "ts_inline_accessor_deopt", + "ts_inline_accessor_extends", + "ts_inline_accessor_same_name", "ts_inline_change_target", "ts_inline_deopt", + "ts_inline_deopt_loop", + "ts_inline_exception1", + "ts_inline_exception2", + "ts_inline_exception3", + "ts_inline_exception4", + "ts_inline_exception5", + "ts_inline_exception6", + "ts_inline_exception7", + "ts_inline_exception8", "ts_inline_extends", "ts_inline_loop", "ts_inline_max_call", + "ts_multi_inline", + "ts_multi_inline_deopt", + "ts_multi_inline_max_call", + "ts_multi_inline_recursive", + "typedarray", + "typedarray_load_store", "typeof", + "unaryop_special_value", "xor", ] deps = [] foreach(test, test_list) { - deps += [ "${test}:${test}AotAction" ] + deps += [ + "${test}:${test}AotAction", + "${test}:${test}AotContextAction", + ] } if (is_debug) { @@ -206,7 +253,10 @@ group("ark_aot_ts_test") { "builtins_stub", ] foreach(test, debug_test_list) { - deps += [ "${test}:${test}AotAction" ] + deps += [ + "${test}:${test}AotAction", + "${test}:${test}AotContextAction", + ] } } } @@ -220,6 +270,7 @@ group("ark_aot_test") { "aot_compatibility_test:aot_compatibility_test", "aot_multi_constantpool_test:aot_multi_constantpool_test", "aot_type_test:aot_type_test", + "object:object_test", "vtable:vtable_test", ] } diff --git a/test/aottest/aot_type_test/BUILD.gn b/test/aottest/aot_type_test/BUILD.gn index d74e5e2de41ecf8e5559170137d59c2165ce1b30..c1d7ec9367d90790cc455ca537964867d02a0694 100644 --- a/test/aottest/aot_type_test/BUILD.gn +++ b/test/aottest/aot_type_test/BUILD.gn @@ -17,6 +17,15 @@ group("aot_type_test") { test_list = [ "arraylength", "float32Array", + "float64Array", + "int8Array", + "uint8Array", + "uint8ClampedArray", + "int16Array", + "uint16Array", + "int32Array", + "polyaccess", + "uint32Array", "typeadd", "typediv", "typeequal", diff --git a/test/aottest/aot_type_test/float64Array/BUILD.gn b/test/aottest/aot_type_test/float64Array/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..f333dbaed700d70ccdf1928fe77675d1801b4c89 --- /dev/null +++ b/test/aottest/aot_type_test/float64Array/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("float64Array") { + deps = [] +} diff --git a/test/aottest/aot_type_test/float64Array/expect_output.txt b/test/aottest/aot_type_test/float64Array/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..19da7fb8d7c4acb61f0ea65e64c7f846ca9436a6 --- /dev/null +++ b/test/aottest/aot_type_test/float64Array/expect_output.txt @@ -0,0 +1,15 @@ +# 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. + +1 +29.5 diff --git a/test/aottest/aot_type_test/float64Array/float64Array.ts b/test/aottest/aot_type_test/float64Array/float64Array.ts new file mode 100644 index 0000000000000000000000000000000000000000..2c940e2b7deed841dcfea0225c2224f0e2bc20de --- /dev/null +++ b/test/aottest/aot_type_test/float64Array/float64Array.ts @@ -0,0 +1,26 @@ +/* + * 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. + */ + +declare function print(arg:any):string; +{ + let typedArray : Float64Array = new Float64Array([1.5, 2.5, 3, 4.5, 5.5, 6, 7]); + typedArray[0] = 1; + print(typedArray[0]) + let s = 0; + for (let i = 0; i < typedArray.length; ++i) { + s += typedArray[i]; + } + print(s); +} \ No newline at end of file diff --git a/test/aottest/aot_type_test/int16Array/BUILD.gn b/test/aottest/aot_type_test/int16Array/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..635eff5a8a9121ce2dda902cc173dea0874b0eb0 --- /dev/null +++ b/test/aottest/aot_type_test/int16Array/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("int16Array") { + deps = [] +} diff --git a/test/aottest/aot_type_test/int16Array/expect_output.txt b/test/aottest/aot_type_test/int16Array/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..c98a6ad6185d129c3cb337cf86dba037d3979cfa --- /dev/null +++ b/test/aottest/aot_type_test/int16Array/expect_output.txt @@ -0,0 +1,19 @@ +# 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. + +32767 +-32768 +-32768 +32767 +32768 +-32769 diff --git a/test/aottest/aot_type_test/int16Array/int16Array.ts b/test/aottest/aot_type_test/int16Array/int16Array.ts new file mode 100644 index 0000000000000000000000000000000000000000..9f8e73c4861b1c958ba61a2164ec2fa31d14c633 --- /dev/null +++ b/test/aottest/aot_type_test/int16Array/int16Array.ts @@ -0,0 +1,31 @@ +/* + * 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. + */ + +declare function print(arg:any):string; +{ + let typedArray : Int16Array = new Int16Array([1, 2, 32760, -32760, 7, 8, 8, 9]); + typedArray[0] = typedArray[2] + typedArray[4]; //upper bound + print(typedArray[0]); + typedArray[1] = typedArray[3] - typedArray[5]; //lower bound + print(typedArray[1]); + typedArray[0] = typedArray[2] + typedArray[6]; //over flow + print(typedArray[0]); + typedArray[1] = typedArray[3] - typedArray[7]; //under spill + print(typedArray[1]); + let s = typedArray[2] + typedArray[6]; + print(s); + s = typedArray[3] - typedArray[7]; + print(s); +} \ No newline at end of file diff --git a/test/aottest/aot_type_test/int32Array/BUILD.gn b/test/aottest/aot_type_test/int32Array/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..4393201c356ded9305633b4a15d0bb6f954427a9 --- /dev/null +++ b/test/aottest/aot_type_test/int32Array/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("int32Array") { + deps = [] +} diff --git a/test/aottest/aot_type_test/int32Array/expect_output.txt b/test/aottest/aot_type_test/int32Array/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..cde62bf37e89370ce0855407be1d5b976138e522 --- /dev/null +++ b/test/aottest/aot_type_test/int32Array/expect_output.txt @@ -0,0 +1,19 @@ +# 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. + +2147483647 +-2147483648 +-2147483648 +2147483647 +2147483648 +-2147483649 diff --git a/test/aottest/aot_type_test/int32Array/int32Array.ts b/test/aottest/aot_type_test/int32Array/int32Array.ts new file mode 100644 index 0000000000000000000000000000000000000000..0ee7d24aa8ba74bb7bf96d622d0305fb786baf20 --- /dev/null +++ b/test/aottest/aot_type_test/int32Array/int32Array.ts @@ -0,0 +1,31 @@ +/* + * 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. + */ + +declare function print(arg:any):string; +{ + let typedArray : Int32Array = new Int32Array([1, 2, 2147483640, -2147483640, 7, 8, 8, 9]); + typedArray[0] = typedArray[2] + typedArray[4]; //upper bound + print(typedArray[0]); + typedArray[1] = typedArray[3] - typedArray[5]; //lower bound + print(typedArray[1]); + typedArray[0] = typedArray[2] + typedArray[6]; //over flow + print(typedArray[0]); + typedArray[1] = typedArray[3] - typedArray[7]; //under spill + print(typedArray[1]); + let s = typedArray[2] + typedArray[6]; + print(s); + s = typedArray[3] - typedArray[7]; + print(s); +} \ No newline at end of file diff --git a/test/aottest/aot_type_test/int8Array/BUILD.gn b/test/aottest/aot_type_test/int8Array/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..25fe205c796dff1c5985a79cf45c7498ffd71ac5 --- /dev/null +++ b/test/aottest/aot_type_test/int8Array/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("int8Array") { + deps = [] +} diff --git a/test/aottest/aot_type_test/int8Array/expect_output.txt b/test/aottest/aot_type_test/int8Array/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..a59546956da72ec7a2e65632f56c22935ac307a3 --- /dev/null +++ b/test/aottest/aot_type_test/int8Array/expect_output.txt @@ -0,0 +1,19 @@ +# 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. + +127 +-128 +-128 +127 +128 +-129 diff --git a/test/aottest/aot_type_test/int8Array/int8Array.ts b/test/aottest/aot_type_test/int8Array/int8Array.ts new file mode 100644 index 0000000000000000000000000000000000000000..ebe32ab7f4844777c0ee3d759eb6f15485a1a7e7 --- /dev/null +++ b/test/aottest/aot_type_test/int8Array/int8Array.ts @@ -0,0 +1,31 @@ +/* + * 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. + */ + +declare function print(arg:any):string; +{ + let typedArray : Int8Array = new Int8Array([1, 2, 100, -100, 27, 28, 28, 29]); + typedArray[0] = typedArray[2] + typedArray[4]; //upper bound + print(typedArray[0]); + typedArray[1] = typedArray[3] - typedArray[5]; //lower bound + print(typedArray[1]); + typedArray[0] = typedArray[2] + typedArray[6]; //over flow + print(typedArray[0]); + typedArray[1] = typedArray[3] - typedArray[7]; //under spill + print(typedArray[1]); + let s = typedArray[2] + typedArray[6]; + print(s); + s = typedArray[3] - typedArray[7]; + print(s); +} \ No newline at end of file diff --git a/test/aottest/aot_type_test/polyaccess/BUILD.gn b/test/aottest/aot_type_test/polyaccess/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..5f6ce0379b0e9cc7d120852430ecf9f22a51ebd3 --- /dev/null +++ b/test/aottest/aot_type_test/polyaccess/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("polyaccess") { + deps = [] +} diff --git a/test/aottest/aot_type_test/polyaccess/expect_output.txt b/test/aottest/aot_type_test/polyaccess/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..b913893c66eaf2aecc556f86e81ba727a7ba7cc8 --- /dev/null +++ b/test/aottest/aot_type_test/polyaccess/expect_output.txt @@ -0,0 +1,31 @@ +# 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. + +1 +3 +1 +2 +3 +1 +undefined +3 +1 +1 +1 +33 +1 +2 +33 +1 +undefined +33 diff --git a/test/aottest/aot_type_test/polyaccess/polyaccess.ts b/test/aottest/aot_type_test/polyaccess/polyaccess.ts new file mode 100644 index 0000000000000000000000000000000000000000..10a19f3c5dfff3aed88b8c237c1bb0af4ec0fe09 --- /dev/null +++ b/test/aottest/aot_type_test/polyaccess/polyaccess.ts @@ -0,0 +1,63 @@ +/* + * 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. + */ + +declare function print(arg: any):string; + +class C { + x:number = 1; + y:number = 11; +} + +class D { + y:number = 22; + x:number = 2; +} + +function foo(o1: C | D, o2: C) { + // poly + print(o1.x); + o1.y = 3; + print(o1.y); + + // mono + print(o2.x); +} + +function bar(o1: C, o2: C | D) { + // mono + print(o1.x); + + // poly + print(o2.x); + o2.y = 33; + print(o2.y); +} + +function test() { + let c = new C(); + let d = new D(); + foo(c, c); + foo(d, c); + // @ts-ignore + foo({}, c); // will deopt after 2 comparisons + + bar(c, c); + bar(c, d); + // @ts-ignore + bar(c, {}); // will deopt after 2 comparisons +} + +test(); + diff --git a/test/aottest/aot_type_test/uint16Array/BUILD.gn b/test/aottest/aot_type_test/uint16Array/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..9b3cae4ea017951a35158ddfc3526a748681cc54 --- /dev/null +++ b/test/aottest/aot_type_test/uint16Array/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("uint16Array") { + deps = [] +} diff --git a/test/aottest/aot_type_test/uint16Array/expect_output.txt b/test/aottest/aot_type_test/uint16Array/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..bd9b238dd6a2f74d8344645bffe5d975a855fa20 --- /dev/null +++ b/test/aottest/aot_type_test/uint16Array/expect_output.txt @@ -0,0 +1,19 @@ +# 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. + +65535 +0 +0 +65535 +65536 +-1 diff --git a/test/aottest/aot_type_test/uint16Array/uint16Array.ts b/test/aottest/aot_type_test/uint16Array/uint16Array.ts new file mode 100644 index 0000000000000000000000000000000000000000..e126f15d466725f04a5e0a5c66539994039f7703 --- /dev/null +++ b/test/aottest/aot_type_test/uint16Array/uint16Array.ts @@ -0,0 +1,31 @@ +/* + * 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. + */ + +declare function print(arg:any):string; +{ + let typedArray : Uint16Array = new Uint16Array([1, 2, 65530, 5, 5, 5, 6, 6]); + typedArray[0] = typedArray[2] + typedArray[4]; //upper bound + print(typedArray[0]); + typedArray[1] = typedArray[3] - typedArray[5]; //lower bound + print(typedArray[1]); + typedArray[0] = typedArray[2] + typedArray[6]; //over flow + print(typedArray[0]); + typedArray[1] = typedArray[3] - typedArray[7]; //under spill + print(typedArray[1]); + let s = typedArray[2] + typedArray[6]; + print(s); + s = typedArray[3] - typedArray[7]; + print(s); +} \ No newline at end of file diff --git a/test/aottest/aot_type_test/uint32Array/BUILD.gn b/test/aottest/aot_type_test/uint32Array/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..792c5686835d98884451b2dbfc0ef423dd2d098d --- /dev/null +++ b/test/aottest/aot_type_test/uint32Array/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("uint32Array") { + deps = [] +} diff --git a/test/aottest/aot_type_test/uint32Array/expect_output.txt b/test/aottest/aot_type_test/uint32Array/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..efe3d044f6c99b591ba69783e56b5fd676412975 --- /dev/null +++ b/test/aottest/aot_type_test/uint32Array/expect_output.txt @@ -0,0 +1,32 @@ +# 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. + +4294967290 +4294967295 +0 +0 +4294967295 +4294967296 +-1 +1 +-1 +-1 +-1 +2147483647 +1 +-1 +-1 +-2 +-2147483648 +-2 +0 diff --git a/test/aottest/aot_type_test/uint32Array/uint32Array.ts b/test/aottest/aot_type_test/uint32Array/uint32Array.ts new file mode 100644 index 0000000000000000000000000000000000000000..e036d2c146726f1b362bc6953c076a970b696f11 --- /dev/null +++ b/test/aottest/aot_type_test/uint32Array/uint32Array.ts @@ -0,0 +1,56 @@ +/* + * 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. + */ + +declare function print(arg:any):string; +{ + let typedArray : Uint32Array = new Uint32Array([1, 2, 4294967290, 5, 5, 5, 6, 6, 4294967295]); + print(typedArray[2]); + typedArray[0] = typedArray[2] + typedArray[4]; //upper bound + print(typedArray[0]); + typedArray[1] = typedArray[3] - typedArray[5]; //lower bound + print(typedArray[1]); + typedArray[0] = typedArray[2] + typedArray[6]; //over flow + print(typedArray[0]); + typedArray[1] = typedArray[3] - typedArray[7]; //under spill + print(typedArray[1]); + let s = typedArray[2] + typedArray[6]; + print(s); + s = typedArray[3] - typedArray[7]; + print(s); + + // and + print(typedArray[8] & 1); + print(typedArray[8] & -1); + + // or + print(typedArray[8] | 1); + print(typedArray[8] | -1); + + // ashr + print(typedArray[8] >>> 1); + print(typedArray[8] >>> -1); + + // shr + print(typedArray[8] >> 1); + print(typedArray[8] >> -1); + + // shl + print(typedArray[8] << 1); + print(typedArray[8] << -1); + + // xor + print(typedArray[8] ^ 1); + print(typedArray[8] ^ -1); +} \ No newline at end of file diff --git a/test/aottest/aot_type_test/uint8Array/BUILD.gn b/test/aottest/aot_type_test/uint8Array/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..790dc3e1d70b5b29ef43483b575b4662894a679e --- /dev/null +++ b/test/aottest/aot_type_test/uint8Array/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("uint8Array") { + deps = [] +} diff --git a/test/aottest/aot_type_test/uint8Array/expect_output.txt b/test/aottest/aot_type_test/uint8Array/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..cd3efc9d30d153c5f6a8ee7f2317fb0674f04641 --- /dev/null +++ b/test/aottest/aot_type_test/uint8Array/expect_output.txt @@ -0,0 +1,19 @@ +# 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. + +255 +0 +0 +255 +256 +-1 diff --git a/test/aottest/aot_type_test/uint8Array/uint8Array.ts b/test/aottest/aot_type_test/uint8Array/uint8Array.ts new file mode 100644 index 0000000000000000000000000000000000000000..9403ce53803f58ac5076c9f3b6b3e8a3c5fb4a26 --- /dev/null +++ b/test/aottest/aot_type_test/uint8Array/uint8Array.ts @@ -0,0 +1,31 @@ +/* + * 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. + */ + +declare function print(arg:any):string; +{ + let typedArray : Uint8Array = new Uint8Array([1, 2, 250, 5, 5, 5, 6, 6]); + typedArray[0] = typedArray[2] + typedArray[4]; //upper bound + print(typedArray[0]); + typedArray[1] = typedArray[3] - typedArray[5]; //lower bound + print(typedArray[1]); + typedArray[0] = typedArray[2] + typedArray[6]; //over flow + print(typedArray[0]); + typedArray[1] = typedArray[3] - typedArray[7]; //under spill + print(typedArray[1]); + let s = typedArray[2] + typedArray[6]; + print(s); + s = typedArray[3] - typedArray[7]; + print(s); +} \ No newline at end of file diff --git a/test/aottest/aot_type_test/uint8ClampedArray/BUILD.gn b/test/aottest/aot_type_test/uint8ClampedArray/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..d027b9ca0b6ce3eed24ab1d437067a1309085c23 --- /dev/null +++ b/test/aottest/aot_type_test/uint8ClampedArray/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("uint8ClampedArray") { + deps = [] +} diff --git a/test/aottest/aot_type_test/uint8ClampedArray/expect_output.txt b/test/aottest/aot_type_test/uint8ClampedArray/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..449ef050163ed3bb763b7767317618980044ecfd --- /dev/null +++ b/test/aottest/aot_type_test/uint8ClampedArray/expect_output.txt @@ -0,0 +1,21 @@ +# 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. + +255 +0 +255 +0 +255 +0 +257 +-2 diff --git a/test/aottest/aot_type_test/uint8ClampedArray/uint8ClampedArray.ts b/test/aottest/aot_type_test/uint8ClampedArray/uint8ClampedArray.ts new file mode 100644 index 0000000000000000000000000000000000000000..49edf7660baa729d41a45789a134d8a10d6b2fd0 --- /dev/null +++ b/test/aottest/aot_type_test/uint8ClampedArray/uint8ClampedArray.ts @@ -0,0 +1,33 @@ +/* + * 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. + */ + +declare function print(arg:any):string; +{ + let typedArray : Uint8ClampedArray = new Uint8ClampedArray([8848, -6666, 250, 5, 5, 5, 7, 7]); + print(typedArray[0]); + print(typedArray[1]); + typedArray[0] = typedArray[2] + typedArray[4]; //upper bound + print(typedArray[0]); + typedArray[1] = typedArray[3] - typedArray[5]; //lower bound + print(typedArray[1]); + typedArray[0] = typedArray[2] + typedArray[6]; //over flow + print(typedArray[0]); + typedArray[1] = typedArray[3] - typedArray[7]; //under spill + print(typedArray[1]); + let s = typedArray[2] + typedArray[6]; + print(s); + s = typedArray[3] - typedArray[7]; + print(s); +} \ No newline at end of file diff --git a/test/aottest/array/BUILD.gn b/test/aottest/array/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..c7ec4de51f2dec5bec4a5ef22d250f3f211e00b0 --- /dev/null +++ b/test/aottest/array/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("array") { + deps = [] +} diff --git a/test/aottest/array/array.ts b/test/aottest/array/array.ts new file mode 100644 index 0000000000000000000000000000000000000000..0c3ea9634c88d752cc922cbfd7d8f506a40a4378 --- /dev/null +++ b/test/aottest/array/array.ts @@ -0,0 +1,39 @@ +/* + * 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. + */ + +declare function print(arg:any):string; +let a = [] +let l = a.push(1) +print(l) +l = a.push(1, 2, 3, 4, 5) +print(l) + +for (let i = 0; i < 100; i++) { + a.push(i) +} + +let c = [1, 2, 3, 4] +a.push(...c) + +print(a.length) + +let b = [] +b.push(1, 2, 3, 4) +b.push(1, 2, 3) +b.push(1, 2) +b.push(1) +b.push() +print(Object.values(b)) +print(b.length) diff --git a/test/aottest/array/expect_output.txt b/test/aottest/array/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..4c3ea11575b04bbac25735db95fbf4a0fac8d5ac --- /dev/null +++ b/test/aottest/array/expect_output.txt @@ -0,0 +1,18 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +1 +6 +110 +1,2,3,4,1,2,3,1,2,1 +10 diff --git a/test/aottest/array_bounds_check_elimination/BUILD.gn b/test/aottest/array_bounds_check_elimination/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..1db8a669bf0e1134156ecb39d86ce532dbb32fef --- /dev/null +++ b/test/aottest/array_bounds_check_elimination/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("array_bounds_check_elimination") { + deps = [] +} diff --git a/test/aottest/array_bounds_check_elimination/array_bounds_check_elimination.cpp b/test/aottest/array_bounds_check_elimination/array_bounds_check_elimination.cpp new file mode 100644 index 0000000000000000000000000000000000000000..44d1ca866a05884761b6c927f4948a0fe1a31145 --- /dev/null +++ b/test/aottest/array_bounds_check_elimination/array_bounds_check_elimination.cpp @@ -0,0 +1,70 @@ +/* + * 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. + */ + +function get(a: number[], p: number): number { + if (p <= a.length && p > 0) { + return a[p - 1]; + } + return 0; +} + +function get2(a: number[], p: number): number { + if (p <= a.length - 1 && p >= 0) { + return a[p]; + } + return 0; +} + +function clear1(a: number[], x: number) { + for (let i = 0; i < x; i++) { + a[i] = 0; + } +} + +function clear2(b: Int32Array[], x: number) { + for (let i = 0; i < x; i++) { + b[i] = 0; + } +} + +function triple(a: number[], i: number) { + a[i + 3] = 0; + a[i + 1] = 0; + a[i + 2] = 0; +} + +function triple2(a: number[]) { + a[0] = 0; + a[1] = 0; + a[2] = 0; +} + +let a: number[] = [1, 2, 3]; +let b: Int32Array[] = [1, 2, 3]; +let x = 3; +print(get(a, x)); +print(get2(a, x)); + +clear1(a, 3); +clear2(b, 3); + +clear1(a, 4); +clear2(b, 4); + +let c: number[] = [1, 2, 3, 4]; +triple(c, 0); +triple2(a); +print(a[2]); +print(c[2]); \ No newline at end of file diff --git a/test/aottest/array_bounds_check_elimination/expect_output.txt b/test/aottest/array_bounds_check_elimination/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..070089698e42a731b0e96bfea40fcbe59c4778f9 --- /dev/null +++ b/test/aottest/array_bounds_check_elimination/expect_output.txt @@ -0,0 +1,17 @@ +# 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. + +3 +0 +0 +0 \ No newline at end of file diff --git a/test/aottest/async_loop/BUILD.gn b/test/aottest/async_loop/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..5cf938286e1230fc70009319b86fdc7a4c4fda02 --- /dev/null +++ b/test/aottest/async_loop/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("async_loop") { + deps = [] +} diff --git a/test/aottest/async_loop/async_loop.ts b/test/aottest/async_loop/async_loop.ts new file mode 100644 index 0000000000000000000000000000000000000000..a21b9208761b4a3918e84a34bcdee919ba47ed98 --- /dev/null +++ b/test/aottest/async_loop/async_loop.ts @@ -0,0 +1,25 @@ +/* + * 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. + */ + +declare function goo(): Promise; + +async function foo() { + let last = 10000001 % 1; + for (let i = 0; i < last; i++) { + await goo(); + } +} + +print("success") \ No newline at end of file diff --git a/test/aottest/async_loop/expect_output.txt b/test/aottest/async_loop/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..5ad2c95118fabe73824bf777fb0d96b6713e7288 --- /dev/null +++ b/test/aottest/async_loop/expect_output.txt @@ -0,0 +1,14 @@ +# 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. + +success diff --git a/test/aottest/binaryop_special_value/BUILD.gn b/test/aottest/binaryop_special_value/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..da8fc2daf0834c7a33a4b191dad015de9c10b953 --- /dev/null +++ b/test/aottest/binaryop_special_value/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("binaryop_special_value") { + deps = [] +} diff --git a/test/aottest/binaryop_special_value/binaryop_special_value.ts b/test/aottest/binaryop_special_value/binaryop_special_value.ts new file mode 100644 index 0000000000000000000000000000000000000000..0314c044b6e78766472be5af4127080ac75ea0f5 --- /dev/null +++ b/test/aottest/binaryop_special_value/binaryop_special_value.ts @@ -0,0 +1,403 @@ +/* + * 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. + */ + +declare function print(arg:any):string; + +let undf: any = undefined; +let intNum: number = 5; +let doubleNum: number = 1.1; +let falseValue: boolean = false; +let trueValue: boolean = true; +let nullValue: any = null; + +// int op undefined +print("=====int op undefined=====") +print(intNum + undf); +print(intNum - undf); +print(intNum * undf); +print(intNum / undf); +print(intNum % undf); +print(intNum < undf); +print(intNum <= undf); +print(intNum > undf); +print(intNum >= undf); +print(intNum == undf); +print(intNum === undf); +print(intNum != undf); +print(intNum << undf); +print(intNum >> undf); +print(intNum >>> undf); +print(intNum | undf); +print(intNum ^ undf); + +// double op undefined +print("=====double op undefined=====") +print(doubleNum + undf); +print(doubleNum - undf); +print(doubleNum * undf); +print(doubleNum / undf); +print(doubleNum % undf); +print(doubleNum < undf); +print(doubleNum <= undf); +print(doubleNum > undf); +print(doubleNum >= undf); +print(doubleNum == undf); +print(doubleNum === undf); +print(doubleNum != undf); +print(doubleNum << undf); +print(doubleNum >> undf); +print(doubleNum >>> undf); +print(doubleNum | undf); +print(doubleNum ^ undf); + +// false op undefined +print("=====false op undefined=====") +print(falseValue + undf); +print(falseValue - undf); +print(falseValue * undf); +print(falseValue / undf); +print(falseValue % undf); +print(falseValue < undf); +print(falseValue <= undf); +print(falseValue > undf); +print(falseValue >= undf); +print(falseValue == undf); +print(falseValue === undf); +print(falseValue != undf); +print(falseValue << undf); +print(falseValue >> undf); +print(falseValue >>> undf); +print(falseValue | undf); +print(falseValue ^ undf); + +// true op undefined +print("=====true op undefined=====") +print(trueValue + undf); +print(trueValue - undf); +print(trueValue * undf); +print(trueValue / undf); +print(trueValue % undf); +print(trueValue < undf); +print(trueValue <= undf); +print(trueValue > undf); +print(trueValue >= undf); +print(trueValue == undf); +print(trueValue === undf); +print(trueValue != undf); +print(trueValue << undf); +print(trueValue >> undf); +print(trueValue >>> undf); +print(trueValue | undf); +print(trueValue ^ undf); + +// null op undefined +print("=====null op undefined=====") +print(nullValue + undf); +print(nullValue - undf); +print(nullValue * undf); +print(nullValue / undf); +print(nullValue % undf); +print(nullValue < undf); +print(nullValue <= undf); +print(nullValue > undf); +print(nullValue >= undf); +print(nullValue == undf); +print(nullValue === undf); +print(nullValue != undf); +print(nullValue << undf); +print(nullValue >> undf); +print(nullValue >>> undf); +print(nullValue | undf); +print(nullValue ^ undf); + +// int op null +print("=====int op null=====") +print(intNum + nullValue); +print(intNum - nullValue); +print(intNum * nullValue); +print(intNum / nullValue); +print(intNum % nullValue); +print(intNum < nullValue); +print(intNum <= nullValue); +print(intNum > nullValue); +print(intNum >= nullValue); +print(intNum == nullValue); +print(intNum === nullValue); +print(intNum != nullValue); +print(intNum << nullValue); +print(intNum >> nullValue); +print(intNum >>> nullValue); +print(intNum | nullValue); +print(intNum ^ nullValue); + +// double op null +print("=====double op null=====") +print(doubleNum + nullValue); +print(doubleNum - nullValue); +print(doubleNum * nullValue); +print(doubleNum / nullValue); +print(doubleNum % nullValue); +print(doubleNum < nullValue); +print(doubleNum <= nullValue); +print(doubleNum > nullValue); +print(doubleNum >= nullValue); +print(doubleNum == nullValue); +print(doubleNum === nullValue); +print(doubleNum != nullValue); +print(doubleNum << nullValue); +print(doubleNum >> nullValue); +print(doubleNum >>> nullValue); +print(doubleNum | nullValue); +print(doubleNum ^ nullValue); + +// false op null +print("=====false op null=====") +print(falseValue + nullValue); +print(falseValue - nullValue); +print(falseValue * nullValue); +print(falseValue / nullValue); +print(falseValue % nullValue); +print(falseValue < nullValue); +print(falseValue <= nullValue); +print(falseValue > nullValue); +print(falseValue >= nullValue); +print(falseValue == nullValue); +print(falseValue === nullValue); +print(falseValue != nullValue); +print(falseValue << nullValue); +print(falseValue >> nullValue); +print(falseValue >>> nullValue); +print(falseValue | nullValue); +print(falseValue ^ nullValue); + +// true op null +print("=====true op null=====") +print(trueValue + nullValue); +print(trueValue - nullValue); +print(trueValue * nullValue); +print(trueValue / nullValue); +print(trueValue % nullValue); +print(trueValue < nullValue); +print(trueValue <= nullValue); +print(trueValue > nullValue); +print(trueValue >= nullValue); +print(trueValue == nullValue); +print(trueValue === nullValue); +print(trueValue != nullValue); +print(trueValue << nullValue); +print(trueValue >> nullValue); +print(trueValue >>> nullValue); +print(trueValue | nullValue); +print(trueValue ^ nullValue); + +// int op false +print("=====int op false=====") +print(intNum + falseValue); +print(intNum - falseValue); +print(intNum * falseValue); +print(intNum / falseValue); +print(intNum % falseValue); +print(intNum < falseValue); +print(intNum <= falseValue); +print(intNum > falseValue); +print(intNum >= falseValue); +print(intNum == falseValue); +print(intNum === falseValue); +print(intNum != falseValue); +print(intNum << falseValue); +print(intNum >> falseValue); +print(intNum >>> falseValue); +print(intNum | falseValue); +print(intNum ^ falseValue); + +// double op false +print("=====double op false=====") +print(doubleNum + falseValue); +print(doubleNum - falseValue); +print(doubleNum * falseValue); +print(doubleNum / falseValue); +print(doubleNum % falseValue); +print(doubleNum < falseValue); +print(doubleNum <= falseValue); +print(doubleNum > falseValue); +print(doubleNum >= falseValue); +print(doubleNum == falseValue); +print(doubleNum === falseValue); +print(doubleNum != falseValue); +print(doubleNum << falseValue); +print(doubleNum >> falseValue); +print(doubleNum >>> falseValue); +print(doubleNum | falseValue); +print(doubleNum ^ falseValue); + +// true op false +print("=====true op false=====") +print(trueValue + falseValue); +print(trueValue - falseValue); +print(trueValue * falseValue); +print(trueValue / falseValue); +print(trueValue % falseValue); +print(trueValue < falseValue); +print(trueValue <= falseValue); +print(trueValue > falseValue); +print(trueValue >= falseValue); +print(trueValue == falseValue); +print(trueValue === falseValue); +print(trueValue != falseValue); +print(trueValue << falseValue); +print(trueValue >> falseValue); +print(trueValue >>> falseValue); +print(trueValue | falseValue); +print(trueValue ^ falseValue); + +// int op true +print("=====int op true=====") +print(intNum + trueValue); +print(intNum - trueValue); +print(intNum * trueValue); +print(intNum / trueValue); +print(intNum % trueValue); +print(intNum < trueValue); +print(intNum <= trueValue); +print(intNum > trueValue); +print(intNum >= trueValue); +print(intNum == trueValue); +print(intNum === trueValue); +print(intNum != trueValue); +print(intNum << trueValue); +print(intNum >> trueValue); +print(intNum >>> trueValue); +print(intNum | trueValue); +print(intNum ^ trueValue); + +// double op true +print("=====double op true=====") +print(doubleNum + trueValue); +print(doubleNum - trueValue); +print(doubleNum * trueValue); +print(doubleNum / trueValue); +print(doubleNum % trueValue); +print(doubleNum < trueValue); +print(doubleNum <= trueValue); +print(doubleNum > trueValue); +print(doubleNum >= trueValue); +print(doubleNum == trueValue); +print(doubleNum === trueValue); +print(doubleNum != trueValue); +print(doubleNum << trueValue); +print(doubleNum >> trueValue); +print(doubleNum >>> trueValue); +print(doubleNum | trueValue); +print(doubleNum ^ trueValue); + +// true op false +print("=====true op false=====") +print(trueValue + falseValue); +print(trueValue - falseValue); +print(trueValue * falseValue); +print(trueValue / falseValue); +print(trueValue % falseValue); +print(trueValue < falseValue); +print(trueValue <= falseValue); +print(trueValue > falseValue); +print(trueValue >= falseValue); +print(trueValue == falseValue); +print(trueValue === falseValue); +print(trueValue != falseValue); +print(trueValue << falseValue); +print(trueValue >> falseValue); +print(trueValue >>> falseValue); +print(trueValue | falseValue); +print(trueValue ^ falseValue); + +// undefined op undefined +print("=====undefined op undefined=====") +print(undf + undf); +print(undf - undf); +print(undf * undf); +print(undf / undf); +print(undf % undf); +print(undf < undf); +print(undf <= undf); +print(undf > undf); +print(undf >= undf); +print(undf == undf); +print(undf === undf); +print(undf != undf); +print(undf << undf); +print(undf >> undf); +print(undf >>> undf); +print(undf | undf); +print(undf ^ undf); + +// null op null +print("=====null op null=====") +print(nullValue + nullValue); +print(nullValue - nullValue); +print(nullValue * nullValue); +print(nullValue / nullValue); +print(nullValue % nullValue); +print(nullValue < nullValue); +print(nullValue <= nullValue); +print(nullValue > nullValue); +print(nullValue >= nullValue); +print(nullValue == nullValue); +print(nullValue === nullValue); +print(nullValue != nullValue); +print(nullValue << nullValue); +print(nullValue >> nullValue); +print(nullValue >>> nullValue); +print(nullValue | nullValue); +print(nullValue ^ nullValue); + +// true op true +print("=====true op true=====") +print(trueValue + trueValue); +print(trueValue - trueValue); +print(trueValue * trueValue); +print(trueValue / trueValue); +print(trueValue % trueValue); +print(trueValue < trueValue); +print(trueValue <= trueValue); +print(trueValue > trueValue); +print(trueValue >= trueValue); +print(trueValue == trueValue); +print(trueValue === trueValue); +print(trueValue != trueValue); +print(trueValue << trueValue); +print(trueValue >> trueValue); +print(trueValue >>> trueValue); +print(trueValue | trueValue); +print(trueValue ^ trueValue); + +// false op false +print("=====false op false=====") +print(falseValue + falseValue); +print(falseValue - falseValue); +print(falseValue * falseValue); +print(falseValue / falseValue); +print(falseValue % falseValue); +print(falseValue < falseValue); +print(falseValue <= falseValue); +print(falseValue > falseValue); +print(falseValue >= falseValue); +print(falseValue == falseValue); +print(falseValue === falseValue); +print(falseValue != falseValue); +print(falseValue << falseValue); +print(falseValue >> falseValue); +print(falseValue >>> falseValue); +print(falseValue | falseValue); +print(falseValue ^ falseValue); \ No newline at end of file diff --git a/test/aottest/binaryop_special_value/expect_output.txt b/test/aottest/binaryop_special_value/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..d9c4fc4348258d8052ad31b6b92300ab2487ba7f --- /dev/null +++ b/test/aottest/binaryop_special_value/expect_output.txt @@ -0,0 +1,355 @@ +# 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. + +=====int op undefined===== +NaN +NaN +NaN +NaN +NaN +false +false +false +false +false +false +true +5 +5 +5 +5 +5 +=====double op undefined===== +NaN +NaN +NaN +NaN +NaN +false +false +false +false +false +false +true +1 +1 +1 +1 +1 +=====false op undefined===== +NaN +NaN +NaN +NaN +NaN +false +false +false +false +false +false +true +0 +0 +0 +0 +0 +=====true op undefined===== +NaN +NaN +NaN +NaN +NaN +false +false +false +false +false +false +true +1 +1 +1 +1 +1 +=====null op undefined===== +NaN +NaN +NaN +NaN +NaN +false +false +false +false +true +false +false +0 +0 +0 +0 +0 +=====int op null===== +5 +5 +0 +Infinity +NaN +false +false +true +true +false +false +true +5 +5 +5 +5 +5 +=====double op null===== +1.1 +1.1 +0 +Infinity +NaN +false +false +true +true +false +false +true +1 +1 +1 +1 +1 +=====false op null===== +0 +0 +0 +NaN +NaN +false +true +false +true +false +false +true +0 +0 +0 +0 +0 +=====true op null===== +1 +1 +0 +Infinity +NaN +false +false +true +true +false +false +true +1 +1 +1 +1 +1 +=====int op false===== +5 +5 +0 +Infinity +NaN +false +false +true +true +false +false +true +5 +5 +5 +5 +5 +=====double op false===== +1.1 +1.1 +0 +Infinity +NaN +false +false +true +true +false +false +true +1 +1 +1 +1 +1 +=====true op false===== +1 +1 +0 +Infinity +NaN +false +false +true +true +false +false +true +1 +1 +1 +1 +1 +=====int op true===== +6 +4 +5 +5 +0 +false +false +true +true +false +false +true +10 +2 +2 +5 +4 +=====double op true===== +2.1 +0.10000000000000009 +1.1 +1.1 +0.10000000000000009 +false +false +true +true +false +false +true +2 +0 +0 +1 +0 +=====true op false===== +1 +1 +0 +Infinity +NaN +false +false +true +true +false +false +true +1 +1 +1 +1 +1 +=====undefined op undefined===== +NaN +NaN +NaN +NaN +NaN +false +false +false +false +true +true +false +0 +0 +0 +0 +0 +=====null op null===== +0 +0 +0 +NaN +NaN +false +true +false +true +true +true +false +0 +0 +0 +0 +0 +=====true op true===== +2 +0 +1 +1 +0 +false +true +false +true +true +true +false +2 +0 +0 +1 +0 +=====false op false===== +0 +0 +0 +NaN +NaN +false +true +false +true +true +true +false +0 +0 +0 +0 +0 diff --git a/test/aottest/builtins_number/BUILD.gn b/test/aottest/builtins_number/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..f1f8aa6604450391c53e322e9d88b275295724fb --- /dev/null +++ b/test/aottest/builtins_number/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("builtins_number") { + deps = [] +} diff --git a/test/aottest/builtins_number/builtins_number.ts b/test/aottest/builtins_number/builtins_number.ts new file mode 100644 index 0000000000000000000000000000000000000000..a89626cab5ed5ce890d77b44ab09f7537efb462b --- /dev/null +++ b/test/aottest/builtins_number/builtins_number.ts @@ -0,0 +1,29 @@ +/* + * 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. + */ +declare function print(arg:any):string; +{ + // test new builtin array + let numObj1 = "-100"; + print(Number.parseInt(numObj1)); + + let numObj2 = "1000"; + print(Number.parseInt(numObj2)); + + let numObj3 = "0001030"; + print(Number.parseInt(numObj3)); + + let numObj4 = " -1123"; + print(Number.parseInt(numObj4)); +} diff --git a/test/aottest/builtins_number/expect_output.txt b/test/aottest/builtins_number/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..02ca7fa5a4886ec2e6657d7bac343d5a78b22087 --- /dev/null +++ b/test/aottest/builtins_number/expect_output.txt @@ -0,0 +1,17 @@ +# 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. + +-100 +1000 +1030 +-1123 diff --git a/test/aottest/calls/calls.ts b/test/aottest/calls/calls.ts index 0573046c2442c7fd6fad8023ca582ac51c9f551b..65becf94301d6f32350321865b4bf8b32d545577 100644 --- a/test/aottest/calls/calls.ts +++ b/test/aottest/calls/calls.ts @@ -161,3 +161,16 @@ function funcAsm(value: number, value1: number, value2: number): number { } ArkTools.removeAOTFlag(funcAsm); funcAsm(1, 2); + +class TestCallThis0 { + foo(arg?: any) { + print(arg); + } +} + +function testCallThis0() { + let t = new TestCallThis0(); + t.foo(); +} + +testCallThis0(); diff --git a/test/aottest/calls/expect_output.txt b/test/aottest/calls/expect_output.txt index 135a21ad418a95725edc7da8fb1e8221d440c03e..400c7e9bf5f77b851efe7d2286e9996840c13616 100644 --- a/test/aottest/calls/expect_output.txt +++ b/test/aottest/calls/expect_output.txt @@ -92,3 +92,4 @@ C 7 9 10 +undefined diff --git a/test/aottest/class_method_signature/class_method_signature.ts b/test/aottest/class_method_signature/class_method_signature.ts index f8768784b093764f2d217c9239c7d456e005247b..5fc149826891d494d7c96f2b425e4d062ea8d6c2 100644 --- a/test/aottest/class_method_signature/class_method_signature.ts +++ b/test/aottest/class_method_signature/class_method_signature.ts @@ -15,6 +15,91 @@ declare function print(str:any):string; +class A { + constructor() {} + + foo?(): string; + + bar(): string { + return "bar"; + } + + bar2(): string { + return "bar2"; + } +} + +let a = new A(); +print(a.bar()); +print(a.bar2()); +print(Reflect.ownKeys(A.prototype)); //constructor,bar,bar2 + +class A2 { + constructor() {} + + foo?(): string; + foo(): string { + return "foo"; + } + + bar(): string { + return "bar"; + } + + bar2(): string { + return "bar2"; + } +} + +let a2 = new A2(); +print(a2.foo()); +print(a2.bar()); +print(a2.bar2()); +print(Reflect.ownKeys(A2.prototype)); //constructor,foo,bar,bar2 + + +class B { + constructor() {} + + bar(): string { + return "bar"; + } + + foo?(): string; + + bar2(): string { + return "bar2"; + } +} + +let b = new B(); +print(b.bar()); +print(b.bar2()); +print(Reflect.ownKeys(B.prototype)); //constructor,bar,bar2 + +class B2 { + constructor() {} + + bar(): string { + return "bar"; + } + + foo?(): string; + foo(): string { + return "foo"; + } + + bar2(): string { + return "bar2"; + } +} + +let b2 = new B2(); +print(b2.foo()); +print(b2.bar()); +print(b2.bar2()); +print(Reflect.ownKeys(B2.prototype)); //constructor,bar,foo,bar2 + // one signature but no body class C { constructor() {} diff --git a/test/aottest/class_method_signature/expect_output.txt b/test/aottest/class_method_signature/expect_output.txt index 4656c9a42d1419a8d7a614433e3bf508f0f19afb..e846618f11b7b7cf72b29cb6c5d3d894a5a382de 100644 --- a/test/aottest/class_method_signature/expect_output.txt +++ b/test/aottest/class_method_signature/expect_output.txt @@ -11,6 +11,20 @@ # See the License for the specific language governing permissions and # limitations under the License. +bar +bar2 +constructor,bar,bar2 +foo +bar +bar2 +constructor,foo,bar,bar2 +bar +bar2 +constructor,bar,bar2 +foo +bar +bar2 +constructor,bar,foo,bar2 test one signature but no body D test multi-signatures but one body diff --git a/test/aottest/classstatic/classstatic.ts b/test/aottest/classstatic/classstatic.ts index 492ff85b2d3e0848808d78b085630de8c9bf57f6..46c202b4ba54f1827959f06ab70ddce4ac830f91 100644 --- a/test/aottest/classstatic/classstatic.ts +++ b/test/aottest/classstatic/classstatic.ts @@ -167,6 +167,23 @@ class cpu1 { constructor() { this.mode = 4; } + + public static color1(op: number): number { + return op; + } + + public static color(color1:string, op: number): number; + + public static color(color1:number, op: number): number; + + public static color(color1: string | number, op: number): void { + print("method is ok"); + } + + public static color2(op: number): number { + return op; + } + get kind() { print(this.mode); return this.mode; @@ -186,9 +203,124 @@ print(systems.kind); cpu.sub(1, 4); print(cpu1.TwoC); print(cpu1.length); +cpu1.color(); try { cpu1.length = 4; } catch (e) { print(e) -} \ No newline at end of file +} + +class Info { + isHit: boolean = false; + hitCount: int = 0; + distance: number; + + constructor() { + this.distance = 0; + } + + toString() :string { + return 'Intersection [' + this.distance + ']'; + } +} + +class Shape { + intersect(ray: number) : Info { + return undefined; + } + func1(a:any,...A:any) : void { + for (let p in A) { + print(A[p]); + } + } +} + +class Scene { + shapes : Shape[]; + constructor() { + this.shapes = new Array(); + } +} + +class Sphere extends Shape { + radius: number; + + constructor(radius: number) { + super(); + this.radius = radius; + } + + intersect(ray: number) : Info { + var info = new Info(); + print("intersect"); + return info; + } + + func1() : void { + print("func1") + } +} + +function testIntersection(scene: Scene): number { + var hits = 0; + for(var i= 0; i { + if (typeof key == "bigint") { + bigIntSum += value; + return; + } + if (typeof key == "number") { + intSum += value; + return; + } + if (typeof key == "symbol") { + symbolSum += value; + return; + } + if (typeof key == "string") { + stringSum += value; + return; + } +}); + +print(intSum); +print(bigIntSum); +print(symbolSum); +print(stringSum) diff --git a/test/aottest/module/add.js b/test/aottest/module/add.js index 5f0eb24342fb91f32331fdc270725f733a8a9c2d..ca4531e1e42f1117a8fa67b2b591d3559b56cd70 100644 --- a/test/aottest/module/add.js +++ b/test/aottest/module/add.js @@ -16,4 +16,27 @@ export function add(a, b) { return a + b; -} \ No newline at end of file +} + +var a = "abcdwodehhhhjjjjkkkkkooodfjsar"; +var a1 = a.substring(4, 28); +var b = "efgh" +var c = "我的bbb"; +var d = "你的"; +var a2 = a + b; +var a3 = a + c; +var a4 = c + d; +var a5 = a1 + d; +var a6 = a2 + a5; +var a7 = d.toLowerCase() + a1.substring(1); +var a8 = a2.toUpperCase() + a3.substring(2); +var a9 = a7 + a8; +print(a1); +print(a2); +print(a3); +print(a4); +print(a5); +print(a6); +print(a7); +print(a8); +print(a9); \ No newline at end of file diff --git a/test/aottest/module/expect_output.txt b/test/aottest/module/expect_output.txt index 8fb3fad4e8c06455b08b64a7f8ebe0b94b72f08a..503decc3c46b99662cf879c7db3344463490e3fc 100644 --- a/test/aottest/module/expect_output.txt +++ b/test/aottest/module/expect_output.txt @@ -11,4 +11,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +wodehhhhjjjjkkkkkooodfjs +abcdwodehhhhjjjjkkkkkooodfjsarefgh +abcdwodehhhhjjjjkkkkkooodfjsar我的bbb +我的bbb你的 +wodehhhhjjjjkkkkkooodfjs你的 +abcdwodehhhhjjjjkkkkkooodfjsarefghwodehhhhjjjjkkkkkooodfjs你的 +你的odehhhhjjjjkkkkkooodfjs +ABCDWODEHHHHJJJJKKKKKOOODFJSAREFGHcdwodehhhhjjjjkkkkkooodfjsar我的bbb +你的odehhhhjjjjkkkkkooodfjsABCDWODEHHHHJJJJKKKKKOOODFJSAREFGHcdwodehhhhjjjjkkkkkooodfjsar我的bbb 3 diff --git a/test/aottest/new/new.ts b/test/aottest/new/new.ts index 092966b1ee2b5d71be660ce6ae64f9b725642822..3b2472709e7eaef6db2c60d44266b6d174d9f480 100644 --- a/test/aottest/new/new.ts +++ b/test/aottest/new/new.ts @@ -25,4 +25,19 @@ var o:Object = new Object("newobj"); print(o); var a:Array = new Array(10); -print(a.length); \ No newline at end of file +print(a.length); + +class TClass { + dd: number; + + div(v1: number[]): number { + return 1.0 / v1[0]; + } + constructor(p1: number[]) { + let d = this.div(p1); + this.dd = d / 1.0; + } +} + +let p1: number[] = [-10, 10, -10]; +new TClass(p1); diff --git a/test/aottest/numberspeculativeretype/BUILD.gn b/test/aottest/numberspeculativeretype/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..4d573ea96ec99626a5904a4b40ba1c5a593552bd --- /dev/null +++ b/test/aottest/numberspeculativeretype/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("numberspeculativeretype") { + deps = [] +} diff --git a/test/aottest/numberspeculativeretype/expect_output.txt b/test/aottest/numberspeculativeretype/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..290a22cc5cc55aa6c5afb9efd43250243bfb0ef0 --- /dev/null +++ b/test/aottest/numberspeculativeretype/expect_output.txt @@ -0,0 +1,14 @@ +# 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. + +compiler succ diff --git a/test/aottest/numberspeculativeretype/numberspeculativeretype.ts b/test/aottest/numberspeculativeretype/numberspeculativeretype.ts new file mode 100644 index 0000000000000000000000000000000000000000..49d0bbb0cdcba6f1c49558980436d3ffedde4c0b --- /dev/null +++ b/test/aottest/numberspeculativeretype/numberspeculativeretype.ts @@ -0,0 +1,29 @@ +/* + * 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. + */ + +// @ts-nocheck +declare function print(str:any):string; + +class A { + private b: null[] + private a: number = 1 + constructor() { + this.b.push(null); + } + f() { + this.b[this.a--] = null; + } +} +print("compiler succ") diff --git a/test/aottest/object/BUILD.gn b/test/aottest/object/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..ba75d3da98eeccc2a4cfb0308b16f5c6e030df43 --- /dev/null +++ b/test/aottest/object/BUILD.gn @@ -0,0 +1,26 @@ +# 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. + +group("object_test") { + testonly = true + + test_list = [ + "object_hasOwnProperty", + "object_toString", + ] + + deps = [] + foreach(test, test_list) { + deps += [ "${test}:${test}AotAction" ] + } +} diff --git a/test/aottest/object/object_hasOwnProperty/BUILD.gn b/test/aottest/object/object_hasOwnProperty/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..4ed133501ec30aff9a6a3bfc2626c057e0bee04e --- /dev/null +++ b/test/aottest/object/object_hasOwnProperty/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("object_hasOwnProperty") { + deps = [] +} diff --git a/test/aottest/object/object_hasOwnProperty/expect_output.txt b/test/aottest/object/object_hasOwnProperty/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..1365feb430f89ce170247389201e9749a4a6d277 --- /dev/null +++ b/test/aottest/object/object_hasOwnProperty/expect_output.txt @@ -0,0 +1,22 @@ +# 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. + +true +true +false +true +true +true +true +true +true diff --git a/test/aottest/object/object_hasOwnProperty/object_hasOwnProperty.ts b/test/aottest/object/object_hasOwnProperty/object_hasOwnProperty.ts new file mode 100644 index 0000000000000000000000000000000000000000..6b826d6c1d45f810b578d993a7981bc1d24d77f7 --- /dev/null +++ b/test/aottest/object/object_hasOwnProperty/object_hasOwnProperty.ts @@ -0,0 +1,35 @@ +/* + * 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. + */ + +declare function print(arg:any):string; +function Dog(name) { + this.name = name; +} + +const dog1 = new Dog('Gabby'); + +print(dog1.hasOwnProperty("name")); +print(Object.prototype.hasOwnProperty.call(dog1, "name")); +print(Object.prototype.hasOwnProperty.call(dog1, "no")); + +let s = "ss"; +print(Object.prototype.hasOwnProperty.call(s, "length")); +print(Object.prototype.hasOwnProperty.call(s, 0)); + +let a = [1, 2, 3] +print(Object.prototype.hasOwnProperty.call(a, "length")); +print(a.hasOwnProperty("length")); +print(a.hasOwnProperty("0")); +print(a.hasOwnProperty(0)); diff --git a/test/aottest/object/object_toString/BUILD.gn b/test/aottest/object/object_toString/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..d234fa6ce9423018efcd1c28171621a194ec6bea --- /dev/null +++ b/test/aottest/object/object_toString/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("object_toString") { + deps = [] +} diff --git a/test/aottest/object/object_toString/expect_output.txt b/test/aottest/object/object_toString/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..d83d2bdc84bb12a9c6bb3dbdee5913cd6e9b058c --- /dev/null +++ b/test/aottest/object/object_toString/expect_output.txt @@ -0,0 +1,27 @@ +# 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. + +Gabby +[object Object] +[object Object] +[object Number] +[object String] +ss +[object Date] +[object String] +[object Math] +[object Null] +[object Undefined] +[object Date] +[object myDate] +[object prototype polluted] diff --git a/test/aottest/object/object_toString/object_toString.ts b/test/aottest/object/object_toString/object_toString.ts new file mode 100644 index 0000000000000000000000000000000000000000..682fb804f26d69ea3a7a385a37cb20b768bd117b --- /dev/null +++ b/test/aottest/object/object_toString/object_toString.ts @@ -0,0 +1,56 @@ +/* + * 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. + */ + +declare function print(arg:any):string; +function Dog(name) { + this.name = name; +} + +const dog1 = new Dog('Gabby'); + +Dog.prototype.toString = function dogToString() { + return `${this.name}`; +}; + +print(dog1.toString()); +print(Object.prototype.toString.call(dog1)); + +let o = new Object(); +print(o.toString()) + +let n = new Number(1); +print(Object.prototype.toString.call(n)); + +let s = "ss"; +print(Object.prototype.toString.call(s)); +print(s.toString()); + +print(Object.prototype.toString.call(new Date())); +print(Object.prototype.toString.call(new String())); +// Math has its Symbol.toStringTag +print(Object.prototype.toString.call(Math)); + + +print(Object.prototype.toString.call(null)); +print(Object.prototype.toString.call()); + +const myDate = new Date(); +print(Object.prototype.toString.call(myDate)); + +myDate[Symbol.toStringTag] = "myDate"; +print(Object.prototype.toString.call(myDate)); + +Date.prototype[Symbol.toStringTag] = "prototype polluted"; +print(Object.prototype.toString.call(new Date())); diff --git a/test/aottest/operations_stub_test/BUILD.gn b/test/aottest/operations_stub_test/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..109c952e6ae1985f4a9f0c0dbd211144c5514677 --- /dev/null +++ b/test/aottest/operations_stub_test/BUILD.gn @@ -0,0 +1,19 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("operations_stub_test") { + deps = [] + is_enable_pgo = true +} diff --git a/test/aottest/operations_stub_test/expect_output.txt b/test/aottest/operations_stub_test/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..ce415d321ab7b920f7a85495b54d7f2a762720f0 --- /dev/null +++ b/test/aottest/operations_stub_test/expect_output.txt @@ -0,0 +1,14 @@ +# 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. + +true diff --git a/test/aottest/operations_stub_test/operations_stub_test.ts b/test/aottest/operations_stub_test/operations_stub_test.ts new file mode 100644 index 0000000000000000000000000000000000000000..e3e52637f6375d4f0e707acb822aadfa47f89a91 --- /dev/null +++ b/test/aottest/operations_stub_test/operations_stub_test.ts @@ -0,0 +1,18 @@ +/* + * 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. + */ + +const CLICK_TIME : number = 500 +let nowTime = 1695261311458 +print(nowTime - CLICK_TIME > 500) \ No newline at end of file diff --git a/test/aottest/operations_stub_test/pgo_expect_output.txt b/test/aottest/operations_stub_test/pgo_expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..ce415d321ab7b920f7a85495b54d7f2a762720f0 --- /dev/null +++ b/test/aottest/operations_stub_test/pgo_expect_output.txt @@ -0,0 +1,14 @@ +# 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. + +true diff --git a/test/aottest/optimized_call/BUILD.gn b/test/aottest/optimized_call/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..dab09e4c1182d7e29ae408ddd7507eaa9a87e412 --- /dev/null +++ b/test/aottest/optimized_call/BUILD.gn @@ -0,0 +1,19 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("optimized_call") { + deps = [] + userDefinedMethodsInModule = true +} diff --git a/test/aottest/optimized_call/expect_output.txt b/test/aottest/optimized_call/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..11e347853e4da9832b8fd94bc0f667d09cb9a74b --- /dev/null +++ b/test/aottest/optimized_call/expect_output.txt @@ -0,0 +1,14 @@ +# 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. + +2 diff --git a/test/aottest/optimized_call/optimized_call.ts b/test/aottest/optimized_call/optimized_call.ts new file mode 100644 index 0000000000000000000000000000000000000000..ac75a1d266b24eec76816c56db0fde72ad6649a8 --- /dev/null +++ b/test/aottest/optimized_call/optimized_call.ts @@ -0,0 +1,28 @@ +/* + * 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. + */ +declare function print(arg:any):void; + +function sum(a:number, b:number, c:number) { + return a + b + c; +} + +const object = { + apply: function(a:number, b:number, c:number) { + return 2; + } +}; + +const proxy = new Proxy(sum, object); +print(proxy(1, 2, 3)); \ No newline at end of file diff --git a/test/aottest/pgo_call/BUILD.gn b/test/aottest/pgo_call/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..b5727f5472763f1812cb721b59d695fefc710443 --- /dev/null +++ b/test/aottest/pgo_call/BUILD.gn @@ -0,0 +1,24 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("pgo_call") { + deps = [] + is_only_typed_path = true + is_enable_pgo = true + is_enable_opt_inlining = true + is_enable_inline_trace = true + is_enable_trace_deopt = true + log_option = " --log-info=trace" +} diff --git a/test/aottest/pgo_call/expect_output.txt b/test/aottest/pgo_call/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..6b53ff5b702047ba19c849c2221e973d3d345bbc --- /dev/null +++ b/test/aottest/pgo_call/expect_output.txt @@ -0,0 +1,32 @@ +# 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. + +[trace] aot inline function name: callFoo1@pgo_call caller function name: func_main_0@pgo_call +[trace] aot inline function name: foo@pgo_call caller function name: callFoo1@pgo_call +[trace] aot inline function name: callFoo1@pgo_call caller function name: func_main_0@pgo_call +[trace] aot inline function name: foo@pgo_call caller function name: callFoo1@pgo_call +[trace] aot inline function name: callFoo2@pgo_call caller function name: func_main_0@pgo_call +[trace] aot inline function name: foo@pgo_call caller function name: callFoo2@pgo_call +[trace] aot inline function name: callFoo2@pgo_call caller function name: func_main_0@pgo_call +[trace] aot inline function name: foo@pgo_call caller function name: callFoo2@pgo_call +[trace] aot inline function name: callFoo2@pgo_call caller function name: func_main_0@pgo_call +[trace] aot inline function name: bar@pgo_call caller function name: callFoo2@pgo_call +[trace] aot inline function name: callFoo2@pgo_call caller function name: func_main_0@pgo_call +[trace] aot inline function name: bar@pgo_call caller function name: callFoo2@pgo_call +[trace] aot inline function name: callFoo3@pgo_call caller function name: func_main_0@pgo_call +[trace] aot inline function name: callFoo2@pgo_call caller function name: func_main_0@pgo_call +[trace] aot inline function name: bar@pgo_call caller function name: callFoo2@pgo_call +[trace] aot inline function name: callFoo2@pgo_call caller function name: func_main_0@pgo_call +[trace] aot inline function name: foo@pgo_call caller function name: callFoo2@pgo_call +[trace] aot inline function name: foo@pgo_call caller function name: callFoo1@pgo_call +a diff --git a/test/aottest/pgo_call/pgo_call.ts b/test/aottest/pgo_call/pgo_call.ts new file mode 100644 index 0000000000000000000000000000000000000000..79710a8e714ecdca3435a34feaef6d3fb1abf13f --- /dev/null +++ b/test/aottest/pgo_call/pgo_call.ts @@ -0,0 +1,64 @@ +/* + * 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. + */ + +declare function print(arg:any):string; + +function foo(a: number): number { + return a + 1; +} + +function bar(a: number): number { + return a / 2; +} + +function mol(): string { + return "a"; +} + +function callFoo1(f, a: number): number { + return f(a); +} + +function callFoo2(f: (n: number) => number, a: number): number { + return f(a); +} + +function callFoo3(f: (n: number) => number, a: number): number { + return f(a); +} + +for (let i = 0; i < 2; i++) { + callFoo1(foo, i); +} + +for (let i = 0; i < 2; i++) { + callFoo2(foo, i); +} + +for (let i = 0; i < 2; i++) { + callFoo2(bar, i); +} + +let ret = callFoo3(mol, 1); + +callFoo2(bar, 1); // bot callFoo2 and bar inlined +callFoo2(foo, 1); // bot callFoo2 and foo inlined + +// inline_max_count_overflow +callFoo2(bar, 1); // neither callFoo2 nor bar will be inlined +callFoo2(foo, 1); // neither callFoo2 nor foo will be inlined + +callFoo1(foo, 1); // callFoo1 will not be inlined but foo will be inlined in callFoo1 +print(ret) \ No newline at end of file diff --git a/test/aottest/pgo_call/pgo_expect_output.txt b/test/aottest/pgo_call/pgo_expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..5e18e56e1a4636dfa96133251be03e7e7842b4c0 --- /dev/null +++ b/test/aottest/pgo_call/pgo_expect_output.txt @@ -0,0 +1,14 @@ +# 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. + +a diff --git a/test/aottest/range_guard/BUILD.gn b/test/aottest/range_guard/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..ad8e1028838a00bcc95423c879073ca861cbb41a --- /dev/null +++ b/test/aottest/range_guard/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("range_guard") { + deps = [] +} diff --git a/test/aottest/range_guard/expected_output.txt b/test/aottest/range_guard/expected_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..ba5a3651520d34c057d60bccc234bcb07688ebc3 --- /dev/null +++ b/test/aottest/range_guard/expected_output.txt @@ -0,0 +1,15 @@ +# 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. + +8 +8 diff --git a/test/aottest/range_guard/range_guard.ts b/test/aottest/range_guard/range_guard.ts new file mode 100644 index 0000000000000000000000000000000000000000..582344d2dc505c52ee9705fc65053d6e1cd8ca8c --- /dev/null +++ b/test/aottest/range_guard/range_guard.ts @@ -0,0 +1,101 @@ +/* + * 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. + */ +declare function print(arg:any):void; + +function testcase1() { + let testArray: Int32Array = new Int32Array([1, 2, 3]); + let len: number = testArray.length; + let a: number = testArray[0]; + let res: number = 5 + len; + print(res) +} + +function testcase2() { + let testArray: Int32Array = new Int32Array([1, 2, 3]); + let a: number = testArray[0]; + let len: number = testArray.length - 1; + let res: number = 6 + len; + print(res) +} + +function testcase3() { + let testArray: Int32Array = new Int32Array([1, 2, 3]); + let len: number = testArray.length; + let sum: number = 0; + for(let i = 0; i < len; i++) { + print(i + 1); + sum += testArray[i]; + } +} + +function testcase4() { + let testArray: Int32Array = new Int32Array([1, 2, 3]); + let len: number = testArray.length - 1; + let sum: number = 0; + for(let i = 0; i < len; i++) { + sum += testArray[i]; + print(i + 1); + } +} + +function testcase5() { + let testArray: number[] = [1, 2, 3]; + let a: number = testArray[0]; + let res: number = 5 + testArray.length; + print(res); +} + +function testcase6() { + let testArray: number[] = [1, 2, 3]; + let len: number = testArray.length - 1; + let sum: number = 0; + let i: number = 0; + for(let i = 0; i < len; i++) { + sum += testArray[i]; + print(i + 1); + } +} + +//mod CheckRightIsZero +function testcase7() { + const a: number= 20; + let b: number = 5 % a;// not gen CheckRightIsZero + let c: number = 5 % b;// gen CheckRightIsZero +} + +//mul_with_overcheck +function testcase8(a:number, x:number) { + let z = a >>> 1; + let b: number = z % 10;//range[0, 4095] + + let c: number = 10 * b;//not gen mul_with_overcheck, range[0,262143] + let d: number = c * b;//not gen mul_with_overcheck, range[0,1073741823] + let e: number = d * c;//gen mul_with_overcheck + + x = x >>> 1; + let h: number = x * x;//gen mul_with_overcheck +} + +testcase1(); // 8 +testcase2(); // 8 + +testcase3(); +testcase4(); + +testcase5(); +testcase6(); + +testcase7(); +testcase8(4, 107341001);//deopt \ No newline at end of file diff --git a/test/aottest/rodata/BUILD.gn b/test/aottest/rodata/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..67f2ed79a08bc0c2ca248f87597c84e7de2e6cd9 --- /dev/null +++ b/test/aottest/rodata/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("rodata") { + deps = [] +} diff --git a/test/aottest/rodata/expect_output.txt b/test/aottest/rodata/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..6d900641bac464a6b1faedd8883c48f7130d0004 --- /dev/null +++ b/test/aottest/rodata/expect_output.txt @@ -0,0 +1,15 @@ +# 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. + +1.5 +1.5 diff --git a/test/aottest/rodata/rodata.ts b/test/aottest/rodata/rodata.ts new file mode 100644 index 0000000000000000000000000000000000000000..4185ec50f59a605ca442cbcf94d76e06deb18b3b --- /dev/null +++ b/test/aottest/rodata/rodata.ts @@ -0,0 +1,25 @@ +/* + * 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. + */ + +declare function print(arg: any): string; +function foo(x: number, p: Function) { + let f3: number = 1.5 + p(f3) + if (x == 1) { + f3 += 1.1 + } + return f3 +} +print(foo(2, print)) diff --git a/test/aottest/set/BUILD.gn b/test/aottest/set/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..05355d2b1bc62e9133f468ca4166422d93b7677a --- /dev/null +++ b/test/aottest/set/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("set") { + deps = [] +} diff --git a/test/aottest/set/expect_output.txt b/test/aottest/set/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..10b5a0d3bb968275bc00ef0fddb17c2d70aa2a36 --- /dev/null +++ b/test/aottest/set/expect_output.txt @@ -0,0 +1,26 @@ +# 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. + +m[foo] = foo +m[bar] = bar +m[undefined] = undefined +m[foo] = foo +2 +m[bar] = bar +1 +m[undefined] = undefined +0 +499500 +900719925474104050 +1000 +1000 diff --git a/test/aottest/set/set.ts b/test/aottest/set/set.ts new file mode 100644 index 0000000000000000000000000000000000000000..d164cacbecfba5e65f304aaa472dc3ff29ee5ced --- /dev/null +++ b/test/aottest/set/set.ts @@ -0,0 +1,70 @@ +/* + * 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. + */ +function logMapElements(value, key, set) { + print(`m[${key}] = ${value}`); +} + +new Set(['foo', 'bar', undefined]).forEach(logMapElements); + + +function logMapElements1(value, key, set) { + print(`m[${key}] = ${value}`); + set.delete(key); + print(set.size); +} + +new Set(['foo', 'bar', undefined]).forEach(logMapElements1); + +let mySet = new Set(); +for (let i = 0; i < 1000; i++) { + mySet.add(i); + mySet.add("key" + i); + mySet.add(Symbol("foo")); +} + +const hugeString = BigInt("9007199254740991"); +for (let i = 0; i < 100; i++) { + mySet.add(hugeString + BigInt(i)); +} + +var intSum = 0; +var symbolCnt = 0; +var bigIntSum = BigInt(""); +var stringCnt = 0; +mySet.forEach((value, key, set) => { + if (typeof key == "bigint") { + bigIntSum += value; + return; + } + if (typeof key == "number") { + intSum += value; + return; + } + if (typeof key == "symbol") { + symbolCnt += 1; + return; + } + if (typeof key == "string") { + stringCnt += 1; + return; + } +}); + + +print(intSum); +print(bigIntSum); +print(symbolCnt); +print(stringCnt); + diff --git a/test/aottest/statesplit/BUILD.gn b/test/aottest/statesplit/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..2887bfe99c1e71343e1e72e08fceb4296603aa18 --- /dev/null +++ b/test/aottest/statesplit/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_js_test_action("statesplit") { + deps = [] +} diff --git a/test/aottest/statesplit/expect_output.txt b/test/aottest/statesplit/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..290a22cc5cc55aa6c5afb9efd43250243bfb0ef0 --- /dev/null +++ b/test/aottest/statesplit/expect_output.txt @@ -0,0 +1,14 @@ +# 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. + +compiler succ diff --git a/test/aottest/statesplit/statesplit.js b/test/aottest/statesplit/statesplit.js new file mode 100644 index 0000000000000000000000000000000000000000..245669d671beb56e131bfdb409c49575454221e4 --- /dev/null +++ b/test/aottest/statesplit/statesplit.js @@ -0,0 +1,30 @@ +/* + * 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. + */ + +function f20(a22) { + for(let v39 = 0; v39 < 5; v39++) { + function f44() { + const o45 = {} + o45.c = f44; + return o45; + } + try { + ~2147483647; + return v39 + } catch (e55) {} + } + return a22 +} +print("compiler succ") diff --git a/test/aottest/stownbyindex/expect_output.txt b/test/aottest/stownbyindex/expect_output.txt index e111f15c94810f1666289c077d8a5eaf2a321811..1ba5892768f75d115d584cc57e8a8d0b2c12d890 100644 --- a/test/aottest/stownbyindex/expect_output.txt +++ b/test/aottest/stownbyindex/expect_output.txt @@ -19,3 +19,19 @@ yes no 4 5 +1 +2 +3 +4 +5 +99.99 +ark +-88.48 +brk +-1024 +crk +6666 +drk +in 1 second,now,now,1 second ago +in 1 minute,this minute,this minute,1 minute ago +2 diff --git a/test/aottest/stownbyindex/stownbyindex.ts b/test/aottest/stownbyindex/stownbyindex.ts index 31a33a114055d44e4ac3d7a169195d52ce9c0020..1fb71ae88212c1b833f55fcb1b5fe8ffa8d12c60 100644 --- a/test/aottest/stownbyindex/stownbyindex.ts +++ b/test/aottest/stownbyindex/stownbyindex.ts @@ -27,4 +27,86 @@ var b = { print(b[1][0]); print(b[1][1]); print(b["100"][0]); -print(b["100"][1]); \ No newline at end of file +print(b["100"][1]); + +class Attrs { + constructor(a : number, b : number, c : number) { + this.x = a; + this.y = b; + this.z = c; + } + x : number; + y : number; + z : number; +} +function test() : void { + for (let j = 0; j < 5; ++j) { + var _attrs : Attrs[] = [ + new Attrs(1, 1, 1), + new Attrs(2, 2, 2), + new Attrs(3, 3, 3), + new Attrs(4, 4, 4), + new Attrs(5, 5, 5), + ]; + print(_attrs[j].x); + } +} +test(); + +const tests = [ + [ + 99.99, + "ark" + ], + [ + -88.48, + "brk" + ], + [ + -1024, + "crk" + ], + [ + 6666, + "drk" + ], +]; + +for (const [number, strData] of tests) { + print(number); + print(strData); +} + +const units = [ + "second", + "minute", +]; + +const exceptions = { + "minute": { + "-1": "1 minute ago", + '0': 'this minute', + "1": "in 1 minute", + }, + "second": { + "-1": "1 second ago", + "0": "now", + "1": "in 1 second", + }, +}; + + +for (const unit of units) { + const expected = unit in exceptions + ? [exceptions[unit]["1"], exceptions[unit]["0"], exceptions[unit]["0"], exceptions[unit]["-1"]] + : [`in 1 ${unit}`, `in 0 ${unit}s`, `0 ${unit}s ago`, `1 ${unit} ago`]; + + print(expected); +} + +Object.defineProperty(Array.prototype, '0', { + value: 42, +}); +let arr = [, 2, 3]; +arr.pop(); +print(arr[1]); \ No newline at end of file diff --git a/test/aottest/stownbyname/expect_output.txt b/test/aottest/stownbyname/expect_output.txt index b30e0ffbc938675fc7632ae4ea2ac9c379d0fe43..991bae6db4ec1246c6bf6494a873bd5141e27f78 100644 --- a/test/aottest/stownbyname/expect_output.txt +++ b/test/aottest/stownbyname/expect_output.txt @@ -13,3 +13,5 @@ a b +100 +helloworld diff --git a/test/aottest/stownbyname/stownbyname.ts b/test/aottest/stownbyname/stownbyname.ts index 56e64c25704956f2e98a7ebccad391a25eaf0636..6bf66c87473580c9d3a3eeb2eb9050a11df20557 100644 --- a/test/aottest/stownbyname/stownbyname.ts +++ b/test/aottest/stownbyname/stownbyname.ts @@ -18,4 +18,37 @@ var foo = { bar: [ "a", "b" ] }; print(foo.bar[0]); -print(foo.bar[1]); \ No newline at end of file +print(foo.bar[1]); + +interface objInterface { + [key: string]: any +} +let obj:objInterface = {}; +obj.a = 100; +obj.b = "helloworld"; +print(obj.a) +print(obj.b); + +function ArkFunc(depth: number, tag: string): { + array: number[], + string: string +} | { + left: object, + right: object +} { + if (depth == 0) { + return { + array: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + string: 'Ark ' + tag + ' Departure!' + }; + } else { + return { + left: ArkFunc(depth - 1, tag), + right: ArkFunc(depth - 1, tag) + }; + } +} + +var arkDepth: number = 5; +var arkTag: number = 18; +var arkResult = ArkFunc(arkDepth, String(arkTag)); \ No newline at end of file diff --git a/test/aottest/string/BUILD.gn b/test/aottest/string/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..c1c794f018fcffa34324da309887dc65a046f9dc --- /dev/null +++ b/test/aottest/string/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("string") { + deps = [] +} diff --git a/test/aottest/string/expect_output.txt b/test/aottest/string/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..bcfebe5c784fc58723c90ec2887bafc63210ec63 --- /dev/null +++ b/test/aottest/string/expect_output.txt @@ -0,0 +1,21 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +52 +4 +5 +52 +5678901234567890 +5 +8 +57 diff --git a/test/aottest/string/string.ts b/test/aottest/string/string.ts new file mode 100644 index 0000000000000000000000000000000000000000..1f7a73d78f56c17cff6110234866a8420dae36f9 --- /dev/null +++ b/test/aottest/string/string.ts @@ -0,0 +1,52 @@ +/* + * 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. + */ +declare function print(arg:any, arg1?:any):string; + +class cpu { + public mode: number = 1; + constructor() { + this.mode = 4; + } + public static add(a: string):number { + let i : number = 3; + let b : string = a.substring(3); + print(b); + let d : number = b.charCodeAt(4); + print(a[i]); + print(b[i]); + return d; + } +} + +function foo(a: string):number { + let b : string = a.substring(1); + let d : number = b.charCodeAt(1); + return d; +} + +function foo1(a: string):number { + let i : number = 2; + let b : string = a.substring(1); + let d : number = b.charCodeAt(1); + print(a[i]); + print(b[i]); + return d; +} +var a : string = "12345678901234567890" +var b : string = a.substring(1); +print(foo(b)); +print(foo1(b)); +print(cpu.add(b)); + diff --git a/test/aottest/string_equal/BUILD.gn b/test/aottest/string_equal/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..660869eddd0a79dfd7c3c6c18c8d9b364b401418 --- /dev/null +++ b/test/aottest/string_equal/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("string_equal") { + deps = [] +} diff --git a/test/aottest/string_equal/expect_output.txt b/test/aottest/string_equal/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..adfb2e7ee22e6c3c37526fdd21b68c12fc7987be --- /dev/null +++ b/test/aottest/string_equal/expect_output.txt @@ -0,0 +1,19 @@ +# 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. + +true +true +true +2 +true +false diff --git a/test/aottest/string_equal/string_equal.ts b/test/aottest/string_equal/string_equal.ts new file mode 100644 index 0000000000000000000000000000000000000000..04590ad5dcfc4a54349fab9c00becc0deed2ead8 --- /dev/null +++ b/test/aottest/string_equal/string_equal.ts @@ -0,0 +1,47 @@ +/* + * 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. + */ +declare function print(arg:any, arg1?:any):string; + +let str:string = "1234567890上下左右中"; + +// two const +print("123" == "123"); +print(str == "1234567890上下左右中"); +print("1" == "1") + +// one const +let ans = 0; +for (let i = 0; i<15; ++i) { + let m:string = str[i]; + if (m == "1" || m == "上" || m == "国") { + ans += 1; + } +} +print(ans); + +// no const +function foo(flag) { + let str = "12"; + if (flag) { + return str[0]; + } else { + return str[1]; + } +} +let left:string = foo(true); +let right1:string = foo(true); +let right2:string = foo(false); +print(left == right1); +print(left == right2); diff --git a/test/aottest/try/BUILD.gn b/test/aottest/try/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..eb1a83f0cdd2bf2d2fa4a7b6a928c7b29b7b426b --- /dev/null +++ b/test/aottest/try/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_js_test_action("try") { + deps = [] +} diff --git a/test/aottest/try/expect_output.txt b/test/aottest/try/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..290a22cc5cc55aa6c5afb9efd43250243bfb0ef0 --- /dev/null +++ b/test/aottest/try/expect_output.txt @@ -0,0 +1,14 @@ +# 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. + +compiler succ diff --git a/test/aottest/try/try.js b/test/aottest/try/try.js new file mode 100644 index 0000000000000000000000000000000000000000..6415fa176960d47427ea9d75a1e65ccaceb41ee3 --- /dev/null +++ b/test/aottest/try/try.js @@ -0,0 +1,36 @@ +/* + * 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. + */ + +function foo() { + try { + const a = {}; + } catch (err) { + for (let i = 0; i < 5; i++) {} + } +} + +function bar(a) { + try { + let i = 0; + do { + a = -5.0; + i++; + } while (i < 3) + } catch (e) { + try { a.x } catch (e2) {} + } +} + +print("compiler succ") diff --git a/test/aottest/try_catch_finally/try_catch_finally.ts b/test/aottest/try_catch_finally/try_catch_finally.ts index 92b0442f130ad9ae7482b67926eab90a6cd91d4f..c9a549f42015cc01560133163293e1193e4b85ae 100644 --- a/test/aottest/try_catch_finally/try_catch_finally.ts +++ b/test/aottest/try_catch_finally/try_catch_finally.ts @@ -111,3 +111,15 @@ for (x in mycars) { } print(fin6) print(c6) + +class A { + constructor(a:any|number) { + try { + a = -1234.5678; + function f():void {} + f(f, a); + } finally { + Symbol[a] = 1.0 + } + } +} \ No newline at end of file diff --git a/test/aottest/tryldglobalbyname/BUILD.gn b/test/aottest/tryldglobalbyname/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..192c96bd3d26476983ca1f81513d1afbe2e5e757 --- /dev/null +++ b/test/aottest/tryldglobalbyname/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_js_test_action("tryldglobalbyname") { + deps = [] +} diff --git a/test/aottest/tryldglobalbyname/expect_output.txt b/test/aottest/tryldglobalbyname/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..c54610e08d0ab6ab7edcff1610ef10057f5412f2 --- /dev/null +++ b/test/aottest/tryldglobalbyname/expect_output.txt @@ -0,0 +1,15 @@ +# 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. + +-1.1 +1.1 diff --git a/test/aottest/tryldglobalbyname/tryldglobalbyname.js b/test/aottest/tryldglobalbyname/tryldglobalbyname.js new file mode 100644 index 0000000000000000000000000000000000000000..8d19a0568949d2a4046dcd6452ec85649d9e2e20 --- /dev/null +++ b/test/aottest/tryldglobalbyname/tryldglobalbyname.js @@ -0,0 +1,23 @@ +/* + * 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. + */ + +function getPatternParts(positive) { + var n = positive ? 1.1 : -1.1; + return n; +} +let a = getPatternParts(false) +print(a) +let b = getPatternParts(true) +print(b) \ No newline at end of file diff --git a/test/aottest/ts_hclass_generator/BUILD.gn b/test/aottest/ts_hclass_generator/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..55c85957e4208c2ddfa6b8a6eaa6c6cd86ec05d1 --- /dev/null +++ b/test/aottest/ts_hclass_generator/BUILD.gn @@ -0,0 +1,17 @@ +# 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. +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("ts_hclass_generator") { + deps = [] +} diff --git a/test/aottest/ts_hclass_generator/expect_output.txt b/test/aottest/ts_hclass_generator/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..5bad3398446f62565941e3bea229d5255e2eb88e --- /dev/null +++ b/test/aottest/ts_hclass_generator/expect_output.txt @@ -0,0 +1,17 @@ +# 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. + +createSubscriber +subscribe +unSubscribe +1 diff --git a/test/aottest/ts_hclass_generator/ts_hclass_generator.ts b/test/aottest/ts_hclass_generator/ts_hclass_generator.ts new file mode 100644 index 0000000000000000000000000000000000000000..4c9ae6c54bb5e307ea009346fbe6ed145b0ca17d --- /dev/null +++ b/test/aottest/ts_hclass_generator/ts_hclass_generator.ts @@ -0,0 +1,36 @@ +/* + * 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. + */ +class SubscribeEvent { + private static subscriber = null; + private static callback?:(event:string) => void; + + static createSubscriber(subscribeInfo, callback:(event:string) => void) { + print("createSubscriber"); + } + + static subscribe(subscriber) { + print("subscribe"); + } + + static unSubscribe() { + print("unSubscribe"); + } +} +new SubscribeEvent(); +let a : (event:string) => void = function() : void{}; +SubscribeEvent.createSubscriber("", a); +SubscribeEvent.subscribe(""); +SubscribeEvent.unSubscribe(); +print(1); diff --git a/test/aottest/ts_inline/expect_output.txt b/test/aottest/ts_inline/expect_output.txt index 5cee567797728a11af6254eebbfa4744606a62cb..813eaf57575a809eff18fc1bebfe1c032798cb06 100644 --- a/test/aottest/ts_inline/expect_output.txt +++ b/test/aottest/ts_inline/expect_output.txt @@ -18,4 +18,5 @@ [trace] aot inline function name: bar@ts_inline caller function name: func_main_0@ts_inline 56 56 +[trace] aot inline function name: calculate@ts_inline caller function name: func_main_0@ts_inline 16 diff --git a/test/aottest/ts_inline_accessor/BUILD.gn b/test/aottest/ts_inline_accessor/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..dce1be869e1f027e9533f7a8126970b5f0d9f477 --- /dev/null +++ b/test/aottest/ts_inline_accessor/BUILD.gn @@ -0,0 +1,21 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("ts_inline_accessor") { + deps = [] + is_enable_opt_inlining = true + is_enable_inline_trace = true + log_option = " --log-info=trace" +} diff --git a/test/aottest/ts_inline_accessor/expect_output.txt b/test/aottest/ts_inline_accessor/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..191f002d68ee16d4a464476cb7c2d7986961f787 --- /dev/null +++ b/test/aottest/ts_inline_accessor/expect_output.txt @@ -0,0 +1,22 @@ +# 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. + +[trace] aot inline function name: GetName@ts_inline_accessor caller function name: func_main_0@ts_inline_accessor +xiaoming +[trace] aot inline function name: SetName@ts_inline_accessor caller function name: func_main_0@ts_inline_accessor +[trace] aot inline function name: GetName@ts_inline_accessor caller function name: func_main_0@ts_inline_accessor +xiaohong +[trace] aot inline function name: SetAge@ts_inline_accessor caller function name: func_main_0@ts_inline_accessor +foo +[trace] aot inline function name: GetAge@ts_inline_accessor caller function name: func_main_0@ts_inline_accessor +20 diff --git a/test/aottest/ts_inline_accessor/ts_inline_accessor.ts b/test/aottest/ts_inline_accessor/ts_inline_accessor.ts new file mode 100644 index 0000000000000000000000000000000000000000..95059966232de95c8c28dc8175f903fd82a8579e --- /dev/null +++ b/test/aottest/ts_inline_accessor/ts_inline_accessor.ts @@ -0,0 +1,54 @@ +/* + * 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. + */ + +declare function print(arg:any):string; + +class Person { + name: string; + age: number; + + constructor(name: string, age: number) { + this.name = name; + this.age = age; + } + + get GetName() { + return this.name; + } + set SetName(str: string ) { + this.name = str; + } + + get GetAge() { + return this.age; + } + + set SetAge(age: number) { + this.age = age; + this.foo(); + } + + foo() { + print("foo"); + } + +} + +let p = new Person("xiaoming", 18); +print(p.GetName) +p.SetName = "xiaohong"; +print(p.GetName); +p.SetAge = 20; +print(p.GetAge); diff --git a/test/aottest/ts_inline_accessor_deopt/BUILD.gn b/test/aottest/ts_inline_accessor_deopt/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..1a3efc183b2c1d8678bd0c2714d401b912063db1 --- /dev/null +++ b/test/aottest/ts_inline_accessor_deopt/BUILD.gn @@ -0,0 +1,21 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("ts_inline_accessor_deopt") { + deps = [] + is_enable_opt_inlining = true + is_enable_inline_trace = true + log_option = " --log-info=trace" +} diff --git a/test/aottest/ts_inline_accessor_deopt/expect_output.txt b/test/aottest/ts_inline_accessor_deopt/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..7132bb3da804b0888d1a7884572a74bc13f34d1f --- /dev/null +++ b/test/aottest/ts_inline_accessor_deopt/expect_output.txt @@ -0,0 +1,20 @@ +# 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. + +[trace] aot inline function name: GetName@ts_inline_accessor_deopt caller function name: func_main_0@ts_inline_accessor_deopt +xiaoming +[trace] aot inline function name: SetName@ts_inline_accessor_deopt caller function name: func_main_0@ts_inline_accessor_deopt +[trace] aot inline function name: GetName@ts_inline_accessor_deopt caller function name: func_main_0@ts_inline_accessor_deopt +xiaohong +foo +bar diff --git a/test/aottest/ts_inline_accessor_deopt/ts_inline_accessor_deopt.ts b/test/aottest/ts_inline_accessor_deopt/ts_inline_accessor_deopt.ts new file mode 100644 index 0000000000000000000000000000000000000000..764d9f89d513322eb31a38ccd9dabea22e802386 --- /dev/null +++ b/test/aottest/ts_inline_accessor_deopt/ts_inline_accessor_deopt.ts @@ -0,0 +1,46 @@ +/* + * 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. + */ + +declare function print(arg:any):string; + +class Person { + name: string; + + constructor(name: string) { + this.name = name; + } + + get GetName() { + return this.name; + } + set SetName(str: string ) { + this.name = str; + } + +} + +function bar() { + print("bar"); +} + +let p = new Person("xiaoming"); +print(p.GetName) +p.SetName = "xiaohong"; +print(p.GetName); +let foo = "foo"; +let prototype = Person.prototype; +Object.defineProperty(prototype, "GetName", {value:foo}); +print(p.GetName); +bar() \ No newline at end of file diff --git a/test/aottest/ts_inline_accessor_extends/BUILD.gn b/test/aottest/ts_inline_accessor_extends/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..9a0a2789256c7321981b2d58fb1fbc5ab13da975 --- /dev/null +++ b/test/aottest/ts_inline_accessor_extends/BUILD.gn @@ -0,0 +1,21 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("ts_inline_accessor_extends") { + deps = [] + is_enable_opt_inlining = true + is_enable_inline_trace = true + log_option = " --log-info=trace" +} diff --git a/test/aottest/ts_inline_accessor_extends/expect_output.txt b/test/aottest/ts_inline_accessor_extends/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..ff0701b0dac55773582c7f1cd4cb12043c31c88c --- /dev/null +++ b/test/aottest/ts_inline_accessor_extends/expect_output.txt @@ -0,0 +1,15 @@ +# 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. + +[trace] aot inline function name: foo@ts_inline_accessor_extends caller function name: func_main_0@ts_inline_accessor_extends +B.Getter diff --git a/test/aottest/ts_inline_accessor_extends/ts_inline_accessor_extends.ts b/test/aottest/ts_inline_accessor_extends/ts_inline_accessor_extends.ts new file mode 100644 index 0000000000000000000000000000000000000000..c50fab9e6744c0e029dc4a77f18690cd8738445e --- /dev/null +++ b/test/aottest/ts_inline_accessor_extends/ts_inline_accessor_extends.ts @@ -0,0 +1,42 @@ +/* + * 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. + */ + +declare function print(arg:any):string; + +class A { + get Getter() { + print("A.Getter") + } +} + +class B extends A { + get Getter() { + print("B.Getter") + } +} + +class C { + obj: A + constructor(obj: A) { + this.obj = obj + } + + foo() { + this.obj.Getter + } +} +let b = new B() +let c = new C(b) +c.foo(); diff --git a/test/aottest/ts_inline_accessor_same_name/BUILD.gn b/test/aottest/ts_inline_accessor_same_name/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..12d9d42112ff0f571a9cf6f9468a3c492faea94b --- /dev/null +++ b/test/aottest/ts_inline_accessor_same_name/BUILD.gn @@ -0,0 +1,21 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("ts_inline_accessor_same_name") { + deps = [] + is_enable_opt_inlining = true + is_enable_inline_trace = true + log_option = " --log-info=trace" +} diff --git a/test/aottest/ts_inline_accessor_same_name/expect_output.txt b/test/aottest/ts_inline_accessor_same_name/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..c8e56cb6279b4c9df08ceb9e5fdb663eb04a7c10 --- /dev/null +++ b/test/aottest/ts_inline_accessor_same_name/expect_output.txt @@ -0,0 +1,18 @@ +# 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. + +[trace] aot inline function name: #5592482324218508596#Name@ts_inline_accessor_same_name caller function name: func_main_0@ts_inline_accessor_same_name +xiaoming +[trace] aot inline function name: Name@ts_inline_accessor_same_name caller function name: func_main_0@ts_inline_accessor_same_name +[trace] aot inline function name: #5592482324218508596#Name@ts_inline_accessor_same_name caller function name: func_main_0@ts_inline_accessor_same_name +xiaohong diff --git a/test/aottest/ts_inline_accessor_same_name/ts_inline_accessor_same_name.ts b/test/aottest/ts_inline_accessor_same_name/ts_inline_accessor_same_name.ts new file mode 100644 index 0000000000000000000000000000000000000000..4726c2a8a1e876b104bdf5e0577b0ef130c1c25b --- /dev/null +++ b/test/aottest/ts_inline_accessor_same_name/ts_inline_accessor_same_name.ts @@ -0,0 +1,38 @@ +/* + * 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. + */ + +declare function print(arg:any):string; + +class Person { + name: string; + + constructor(name: string) { + this.name = name; + } + + set Name(str: string ) { + this.name = str; + } + + get Name() { + return this.name; + } + +} + +let p = new Person("xiaoming"); +print(p.Name); +p.Name = "xiaohong" +print(p.Name) diff --git a/test/aottest/ts_inline_deopt_loop/BUILD.gn b/test/aottest/ts_inline_deopt_loop/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..f45110be0b61632a100fe9c171f9fa24a0d00930 --- /dev/null +++ b/test/aottest/ts_inline_deopt_loop/BUILD.gn @@ -0,0 +1,21 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("ts_inline_deopt_loop") { + deps = [] + is_enable_opt_inlining = true + is_enable_inline_trace = true + log_option = " --log-info=trace" +} diff --git a/test/aottest/ts_inline_deopt_loop/expect_output.txt b/test/aottest/ts_inline_deopt_loop/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..0973c2e85182a8740f1e22ca947924f0dc22747c --- /dev/null +++ b/test/aottest/ts_inline_deopt_loop/expect_output.txt @@ -0,0 +1,16 @@ +# 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. + +[trace] aot inline function name: RunNormalCall@ts_inline_deopt_loop caller function name: func_main_0@ts_inline_deopt_loop +[trace] aot inline function name: GenerateArray@ts_inline_deopt_loop caller function name: RunNormalCall@ts_inline_deopt_loop +844 diff --git a/test/aottest/ts_inline_deopt_loop/ts_inline_deopt_loop.ts b/test/aottest/ts_inline_deopt_loop/ts_inline_deopt_loop.ts new file mode 100644 index 0000000000000000000000000000000000000000..5b6a65c370c282d0e054f0bd65b2bc8b35e53b6f --- /dev/null +++ b/test/aottest/ts_inline_deopt_loop/ts_inline_deopt_loop.ts @@ -0,0 +1,52 @@ +/* + * 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. + */ + +declare function print(arg:any) : string; + +function GenerateArray(): Int32Array { + let resource: Int32Array = new Int32Array([12, 43, 56, 76, 89, 54, 45, 32, 35, 47, 46, 44, 21, 37, 84]); + return resource; +} + +function Foo(resources: number[], index: number) : number { + let num = resources[index % resources.length]; + if (num % 2 == 0) { + num += 2 + } else { + num += 1 + } + num = num / 2; + for (let i = 0; i < resources.length; ++i) { + num += resources[i] * i; + if (num % 2 == 0) { + num += 2 + } else { + num += 1 + } + num = num / 2; + } + return resources[index % resources.length] + num; +} +export function RunNormalCall():number { + let res : number = 1; + let resources : Int32Array = GenerateArray(); + for(let i=0;i<100;i++) { + res = Foo(resources, i); + } + return res + +} +let num = RunNormalCall() +print(num) \ No newline at end of file diff --git a/test/aottest/ts_inline_exception1/BUILD.gn b/test/aottest/ts_inline_exception1/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..46d777f9b500ad4f9183a372dce58a8645fedb16 --- /dev/null +++ b/test/aottest/ts_inline_exception1/BUILD.gn @@ -0,0 +1,19 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("ts_inline_exception1") { + deps = [] + is_enable_opt_inlining = true +} diff --git a/test/aottest/ts_inline_exception1/expect_output.txt b/test/aottest/ts_inline_exception1/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..739548a9e5e2931793a2e356b9c453402961054e --- /dev/null +++ b/test/aottest/ts_inline_exception1/expect_output.txt @@ -0,0 +1,18 @@ +# 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. + +ReferenceError: b is not defined + at foo1 (maybe inlined). depth: 0 + at foo2 (hidden:26:26) + at func_main_0 (hidden:28:1) + diff --git a/test/aottest/ts_inline_exception1/ts_inline_exception1.ts b/test/aottest/ts_inline_exception1/ts_inline_exception1.ts new file mode 100644 index 0000000000000000000000000000000000000000..b27e7fedb0957e80df4e5b80cf1c406cd1f36346 --- /dev/null +++ b/test/aottest/ts_inline_exception1/ts_inline_exception1.ts @@ -0,0 +1,32 @@ +/* + * 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. + */ + +declare function print(arg:any):string; +declare class ArkTools { + static hiddenStackSourceFile(): boolean; +} +ArkTools.hiddenStackSourceFile() +try { + function foo1() { + b.c + } + function foo2() { + foo1() + } + foo2() +} catch (e) { + print(e) + print(e.stack) +} diff --git a/test/aottest/ts_inline_exception2/BUILD.gn b/test/aottest/ts_inline_exception2/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..5059b497ac3eb30c96c9443e1bdc59ffbb9a4444 --- /dev/null +++ b/test/aottest/ts_inline_exception2/BUILD.gn @@ -0,0 +1,19 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("ts_inline_exception2") { + deps = [] + is_enable_opt_inlining = true +} diff --git a/test/aottest/ts_inline_exception2/expect_output.txt b/test/aottest/ts_inline_exception2/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..ee30cf10c2b51ab2d86dcec7e448eef946210c3f --- /dev/null +++ b/test/aottest/ts_inline_exception2/expect_output.txt @@ -0,0 +1,22 @@ +# 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. + +TypeError: CallObj is NonCallable + at foo1 (maybe inlined). depth: 4 + at foo2 (maybe inlined). depth: 3 + at foo3 (maybe inlined). depth: 2 + at foo4 (maybe inlined). depth: 1 + at foo5 (maybe inlined). depth: 0 + at foo6 (hidden:40:40) + at func_main_0 (hidden:42:1) + diff --git a/test/aottest/ts_inline_exception2/ts_inline_exception2.ts b/test/aottest/ts_inline_exception2/ts_inline_exception2.ts new file mode 100644 index 0000000000000000000000000000000000000000..b2b8f5cfcab2c783ef5361651942f346c7a532e7 --- /dev/null +++ b/test/aottest/ts_inline_exception2/ts_inline_exception2.ts @@ -0,0 +1,53 @@ +/* + * 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. + */ + +declare function print(arg:any):string; +declare class ArkTools { + static hiddenStackSourceFile(): boolean; +} +ArkTools.hiddenStackSourceFile() + +try { + let a = {} + function foo1() { + a() + } + function foo2() { + foo1() + } + function foo3() { + foo2() + } + function foo4() { + foo3() + } + function foo5() { + foo4() + } + function foo6() { + foo5() + } + foo6() + function foo7() { + foo6() + } + function foo8() { + foo7() + } + foo8() +} catch (e) { + print(e) + print(e.stack) +} diff --git a/test/aottest/ts_inline_exception3/BUILD.gn b/test/aottest/ts_inline_exception3/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..fe580cdb72da58bc9cf1061a447c3abc0678bf06 --- /dev/null +++ b/test/aottest/ts_inline_exception3/BUILD.gn @@ -0,0 +1,19 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("ts_inline_exception3") { + deps = [] + is_enable_opt_inlining = true +} diff --git a/test/aottest/ts_inline_exception3/expect_output.txt b/test/aottest/ts_inline_exception3/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..ab3ea753057ca43c290cea4655e03a59d50491b9 --- /dev/null +++ b/test/aottest/ts_inline_exception3/expect_output.txt @@ -0,0 +1,20 @@ +# 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. + +SyntaxError: Unexpected Array in JSON + at foo1 (maybe inlined). depth: 2 + at foo3 (maybe inlined). depth: 1 + at foo4 (maybe inlined). depth: 0 + at foo5 (hidden:36:36) + at func_main_0 (hidden:38:1) + diff --git a/test/aottest/ts_inline_exception3/ts_inline_exception3.ts b/test/aottest/ts_inline_exception3/ts_inline_exception3.ts new file mode 100644 index 0000000000000000000000000000000000000000..03f98aed15498b849f8da3f81943ce3b76cc55aa --- /dev/null +++ b/test/aottest/ts_inline_exception3/ts_inline_exception3.ts @@ -0,0 +1,42 @@ +/* + * 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. + */ + +declare function print(arg:any):string; +declare class ArkTools { + static hiddenStackSourceFile(): boolean; +} +ArkTools.hiddenStackSourceFile() + +try { + function foo1() { + JSON.parse("[1, 2"); + } + function foo2() { + } + function foo3() { + foo2() + foo1() + } + function foo4() { + foo3() + } + function foo5() { + foo4() + } + foo5() +} catch (e) { + print(e) + print(e.stack) +} diff --git a/test/aottest/ts_inline_exception4/BUILD.gn b/test/aottest/ts_inline_exception4/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..ce55cb528d0ec21ed4815bd7cda1af34fdd78802 --- /dev/null +++ b/test/aottest/ts_inline_exception4/BUILD.gn @@ -0,0 +1,19 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("ts_inline_exception4") { + deps = [] + is_enable_opt_inlining = true +} diff --git a/test/aottest/ts_inline_exception4/expect_output.txt b/test/aottest/ts_inline_exception4/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..e2cc032fae05e0affac56ae8a7bfccd06e0323f5 --- /dev/null +++ b/test/aottest/ts_inline_exception4/expect_output.txt @@ -0,0 +1,21 @@ +# 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. + +ReferenceError: c is not defined + at anonymous (hidden:23:23) + at foo1 (maybe inlined). depth: 1 + at foo2 (maybe inlined). depth: 0 + at foo3 (hidden:35:35) + at foo4 (hidden:38:38) + at func_main_0 (hidden:41:1) + diff --git a/test/aottest/ts_inline_exception4/ts_inline_exception4.ts b/test/aottest/ts_inline_exception4/ts_inline_exception4.ts new file mode 100644 index 0000000000000000000000000000000000000000..aa7d1d8c5f5c4b86b57abaf8b92871b90afd5b06 --- /dev/null +++ b/test/aottest/ts_inline_exception4/ts_inline_exception4.ts @@ -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. + */ + +declare function print(arg:any):string; +declare class ArkTools { + static hiddenStackSourceFile(): boolean; +} +ArkTools.hiddenStackSourceFile() +const o = { + [Symbol.toPrimitive] () { + c + return 1 + } +} +try { + function foo1() { + if (o == 1) {} + } + function foo2() { + foo1() + } + function foo3(a) { + foo2() + } + function foo4() { + foo3() + } + + foo4() +} catch (e) { + print(e) + print(e.stack) +} diff --git a/test/aottest/ts_inline_exception5/BUILD.gn b/test/aottest/ts_inline_exception5/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..1bac821d12841172a5f79a8c063c3aab921cb637 --- /dev/null +++ b/test/aottest/ts_inline_exception5/BUILD.gn @@ -0,0 +1,19 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("ts_inline_exception5") { + deps = [] + is_enable_opt_inlining = true +} diff --git a/test/aottest/ts_inline_exception5/expect_output.txt b/test/aottest/ts_inline_exception5/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..0cdbcd7417a75b5fe1c58a9fb33d060018775a4a --- /dev/null +++ b/test/aottest/ts_inline_exception5/expect_output.txt @@ -0,0 +1,20 @@ +# 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. + +RangeError: Invalid array length + at foo1 (maybe inlined). depth: 1 + at foo2 (maybe inlined). depth: 0 + at foo3 (hidden:30:30) + at foo4 (hidden:33:33) + at func_main_0 (hidden:35:1) + diff --git a/test/aottest/ts_inline_exception5/ts_inline_exception5.ts b/test/aottest/ts_inline_exception5/ts_inline_exception5.ts new file mode 100644 index 0000000000000000000000000000000000000000..234a3856008f52d8c64abd99673d4731fdc73968 --- /dev/null +++ b/test/aottest/ts_inline_exception5/ts_inline_exception5.ts @@ -0,0 +1,39 @@ +/* + * 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. + */ + +declare function print(arg:any):string; +declare class ArkTools { + static hiddenStackSourceFile(): boolean; +} +ArkTools.hiddenStackSourceFile() + +try { + function foo1() { + new Array(111111111111111111111) + } + function foo2() { + foo1() + } + function foo3(a) { + foo2() + } + function foo4() { + foo3() + } + foo4() +} catch (e) { + print(e) + print(e.stack) +} diff --git a/test/aottest/ts_inline_exception6/BUILD.gn b/test/aottest/ts_inline_exception6/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..af0dda4dab2a28d9493a20504e4b89b231bf2b36 --- /dev/null +++ b/test/aottest/ts_inline_exception6/BUILD.gn @@ -0,0 +1,19 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("ts_inline_exception6") { + deps = [] + is_enable_opt_inlining = true +} diff --git a/test/aottest/ts_inline_exception6/expect_output.txt b/test/aottest/ts_inline_exception6/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..5d50de70895913b31cb6078ed3e9f322d7acd7c1 --- /dev/null +++ b/test/aottest/ts_inline_exception6/expect_output.txt @@ -0,0 +1,22 @@ +# 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. + +ReferenceError: c is not defined + at foo1 (maybe inlined). depth: 0 + at foo2 (hidden:27:27) + at foo3 (hidden:31:31) + at foo4 (hidden:34:34) + at foo5 (maybe inlined). depth: 0 + at foo6 (hidden:40:40) + at func_main_0 (hidden:42:1) + diff --git a/test/aottest/ts_inline_exception6/ts_inline_exception6.ts b/test/aottest/ts_inline_exception6/ts_inline_exception6.ts new file mode 100644 index 0000000000000000000000000000000000000000..553f16b3d9997f393b992a9c8ff34b98e9590bd5 --- /dev/null +++ b/test/aottest/ts_inline_exception6/ts_inline_exception6.ts @@ -0,0 +1,46 @@ +/* + * 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. + */ + +declare function print(arg:any):string; +declare class ArkTools { + static hiddenStackSourceFile(): boolean; +} +ArkTools.hiddenStackSourceFile() + +try { + function foo1() { + c + } + function foo2() { + foo1() + } + function foo3(a: number): number { + let b: number = a + 1 + foo2() + } + function foo4() { + foo3('a', 1) + } + function foo5() { + foo4(1) + } + function foo6() { + foo5() + } + foo6(1) +} catch (e) { + print(e) + print(e.stack) +} \ No newline at end of file diff --git a/test/aottest/ts_inline_exception7/BUILD.gn b/test/aottest/ts_inline_exception7/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..8297653867a7ce74451cdb06abe57517e6ae74ee --- /dev/null +++ b/test/aottest/ts_inline_exception7/BUILD.gn @@ -0,0 +1,21 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("ts_inline_exception7") { + deps = [] + is_only_typed_path = true + is_enable_opt_inlining = true + is_enable_trace_deopt = true +} diff --git a/test/aottest/ts_inline_exception7/expect_output.txt b/test/aottest/ts_inline_exception7/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..8ebf2e2d6fdcf17b6a85b763e5443808434b6ed8 --- /dev/null +++ b/test/aottest/ts_inline_exception7/expect_output.txt @@ -0,0 +1,20 @@ +# 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. + +SyntaxError: Unexpected Array in JSON + at Student (hidden:26:26) + at foo1 (maybe inlined). depth: 1 + at foo2 (maybe inlined). depth: 0 + at foo3 (hidden:40:40) + at func_main_0 (hidden:43:1) + diff --git a/test/aottest/ts_inline_exception7/ts_inline_exception7.ts b/test/aottest/ts_inline_exception7/ts_inline_exception7.ts new file mode 100644 index 0000000000000000000000000000000000000000..f5f10a3ccc060fbc6fecc21ab8e0218fc4abd8fb --- /dev/null +++ b/test/aottest/ts_inline_exception7/ts_inline_exception7.ts @@ -0,0 +1,47 @@ +/* + * 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. + */ + +declare function print(arg:any):string; +declare class ArkTools { + static hiddenStackSourceFile(): boolean; +} +ArkTools.hiddenStackSourceFile() +try { + class Student { + name : string; + constructor(name:string) { + this.name = name; + JSON.parse("[1, 2"); + } + } + + function foo1() { + let stu = new Student("xiaoming"); + let ans = stu.name + } + + function foo2() { + foo1() + } + + function foo3() { + foo2() + } + + foo3(1) +} catch (e) { + print(e) + print(e.stack) +} \ No newline at end of file diff --git a/test/aottest/ts_inline_exception8/BUILD.gn b/test/aottest/ts_inline_exception8/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..a89908df5ad6200305222e355749d70b662c663d --- /dev/null +++ b/test/aottest/ts_inline_exception8/BUILD.gn @@ -0,0 +1,19 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("ts_inline_exception8") { + deps = [] + is_enable_opt_inlining = true +} diff --git a/test/aottest/ts_inline_exception8/expect_output.txt b/test/aottest/ts_inline_exception8/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..1ccf5654589aed7fe5c1a9cd059e8f299fe1100b --- /dev/null +++ b/test/aottest/ts_inline_exception8/expect_output.txt @@ -0,0 +1,20 @@ +# 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. + +0 +ReferenceError: a is not defined + at name (hidden:25:25) + at foo1 (maybe inlined). depth: 0 + at foo2 (hidden:34:34) + at func_main_0 (hidden:37:1) + diff --git a/test/aottest/ts_inline_exception8/ts_inline_exception8.ts b/test/aottest/ts_inline_exception8/ts_inline_exception8.ts new file mode 100644 index 0000000000000000000000000000000000000000..1e90c84faab2a14d0af51a1059ac2e238ab76cc6 --- /dev/null +++ b/test/aottest/ts_inline_exception8/ts_inline_exception8.ts @@ -0,0 +1,41 @@ +/* + * 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. + */ + +declare function print(arg:any):string; +declare class ArkTools { + static hiddenStackSourceFile(): boolean; +} +ArkTools.hiddenStackSourceFile() +class A { + constructor() {} + get name() { + print(arguments.length) + a.b + } + set name(a) {} +} +let ins = new A() +function foo1() { + ins.name +} +function foo2() { + foo1() +} +try { + foo2() +} catch (e) { + print(e) + print(e.stack) +} \ No newline at end of file diff --git a/test/aottest/ts_inline_max_call/expect_output.txt b/test/aottest/ts_inline_max_call/expect_output.txt index 0c98b3fb924f800549273168559bbf93355c4b30..2a2347902e1d6643ca120bcc2f32e8c951732986 100644 --- a/test/aottest/ts_inline_max_call/expect_output.txt +++ b/test/aottest/ts_inline_max_call/expect_output.txt @@ -21,4 +21,6 @@ foo3 foo4 [trace] aot inline function name: foo5@ts_inline_max_call caller function name: func_main_0@ts_inline_max_call foo5 +[trace] aot inline function name: foo6@ts_inline_max_call caller function name: func_main_0@ts_inline_max_call foo6 +foo7 diff --git a/test/aottest/ts_inline_max_call/ts_inline_max_call.ts b/test/aottest/ts_inline_max_call/ts_inline_max_call.ts index b22cb655a0df4404fb1b4b505a60c5063552d989..ea26e1636259021592b7e2c1217ed2ab1c9d674a 100644 --- a/test/aottest/ts_inline_max_call/ts_inline_max_call.ts +++ b/test/aottest/ts_inline_max_call/ts_inline_max_call.ts @@ -39,10 +39,15 @@ function foo6() { print("foo6"); } +function foo7() { + print("foo7"); +} + // max inline call allowed is 5 foo1() foo2() foo3() foo4() foo5() -foo6() \ No newline at end of file +foo6() +foo7() \ No newline at end of file diff --git a/test/aottest/ts_multi_inline/BUILD.gn b/test/aottest/ts_multi_inline/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..5463bc682dd16aa33378c79ef6d0cbda45765d81 --- /dev/null +++ b/test/aottest/ts_multi_inline/BUILD.gn @@ -0,0 +1,21 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("ts_multi_inline") { + deps = [] + is_enable_opt_inlining = true + is_enable_inline_trace = true + log_option = " --log-info=trace" +} diff --git a/test/aottest/ts_multi_inline/expect_output.txt b/test/aottest/ts_multi_inline/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..ede429289c3a855e3d139613e79d91d1fed3b381 --- /dev/null +++ b/test/aottest/ts_multi_inline/expect_output.txt @@ -0,0 +1,25 @@ +# 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. + +[trace] aot inline function name: foo1@ts_multi_inline caller function name: func_main_0@ts_multi_inline +foo1 +[trace] aot inline function name: foo2@ts_multi_inline caller function name: foo1@ts_multi_inline +foo2 +[trace] aot inline function name: foo3@ts_multi_inline caller function name: foo2@ts_multi_inline +foo3 +[trace] aot inline function name: foo4@ts_multi_inline caller function name: foo3@ts_multi_inline +foo4 +[trace] aot inline function name: foo5@ts_multi_inline caller function name: foo4@ts_multi_inline +foo5 +[trace] aot inline function name: foo6@ts_multi_inline caller function name: foo5@ts_multi_inline +foo6 diff --git a/test/aottest/ts_multi_inline/ts_multi_inline.ts b/test/aottest/ts_multi_inline/ts_multi_inline.ts new file mode 100644 index 0000000000000000000000000000000000000000..62c24aaf3437642e4ecb10e6bae55db35ce2957a --- /dev/null +++ b/test/aottest/ts_multi_inline/ts_multi_inline.ts @@ -0,0 +1,48 @@ +/* + * 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. + */ + +declare function print(arg:any):string; + +function foo1() { + print("foo1"); + foo2() +} + +function foo2() { + print("foo2"); + foo3() +} + +function foo3() { + print("foo3"); + foo4() +} + +function foo4() { + print("foo4"); + foo5() +} + +function foo5() { + print("foo5"); + foo6() +} + +function foo6() { + print("foo6"); +} + +foo1(); + diff --git a/test/aottest/ts_multi_inline_deopt/BUILD.gn b/test/aottest/ts_multi_inline_deopt/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..ba8faa8d45b4b03486fc7a17f3e247fd5b4b3af5 --- /dev/null +++ b/test/aottest/ts_multi_inline_deopt/BUILD.gn @@ -0,0 +1,21 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("ts_multi_inline_deopt") { + deps = [] + is_enable_opt_inlining = true + is_enable_inline_trace = true + log_option = " --log-info=trace" +} diff --git a/test/aottest/ts_multi_inline_deopt/expect_output.txt b/test/aottest/ts_multi_inline_deopt/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..70bddf024976eadf9cbadb07373518492bebf895 --- /dev/null +++ b/test/aottest/ts_multi_inline_deopt/expect_output.txt @@ -0,0 +1,30 @@ +# 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. + +[trace] aot inline function name: PreCall@ts_multi_inline_deopt caller function name: func_main_0@ts_multi_inline_deopt +[trace] aot inline function name: GenerateArray@ts_multi_inline_deopt caller function name: PreCall@ts_multi_inline_deopt +[trace] aot inline function name: RunNormalCall@ts_multi_inline_deopt caller function name: PreCall@ts_multi_inline_deopt +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +818 diff --git a/test/aottest/ts_multi_inline_deopt/ts_multi_inline_deopt.ts b/test/aottest/ts_multi_inline_deopt/ts_multi_inline_deopt.ts new file mode 100644 index 0000000000000000000000000000000000000000..7531b71b93d0ab37cd6e4d0e54a1b4725da2fe95 --- /dev/null +++ b/test/aottest/ts_multi_inline_deopt/ts_multi_inline_deopt.ts @@ -0,0 +1,60 @@ +/* + * 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. + */ + +declare function print(arg:any) : string; + +function GenerateArray(): Int32Array { + let resource: Int32Array = new Int32Array([12, 43, 56, 76, 89, 54, 45, 32, 35, 47, 46, 44, 21, 37, 84]); + return resource; +} + +function Foo(resources: number[], index: number) : number { + let num = resources[index % resources.length]; + if (num % 2 == 0) { + num += 2 + } else { + num += 1 + } + num = num / 2; + for (let i = 0; i < resources.length; ++i) { + num += resources[i] * i; + if (num % 2 == 0) { + num += 2 + } else { + num += 1 + } + num = num / 2; + } + return resources[index % resources.length] + num; +} + +function RunNormalCall(resources: Int32Array):number { + let res : number = 1; + for(let i=0;i<13;i++) { + res = Foo(resources, i); + print(i) + } + return res + +} + +function PreCall(): number { + let resources : Int32Array = GenerateArray(); + let res = RunNormalCall(resources) + return res; +} + +let num = PreCall() +print(num) \ No newline at end of file diff --git a/test/aottest/ts_multi_inline_max_call/BUILD.gn b/test/aottest/ts_multi_inline_max_call/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..eef38f3c4196ec4e54d9898b535fd9bf3365a8dc --- /dev/null +++ b/test/aottest/ts_multi_inline_max_call/BUILD.gn @@ -0,0 +1,21 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("ts_multi_inline_max_call") { + deps = [] + is_enable_opt_inlining = true + is_enable_inline_trace = true + log_option = " --log-info=trace" +} diff --git a/test/aottest/ts_multi_inline_max_call/expect_output.txt b/test/aottest/ts_multi_inline_max_call/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..e56e027211c2f927f13f48708f280f13bad1f798 --- /dev/null +++ b/test/aottest/ts_multi_inline_max_call/expect_output.txt @@ -0,0 +1,34 @@ +# 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. + +[trace] aot inline function name: foo@ts_multi_inline_max_call caller function name: func_main_0@ts_multi_inline_max_call +[trace] aot inline function name: foo1@ts_multi_inline_max_call caller function name: foo@ts_multi_inline_max_call +[trace] aot inline function name: bar@ts_multi_inline_max_call caller function name: foo1@ts_multi_inline_max_call +bar +[trace] aot inline function name: foo2@ts_multi_inline_max_call caller function name: foo@ts_multi_inline_max_call +[trace] aot inline function name: bar@ts_multi_inline_max_call caller function name: foo2@ts_multi_inline_max_call +bar +[trace] aot inline function name: foo3@ts_multi_inline_max_call caller function name: foo@ts_multi_inline_max_call +[trace] aot inline function name: bar@ts_multi_inline_max_call caller function name: foo3@ts_multi_inline_max_call +bar +[trace] aot inline function name: foo4@ts_multi_inline_max_call caller function name: foo@ts_multi_inline_max_call +[trace] aot inline function name: bar@ts_multi_inline_max_call caller function name: foo4@ts_multi_inline_max_call +bar +[trace] aot inline function name: foo5@ts_multi_inline_max_call caller function name: foo@ts_multi_inline_max_call +[trace] aot inline function name: bar@ts_multi_inline_max_call caller function name: foo5@ts_multi_inline_max_call +bar +[trace] aot inline function name: foo6@ts_multi_inline_max_call caller function name: foo@ts_multi_inline_max_call +[trace] aot inline function name: bar@ts_multi_inline_max_call caller function name: foo6@ts_multi_inline_max_call +bar +[trace] aot inline function name: bar@ts_multi_inline_max_call caller function name: foo7@ts_multi_inline_max_call +bar diff --git a/test/aottest/ts_multi_inline_max_call/ts_multi_inline_max_call.ts b/test/aottest/ts_multi_inline_max_call/ts_multi_inline_max_call.ts new file mode 100644 index 0000000000000000000000000000000000000000..2d88cc3702d23af8dc50f6db7f81190c1a4d4dcb --- /dev/null +++ b/test/aottest/ts_multi_inline_max_call/ts_multi_inline_max_call.ts @@ -0,0 +1,60 @@ +/* + * 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. + */ + +declare function print(arg:any):string; + +function foo() { + foo1(); + foo2(); + foo3(); + foo4(); + foo5(); + foo6(); + foo7(); +} + +function foo1() { + bar(); +} + +function foo2() { + bar(); +} + +function foo3() { + bar(); +} + +function foo4() { + bar(); +} + +function foo5() { + bar() +} + +function foo6() { + bar(); +} + +function foo7() { + bar(); +} + +function bar() { + print("bar") +} + +foo(); \ No newline at end of file diff --git a/test/aottest/ts_multi_inline_recursive/BUILD.gn b/test/aottest/ts_multi_inline_recursive/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..56cb3a247b6eeda0a6b4e25673f3142c891a8fb0 --- /dev/null +++ b/test/aottest/ts_multi_inline_recursive/BUILD.gn @@ -0,0 +1,21 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("ts_multi_inline_recursive") { + deps = [] + is_enable_opt_inlining = true + is_enable_inline_trace = true + log_option = " --log-info=trace" +} diff --git a/test/aottest/ts_multi_inline_recursive/expect_output.txt b/test/aottest/ts_multi_inline_recursive/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..a505549561b961e3924c52b80cbc5455cdd3b2d8 --- /dev/null +++ b/test/aottest/ts_multi_inline_recursive/expect_output.txt @@ -0,0 +1,17 @@ +# 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. + +[trace] aot inline function name: foo@ts_multi_inline_recursive caller function name: func_main_0@ts_multi_inline_recursive +foo +[trace] aot inline function name: bar@ts_multi_inline_recursive caller function name: foo@ts_multi_inline_recursive +bar diff --git a/test/aottest/ts_multi_inline_recursive/ts_multi_inline_recursive.ts b/test/aottest/ts_multi_inline_recursive/ts_multi_inline_recursive.ts new file mode 100644 index 0000000000000000000000000000000000000000..ed2907a8bef819c373147cd3f0ec289a2999f95d --- /dev/null +++ b/test/aottest/ts_multi_inline_recursive/ts_multi_inline_recursive.ts @@ -0,0 +1,33 @@ +/* + * 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. + */ + +declare function print(arg:any):string; + +function foo() { + print("foo"); + bar(10); +} + +function bar(num: number) { + if (num == 0) { + print("bar"); + return; + } + num--; + bar(num); +} + +foo() + diff --git a/test/aottest/typedarray/BUILD.gn b/test/aottest/typedarray/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..cc548c78bd1b4c72c5fd1a0c3ceaff74d71c9a3d --- /dev/null +++ b/test/aottest/typedarray/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("typedarray") { + deps = [] +} diff --git a/test/aottest/typedarray/expect_output.txt b/test/aottest/typedarray/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..452c8afdd8b4dfdbfa0116d1d85d13e82ba6a2f0 --- /dev/null +++ b/test/aottest/typedarray/expect_output.txt @@ -0,0 +1,15 @@ +# 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. + +number +boolean diff --git a/test/aottest/typedarray/typedarray.ts b/test/aottest/typedarray/typedarray.ts new file mode 100644 index 0000000000000000000000000000000000000000..ad65708eec481a36f709f9578ac3f7601c02616c --- /dev/null +++ b/test/aottest/typedarray/typedarray.ts @@ -0,0 +1,32 @@ +/* + * 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. + */ + +declare function print(arg:any):string; + +let a = new Int32Array([2, 3]) +a[1] = 1 < 2 +print(typeof a[1]) + +class B { + x + constructor(x) { + this.x = x; + } +} + +let b = new B(1); +b.x = 1 < 2 +print(typeof b.x) + diff --git a/test/aottest/typedarray_load_store/BUILD.gn b/test/aottest/typedarray_load_store/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..b5a6ef17dd63832134fb4bdccbfd15560b920828 --- /dev/null +++ b/test/aottest/typedarray_load_store/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("typedarray_load_store") { + deps = [] +} diff --git a/test/aottest/typedarray_load_store/expect_output.txt b/test/aottest/typedarray_load_store/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..46709658855ebc35b455241ad3a3eb645a39dac2 --- /dev/null +++ b/test/aottest/typedarray_load_store/expect_output.txt @@ -0,0 +1,23 @@ +# 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. + +474 +676 +474 +1150 +1624 +477.80 +818.10 +1013.50 +1513.00 +1990.80 diff --git a/test/aottest/typedarray_load_store/typedarray_load_store.ts b/test/aottest/typedarray_load_store/typedarray_load_store.ts new file mode 100644 index 0000000000000000000000000000000000000000..1f097cf6de3ebaa1f6471289021a4c8e60176fc7 --- /dev/null +++ b/test/aottest/typedarray_load_store/typedarray_load_store.ts @@ -0,0 +1,130 @@ +/* + * 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. + */ + + + + +/** if you failed on this test, it's most likely that you accidentally + * set TypedArray.isOnHeap to true with this TypedArray construded from ArrayBuffer. */ + +let buffer = new ArrayBuffer(120); + +function GenerateFakeRandomInteger(): Int32Array { + let resource: Int32Array = new Int32Array([12, 43, 56, 76, 89, 54, 45, 32, 35, 47, 46, 44, 21, 37, 84]); + return resource; +} + +function GenerateFakeRandomFloat(): Float64Array { + let resource: Float64Array = new Float64Array([12.2, 43.5, 56.2, 76.6, 89.7, 54.9, 45.2, 32.5, 35.6, 47.2, 46.6, 44.3, 21.2, 37.6, 84.57]); + return resource; +} + +function GenerateFakeRandomIndex(): Int32Array { + let resource: Int32Array = new Int32Array([3, 14, 44, 25, 91, 38, 82, 88, 64, 81, 70, 90, 33, 63, 70]); + return resource; +} + +/** Test fake random load and store of Int32Array. */ +function IntegerArray(res: Int32Array) { + let count: number = 10; + let integerIndexes: Int32Array = GenerateFakeRandomIndex(); + let resources: Int32Array = GenerateFakeRandomInteger(); + let resLength: number = res.length; + let num: number = 1; + let length: number = resources.length - 1; + for (let i = 0; i < count; i++) { + num += integerIndexes[i % num & length]; + res[i % resLength] += resources[i % num & length]; + } + let tmp: number = 0; + for (let i = 0; i < resLength; i++) { + tmp += res[i]; + } + print(tmp); +} + +(() => { + /** TypedArray constructed from ArrayBuffer with byteOffset = 20 and length = 4. */ + let arr1 = new Int32Array(buffer, 20, 4); + + IntegerArray(arr1); + + /** TypedArray constructed from ArrayBuffer with byteOffset = 28 and length = 5, intersecting with arr1. */ + let arr2 = new Int32Array(buffer, 28, 5); + + IntegerArray(arr2); + + /** TypedArray constructed from ArrayBuffer with byteOffset = 0 and length = 5. */ + let arr3 = new Int32Array(buffer, 0, 5); + + IntegerArray(arr3); + + /** TypedArray constructed from another TypedArray(constructed from ArrayBuffer). */ + let arr4 = new Int32Array(arr2); + + IntegerArray(arr4); + + /** TypedArray constructed from another TypedArray(constructed from TypedArray). */ + let arr5 = new Int32Array(arr4); + + IntegerArray(arr5); +})(); + +/** Test fake random load and store of Float64Array. */ +function FloatArray(res: Float64Array) { + let count: number = 10; + let resources: Float64Array = GenerateFakeRandomFloat(); + let integerIndexes: Int32Array = GenerateFakeRandomIndex(); + let resLength: number = res.length; + let num: number = 1; + let length: number = resources.length - 1; + for (let i = 0; i < count; i++) { + num += integerIndexes[i % num & length]; + res[i % resLength] += resources[i % num & length]; + } + let tmp: number = 0.0; + for (let i = 0; i < resLength; i++) { + tmp += res[i]; + } + /** use toFixed() to avoid accuracy problems */ + print(tmp.toFixed(2)); +} + +(() => { + /** TypedArray constructed from ArrayBuffer with byteOffset = 16 and length = 4. */ + let arr1 = new Float64Array(buffer, 16, 4); + + FloatArray(arr1); + + /** TypedArray constructed from ArrayBuffer with byteOffset = 24 and length = 5, intersecting with arr1. */ + let arr2 = new Float64Array(buffer, 24, 5); + + FloatArray(arr2); + + /** TypedArray constructed from ArrayBuffer with byteOffset = 0 and length = 5, intersecting with arr1 and arr2. */ + let arr3 = new Float64Array(buffer, 0, 5); + + FloatArray(arr3); + + /** TypedArray constructed from another TypedArray(constructed from ArrayBuffer). */ + let arr4 = new Float64Array(arr2); + + FloatArray(arr4); + + /** TypedArray constructed from another TypedArray(constructed from TypedArray). */ + let arr5 = new Float64Array(arr4); + + FloatArray(arr5); +})(); diff --git a/test/aottest/typeof/expect_output.txt b/test/aottest/typeof/expect_output.txt index b30298f249e8c88394620e59ec4af64c5640aa12..331769bc5df65cf62726640f1d23c1273d11a5ef 100644 --- a/test/aottest/typeof/expect_output.txt +++ b/test/aottest/typeof/expect_output.txt @@ -18,3 +18,8 @@ boolean undefined object object +object +function +object +bigint +function diff --git a/test/aottest/typeof/typeof.ts b/test/aottest/typeof/typeof.ts index 951642cb34afb9b8d6a168cf432d1c1704ba6d62..7f9de628cc3a1d32aab2ad3419b122a8db13596a 100644 --- a/test/aottest/typeof/typeof.ts +++ b/test/aottest/typeof/typeof.ts @@ -36,4 +36,17 @@ print(typeof(arr)); let obj = {}; print(typeof(obj)); +let n:any = null; +print(typeof(n)); +class A {}; +print(typeof(A)); + +let a = new A(); +print(typeof(a)); + +let bigInt = 9007199254740991n; +print(typeof(bigInt)); + +function foo() {} +print(typeof(foo)); \ No newline at end of file diff --git a/test/aottest/unaryop_special_value/BUILD.gn b/test/aottest/unaryop_special_value/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..c57516f972fd31c209568a226ff726f99b8b000b --- /dev/null +++ b/test/aottest/unaryop_special_value/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("unaryop_special_value") { + deps = [] +} diff --git a/test/aottest/unaryop_special_value/expect_output.txt b/test/aottest/unaryop_special_value/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..70a3f44a40df8793cf6754e68a47758ab31c79a0 --- /dev/null +++ b/test/aottest/unaryop_special_value/expect_output.txt @@ -0,0 +1,33 @@ +# 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. + +====== neg ====== +NaN +0 +-1 +0 +====== not ====== +-1 +-1 +-2 +-1 +====== inc ====== +NaN +1 +2 +1 +====== dec ====== +NaN +0 +1 +0 diff --git a/test/aottest/unaryop_special_value/unaryop_special_value.ts b/test/aottest/unaryop_special_value/unaryop_special_value.ts new file mode 100644 index 0000000000000000000000000000000000000000..4080017f0dda89bd0535762d951f9c00765c224e --- /dev/null +++ b/test/aottest/unaryop_special_value/unaryop_special_value.ts @@ -0,0 +1,50 @@ +/* + * 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. + */ + +declare function print(arg:any):string; + +let undf: any = undefined; +let falseValue: boolean = false; +let trueValue: boolean = true; +let nullValue: any = null; + +// neg op +print("====== neg ======"); +print(-undf); +print(-falseValue); +print(-trueValue); +print(-nullValue); + +// not op +print("====== not ======"); +print(~undf); +print(~falseValue); +print(~trueValue); +print(~nullValue); + +// inc op +print("====== inc ======"); +print(++undf); +print(++falseValue); +print(++trueValue); +print(++nullValue); + + +// dec op +print("====== dec ======"); +print(--undf); +print(--falseValue); +print(--trueValue); +print(--nullValue); \ No newline at end of file diff --git a/test/deopttest/BUILD.gn b/test/deopttest/BUILD.gn index 45343948546f010f6bb618cb75b34e9ced579e18..3fbe87338e82933846cf6ad9230ebde90aa7c42e 100644 --- a/test/deopttest/BUILD.gn +++ b/test/deopttest/BUILD.gn @@ -17,12 +17,14 @@ group("ark_deopt_test") { "arithmetic", "async_deopt", "comparison", + "createarraywithbuffer", "dec", "div", "inc", "live_out", "mod", "newobjrange", + "polymorphic_array", "restore_vregs", ] diff --git a/test/deopttest/async_deopt/async_deopt.ts b/test/deopttest/async_deopt/async_deopt.ts index 5d9f5e2b50f45a18222596498972343730455841..308a9307be59546a8094350967ec6aeab6adedc0 100644 --- a/test/deopttest/async_deopt/async_deopt.ts +++ b/test/deopttest/async_deopt/async_deopt.ts @@ -17,22 +17,26 @@ declare function print(arg:any):string; class A { n: number = 2 + + x(){} } class B { m:number = 3 + + y(){} } async function foo() { - A.prototype.o = 1 let a = new A() + a.x = 1 let r = await a.n // deopt before suspendgenerator print(r) } async function bar() { - B.prototype.o = 1 let b = new B() + b.y = 1 let r = await 1 let c = b.m // deopt after suspendgenerator print(c) diff --git a/test/deopttest/createarraywithbuffer/BUILD.gn b/test/deopttest/createarraywithbuffer/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..00b73ab3be48f838729187463c81d9d27d2b7a3a --- /dev/null +++ b/test/deopttest/createarraywithbuffer/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("createarraywithbuffer") { + deps = [] +} diff --git a/test/deopttest/createarraywithbuffer/createarraywithbuffer.ts b/test/deopttest/createarraywithbuffer/createarraywithbuffer.ts new file mode 100644 index 0000000000000000000000000000000000000000..bb6d6bd55c8e27aa8f1fa504b7076c9e8a76759c --- /dev/null +++ b/test/deopttest/createarraywithbuffer/createarraywithbuffer.ts @@ -0,0 +1,28 @@ +/* + * 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. + */ + +declare function print(arg:any, arg1?: any):string; + +function lengthVector(self: number[]): number { + return Math.sqrt(self[0] * self[0] + self[1] * self[1] + self[1] * self[1]); +} + +function normalise(v: number[]): number[] { + let len = lengthVector(v); + print(len); + return [v[0] / len, v[1] / len, v[2] / len]; +} + +print(normalise([0.8, -0.8, 1])); \ No newline at end of file diff --git a/test/deopttest/createarraywithbuffer/expect_output.txt b/test/deopttest/createarraywithbuffer/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..c9091a883b3670d6f8c11b212b858c58c44f724a --- /dev/null +++ b/test/deopttest/createarraywithbuffer/expect_output.txt @@ -0,0 +1,15 @@ +# 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. + +1.385640646055102 +0.5773502691896257,-0.5773502691896257,0.721687836487032 diff --git a/test/deopttest/polymorphic_array/BUILD.gn b/test/deopttest/polymorphic_array/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..e899e43fa71e1852f69ea477d90c1728ccb43736 --- /dev/null +++ b/test/deopttest/polymorphic_array/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_aot_test_action("polymorphic_array") { + deps = [] +} diff --git a/test/deopttest/polymorphic_array/expect_output.txt b/test/deopttest/polymorphic_array/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..2319bf138639911e9641083d14b005bdbf0a783e --- /dev/null +++ b/test/deopttest/polymorphic_array/expect_output.txt @@ -0,0 +1,25 @@ +# 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. + +0 +1 +2 +3 +0 +1 +2 +3 +8 +8 +4 +8 diff --git a/test/deopttest/polymorphic_array/polymorphic_array.ts b/test/deopttest/polymorphic_array/polymorphic_array.ts new file mode 100644 index 0000000000000000000000000000000000000000..35391500cb4b6dcc10a3eac5f747ceca42aa2f0c --- /dev/null +++ b/test/deopttest/polymorphic_array/polymorphic_array.ts @@ -0,0 +1,55 @@ +/* + * 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. + */ + +declare function print(arg:any, arg1?: any):string; + +function foo(arr: number[]) { + for (let i = 0; i < 4; i++) { + print(arr[i]); + } +} + +function test() { + let a1: number[] = [0, 1, 2, 3]; + foo(a1); + + let a2: number[] = [1.2, 1.3, 2.4, 3]; + for (let i = 0; i < 4; i++) { + a2[i] = i; + } + foo(a2); +} + + +test(); + +function foo1(M1: number[]): number[] { + let M:number[] = new Array(4); + for (let i = 0; i < 4; i++) { + M[i] = M1[i]; + } + return M; +} + +function Init() { + let T:number[] = [8,8,4,8]; + let arr: number[] = foo1(T); + let arr1: number[] = foo1(arr); + for (let i = 0; i < 4; i++) { + print(arr1[i]); + } +} + +Init(); diff --git a/test/fuzztest/arraybufferrefnew_fuzzer/BUILD.gn b/test/fuzztest/arraybufferrefnew_fuzzer/BUILD.gn index 39627d60b2a1c5f251b38a8b2b65025c4b3eb1af..3c951d78f9f34d263cba545363665feed772c0eb 100644 --- a/test/fuzztest/arraybufferrefnew_fuzzer/BUILD.gn +++ b/test/fuzztest/arraybufferrefnew_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ArrayBufferRefNewFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/arraybufferrefnew_fuzzer" diff --git a/test/fuzztest/arraybufferrefnewwithtwoparameters_fuzzer/BUILD.gn b/test/fuzztest/arraybufferrefnewwithtwoparameters_fuzzer/BUILD.gn index b10b6e0a755a8322be2107206094885c0779cb73..69beb81fd2261d3877e5e1a3da0ec7692da8c04e 100644 --- a/test/fuzztest/arraybufferrefnewwithtwoparameters_fuzzer/BUILD.gn +++ b/test/fuzztest/arraybufferrefnewwithtwoparameters_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ArrayBufferRefNewWithTwoParametersFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/arraybufferrefnewwithtwoparameters_fuzzer" diff --git a/test/fuzztest/arraylist_fuzzer/BUILD.gn b/test/fuzztest/arraylist_fuzzer/BUILD.gn index 223eb2e648a5b8a2f157a56bdbff1cdb9197c3ce..538eda54976bf47b8360ef650b59063a64089c60 100644 --- a/test/fuzztest/arraylist_fuzzer/BUILD.gn +++ b/test/fuzztest/arraylist_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ArrayListFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/arraylist_fuzzer" diff --git a/test/fuzztest/bigint64arrayrefnew_fuzzer/BUILD.gn b/test/fuzztest/bigint64arrayrefnew_fuzzer/BUILD.gn index 7246a384e6f0db4aca72ce164a7bbfdb23ea82e4..d717cc9364a2306a1e27f89008f10c5e43ad2310 100644 --- a/test/fuzztest/bigint64arrayrefnew_fuzzer/BUILD.gn +++ b/test/fuzztest/bigint64arrayrefnew_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("BigInt64ArrayRefNewFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/bigint64arrayrefnew_fuzzer" diff --git a/test/fuzztest/biginttoint64_fuzzer/BUILD.gn b/test/fuzztest/biginttoint64_fuzzer/BUILD.gn index e25f93a6becd5645169c8d5ca0084c7c2d9e13cf..487e22b1d7f334f0b670c40d60883eca81613639 100644 --- a/test/fuzztest/biginttoint64_fuzzer/BUILD.gn +++ b/test/fuzztest/biginttoint64_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("BigIntToInt64FuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/biginttoint64_fuzzer" diff --git a/test/fuzztest/biginttouint64_fuzzer/BUILD.gn b/test/fuzztest/biginttouint64_fuzzer/BUILD.gn index 70f24f628e446c03f2df98b4a81da62224f97903..83ef3545b3deaa313c311adc7aeeedb7c08ab401 100644 --- a/test/fuzztest/biginttouint64_fuzzer/BUILD.gn +++ b/test/fuzztest/biginttouint64_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("BigIntToUint64FuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/biginttouint64_fuzzer" diff --git a/test/fuzztest/biguint64arrayrefnew_fuzzer/BUILD.gn b/test/fuzztest/biguint64arrayrefnew_fuzzer/BUILD.gn index d7c166b228bb09cdcdf797452209d1403519c7b0..8002e5efd9c15c7fd076e531197ed8f49a23233a 100644 --- a/test/fuzztest/biguint64arrayrefnew_fuzzer/BUILD.gn +++ b/test/fuzztest/biguint64arrayrefnew_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("BigUint64ArrayRefNewFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/biguint64arrayrefnew_fuzzer" diff --git a/test/fuzztest/binarytotext_fuzzer/BUILD.gn b/test/fuzztest/binarytotext_fuzzer/BUILD.gn index 6bf3306700db40adaaf0fe1f8d914155e11d604a..ad40d8387554fb9be79e10d0c678d388d75a5a15 100644 --- a/test/fuzztest/binarytotext_fuzzer/BUILD.gn +++ b/test/fuzztest/binarytotext_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("BinaryToTextFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "$js_root/test/fuzztest/binarytotext_fuzzer" @@ -28,10 +28,14 @@ ohos_fuzztest("BinaryToTextFuzzTest") { configs = [ "$js_root:ecma_test_config" ] deps = [ - "$js_root:libark_jsruntime", + "$ark_third_party_root/icu/icu4c:shared_icui18n", + "$ark_third_party_root/icu/icu4c:shared_icuuc", + "$js_root:libark_jsruntime_static", sdk_libc_secshared_dep, ] + deps += [ "$ark_root/libpandafile:libarkfile_static" ] + # hiviewdfx libraries external_deps = hiviewdfx_ext_deps deps += hiviewdfx_deps diff --git a/test/fuzztest/binarytotext_fuzzer/binarytotext_fuzzer.cpp b/test/fuzztest/binarytotext_fuzzer/binarytotext_fuzzer.cpp index b0afaa34bf6779b8bed9ecad6acc0ca8bb11fdec..4e13bef5c1a0bb87258e711ed0a0f84f1b3d8e46 100644 --- a/test/fuzztest/binarytotext_fuzzer/binarytotext_fuzzer.cpp +++ b/test/fuzztest/binarytotext_fuzzer/binarytotext_fuzzer.cpp @@ -28,7 +28,7 @@ namespace OHOS { std::string result(data, data + size); file.write(result.c_str(), result.size()); file.close(); - PGOProfilerManager::GetInstance()->BinaryToText( + panda::ecmascript::pgo::PGOProfilerManager::GetInstance()->BinaryToText( "ark-profiler11/modules.ap", "ark-profiler11/modules_recover.text", 2); // 2 : means the hotness threshold } } diff --git a/test/fuzztest/booleanrefnewbool_fuzzer/BUILD.gn b/test/fuzztest/booleanrefnewbool_fuzzer/BUILD.gn index ff453db538b8a0a45eafe31dbfa7c0d7d2d01061..48f0b93cf961f6004ed6a75d2ed92b9c752b2ebf 100644 --- a/test/fuzztest/booleanrefnewbool_fuzzer/BUILD.gn +++ b/test/fuzztest/booleanrefnewbool_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("BooleanRefNewBoolFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/booleanrefnewbool_fuzzer" diff --git a/test/fuzztest/buildjsstacktrace_fuzzer/BUILD.gn b/test/fuzztest/buildjsstacktrace_fuzzer/BUILD.gn index 58edd831bc3862d07c734c829a68d6d6128ff330..2318e029201f896f7734c8fc949beb181757c870 100644 --- a/test/fuzztest/buildjsstacktrace_fuzzer/BUILD.gn +++ b/test/fuzztest/buildjsstacktrace_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("BuildJsStackTraceFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "$js_root/test/fuzztest/buildjsstacktrace_fuzzer" diff --git a/test/fuzztest/buildnativeandJsstacktrace_fuzzer/BUILD.gn b/test/fuzztest/buildnativeandJsstacktrace_fuzzer/BUILD.gn index 82b485a8aed07f44eb0bbe1055dca88fbe626db8..121ea7558ee1ade040d34d6dc6e60c6fd77e6105 100644 --- a/test/fuzztest/buildnativeandJsstacktrace_fuzzer/BUILD.gn +++ b/test/fuzztest/buildnativeandJsstacktrace_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("BuildNativeAndJsStackTraceFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/buildnativeandJsstacktrace_fuzzer" diff --git a/test/fuzztest/builtinsarraybufferallocatearraybuffer_fuzzer/BUILD.gn b/test/fuzztest/builtinsarraybufferallocatearraybuffer_fuzzer/BUILD.gn index 5f34be841d8ca39c6529031eeb096098ae3869a9..a5509bec1429adff65068cc12f5631df65a37aee 100644 --- a/test/fuzztest/builtinsarraybufferallocatearraybuffer_fuzzer/BUILD.gn +++ b/test/fuzztest/builtinsarraybufferallocatearraybuffer_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ##################################fuzztest##################################### ohos_fuzztest("BuiltinsArrayBufferAllocateArrayBufferFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/builtinsarraybufferallocatearraybuffer_fuzzer" diff --git a/test/fuzztest/builtinsarraybufferarraybufferconstructor_fuzzer/BUILD.gn b/test/fuzztest/builtinsarraybufferarraybufferconstructor_fuzzer/BUILD.gn index f6ea3a221931b88c069d406f6874bf41ab27677d..01292be5c0559a1c9f5ea1c1833720d0581e0fce 100644 --- a/test/fuzztest/builtinsarraybufferarraybufferconstructor_fuzzer/BUILD.gn +++ b/test/fuzztest/builtinsarraybufferarraybufferconstructor_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ##################################fuzztest##################################### ohos_fuzztest("BuiltinsArrayBufferArrayBufferConstructorFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/builtinsarraybufferarraybufferconstructor_fuzzer" diff --git a/test/fuzztest/builtinsarraybufferclonearraybuffer_fuzzer/BUILD.gn b/test/fuzztest/builtinsarraybufferclonearraybuffer_fuzzer/BUILD.gn index fe11ee7ca604158d412e6985d0a0e58150aea070..bc59df5a34d7162ddda9154bf2643620ea85d495 100644 --- a/test/fuzztest/builtinsarraybufferclonearraybuffer_fuzzer/BUILD.gn +++ b/test/fuzztest/builtinsarraybufferclonearraybuffer_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ##################################fuzztest##################################### ohos_fuzztest("BuiltinsArrayBufferCloneArrayBufferFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/builtinsarraybufferclonearraybuffer_fuzzer" diff --git a/test/fuzztest/builtinsarraybufferfastsetvalueinbuffer_fuzzer/BUILD.gn b/test/fuzztest/builtinsarraybufferfastsetvalueinbuffer_fuzzer/BUILD.gn index 33eb6abac982554d19365d41011f6e4a0dd79ac3..5519385a7f32ab843fc1de75275243b75d87873a 100644 --- a/test/fuzztest/builtinsarraybufferfastsetvalueinbuffer_fuzzer/BUILD.gn +++ b/test/fuzztest/builtinsarraybufferfastsetvalueinbuffer_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ##################################fuzztest##################################### ohos_fuzztest("BuiltinsArrayBufferFastSetValueInBufferFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/builtinsarraybufferfastsetvalueinbuffer_fuzzer" diff --git a/test/fuzztest/builtinsarraybufferfastsetvalueinbuffer_fuzzer/builtinsarraybufferfastsetvalueinbuffer_fuzzer.cpp b/test/fuzztest/builtinsarraybufferfastsetvalueinbuffer_fuzzer/builtinsarraybufferfastsetvalueinbuffer_fuzzer.cpp index 02fae3a9c07c6da43dfcc335fb4b0f56891f87cb..b4ad93cee32a8c46f2bf2942a10b43cb17553dbe 100644 --- a/test/fuzztest/builtinsarraybufferfastsetvalueinbuffer_fuzzer/builtinsarraybufferfastsetvalueinbuffer_fuzzer.cpp +++ b/test/fuzztest/builtinsarraybufferfastsetvalueinbuffer_fuzzer/builtinsarraybufferfastsetvalueinbuffer_fuzzer.cpp @@ -55,7 +55,7 @@ namespace OHOS { JSTaggedValue arrayBuffer = BuiltinsArrayBuffer::AllocateArrayBuffer(thread, bufferConstructor, MAXBYTELEN); double val = JSTaggedValue(input).GetNumber(); - BuiltinsArrayBuffer::FastSetValueInBuffer(arrayBuffer, 0, DataViewType::INT32, val, true); + BuiltinsArrayBuffer::FastSetValueInBuffer(thread, arrayBuffer, 0, DataViewType::INT32, val, true); JSNApi::DestroyJSVM(vm); } } diff --git a/test/fuzztest/builtinsarraybuffergetvaluefrombufferforbigint_fuzzer/BUILD.gn b/test/fuzztest/builtinsarraybuffergetvaluefrombufferforbigint_fuzzer/BUILD.gn index 26e9f5822ec225cc20a85335c36f2f360312405f..737084ce124af2cc021ba9a5d1e048649eea0a47 100644 --- a/test/fuzztest/builtinsarraybuffergetvaluefrombufferforbigint_fuzzer/BUILD.gn +++ b/test/fuzztest/builtinsarraybuffergetvaluefrombufferforbigint_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ##################################fuzztest##################################### ohos_fuzztest("BuiltinsArrayBufferGetValueFromBufferForBigIntFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/builtinsarraybuffergetvaluefrombufferforbigint_fuzzer" diff --git a/test/fuzztest/builtinsarraybuffergetvaluefrombufferforfloat_fuzzer/BUILD.gn b/test/fuzztest/builtinsarraybuffergetvaluefrombufferforfloat_fuzzer/BUILD.gn index f4de6f1d8962eccc94228f58a6a69634e8eb30cb..cfa368199de739b59b38b2d72169be8e1f53b8ff 100644 --- a/test/fuzztest/builtinsarraybuffergetvaluefrombufferforfloat_fuzzer/BUILD.gn +++ b/test/fuzztest/builtinsarraybuffergetvaluefrombufferforfloat_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ##################################fuzztest##################################### ohos_fuzztest("BuiltinsArrayBufferGetValueFromBufferForFloatFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/builtinsarraybuffergetvaluefrombufferforfloat_fuzzer" diff --git a/test/fuzztest/builtinsarraybuffergetvaluefrombufferforinteger_fuzzer/BUILD.gn b/test/fuzztest/builtinsarraybuffergetvaluefrombufferforinteger_fuzzer/BUILD.gn index 4433ff6c5403dd6085b808c7d8bf0df0fce74bd6..3652f420fbe4ac59c384bc1a5efaea29851186f1 100644 --- a/test/fuzztest/builtinsarraybuffergetvaluefrombufferforinteger_fuzzer/BUILD.gn +++ b/test/fuzztest/builtinsarraybuffergetvaluefrombufferforinteger_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ##################################fuzztest##################################### ohos_fuzztest("BuiltinsArrayBufferGetValueFromBufferForIntegerFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/builtinsarraybuffergetvaluefrombufferforinteger_fuzzer" diff --git a/test/fuzztest/builtinsarraybufferisview_fuzzer/BUILD.gn b/test/fuzztest/builtinsarraybufferisview_fuzzer/BUILD.gn index a9b253214dd468d74fb4a4f1febc557dc91c28a7..45a50826b60cd3c05845808a6bfac29fabf3b374 100644 --- a/test/fuzztest/builtinsarraybufferisview_fuzzer/BUILD.gn +++ b/test/fuzztest/builtinsarraybufferisview_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ##################################fuzztest##################################### ohos_fuzztest("BuiltinsArrayBufferIsViewFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/builtinsarraybufferisview_fuzzer" diff --git a/test/fuzztest/builtinsarraybuffersetvalueinbufferforbyte_fuzzer/BUILD.gn b/test/fuzztest/builtinsarraybuffersetvalueinbufferforbyte_fuzzer/BUILD.gn index 4aa75e9f2df7908a4690fa22e8295c970908a224..9802f9837eea0a1dec899053d5c91e013f990346 100644 --- a/test/fuzztest/builtinsarraybuffersetvalueinbufferforbyte_fuzzer/BUILD.gn +++ b/test/fuzztest/builtinsarraybuffersetvalueinbufferforbyte_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ##################################fuzztest##################################### ohos_fuzztest("BuiltinsArrayBufferSetValueInBufferForByteFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/builtinsarraybuffersetvalueinbufferforbyte_fuzzer" diff --git a/test/fuzztest/builtinsarraybuffersetvalueinbufferforuint8clamped_fuzzer/BUILD.gn b/test/fuzztest/builtinsarraybuffersetvalueinbufferforuint8clamped_fuzzer/BUILD.gn index 25ab651cab978c8dac06e091f394058614c58978..1f4c7065f261aadb9e254bc0c96e07956080a20c 100644 --- a/test/fuzztest/builtinsarraybuffersetvalueinbufferforuint8clamped_fuzzer/BUILD.gn +++ b/test/fuzztest/builtinsarraybuffersetvalueinbufferforuint8clamped_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ##################################fuzztest##################################### ohos_fuzztest("BuiltinsArrayBufferSetValueInBufferForUint8ClampedFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/builtinsarraybuffersetvalueinbufferforuint8clamped_fuzzer" diff --git a/test/fuzztest/containersdequeforeach_fuzzer/BUILD.gn b/test/fuzztest/containersdequeforeach_fuzzer/BUILD.gn index 47a8df6a68c5631583cbd4e8a0d24186f069859f..c746d86dac7cfb301212b6ff5c5d2905059cf58e 100644 --- a/test/fuzztest/containersdequeforeach_fuzzer/BUILD.gn +++ b/test/fuzztest/containersdequeforeach_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersDequeForEachFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersdequeforeach_fuzzer" diff --git a/test/fuzztest/containersdequegetfirst_fuzzer/BUILD.gn b/test/fuzztest/containersdequegetfirst_fuzzer/BUILD.gn index 85dcca75b2506aaa82fa6b90e4a0a0e2b166c9e3..56b85816825092576b6ca344ed5f552ff7998b85 100644 --- a/test/fuzztest/containersdequegetfirst_fuzzer/BUILD.gn +++ b/test/fuzztest/containersdequegetfirst_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersDequeGetFirstFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersdequegetfirst_fuzzer" diff --git a/test/fuzztest/containersdequegetlast_fuzzer/BUILD.gn b/test/fuzztest/containersdequegetlast_fuzzer/BUILD.gn index 1ec8ab5b3df835109105c73d189f93eab1662adb..feb5715eb30154401802209c3b16871aaf781aba 100644 --- a/test/fuzztest/containersdequegetlast_fuzzer/BUILD.gn +++ b/test/fuzztest/containersdequegetlast_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersDequeGetLastFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersdequegetlast_fuzzer" diff --git a/test/fuzztest/containersdequehas_fuzzer/BUILD.gn b/test/fuzztest/containersdequehas_fuzzer/BUILD.gn index cb46e0b927dc0733f54d409ab132cadaa0ed9883..70f1fd02987fb64aa142fcf78bc7e086cd66a03e 100644 --- a/test/fuzztest/containersdequehas_fuzzer/BUILD.gn +++ b/test/fuzztest/containersdequehas_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersDequeHasFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersdequehas_fuzzer" diff --git a/test/fuzztest/containersdequeinsertend_fuzzer/BUILD.gn b/test/fuzztest/containersdequeinsertend_fuzzer/BUILD.gn index b9986020c99ed3243340cc457d003dc5024b18e1..14c7b8ef307e58110d7cc40cc053426fa275778d 100644 --- a/test/fuzztest/containersdequeinsertend_fuzzer/BUILD.gn +++ b/test/fuzztest/containersdequeinsertend_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersDequeInsertEndFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersdequeinsertend_fuzzer" diff --git a/test/fuzztest/containersdequeinsertfront_fuzzer/BUILD.gn b/test/fuzztest/containersdequeinsertfront_fuzzer/BUILD.gn index 6915ebca6916112168d517cc7e559d8fe6c69be5..405baad10f34ebf024412cc7e54e5ad805823c46 100644 --- a/test/fuzztest/containersdequeinsertfront_fuzzer/BUILD.gn +++ b/test/fuzztest/containersdequeinsertfront_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersDequeInsertFrontFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersdequeinsertfront_fuzzer" diff --git a/test/fuzztest/containersdequeiterator_fuzzer/BUILD.gn b/test/fuzztest/containersdequeiterator_fuzzer/BUILD.gn index 34a07c48466fb865a54fb8b84c4293428cd5a188..4736280b212aec0c2869e3f6db2e438c82454d86 100644 --- a/test/fuzztest/containersdequeiterator_fuzzer/BUILD.gn +++ b/test/fuzztest/containersdequeiterator_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersDequeIteratorFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersdequeiterator_fuzzer" diff --git a/test/fuzztest/containersdequepopfirst_fuzzer/BUILD.gn b/test/fuzztest/containersdequepopfirst_fuzzer/BUILD.gn index 98959271bd5731f238dd91681e853640c276f2eb..6d7a88ec93631c022ce0416b63dd9cabedae7bad 100644 --- a/test/fuzztest/containersdequepopfirst_fuzzer/BUILD.gn +++ b/test/fuzztest/containersdequepopfirst_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersDequePopFirstFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersdequepopfirst_fuzzer" diff --git a/test/fuzztest/containersdequepoplast_fuzzer/BUILD.gn b/test/fuzztest/containersdequepoplast_fuzzer/BUILD.gn index dc7a6ed9d6f0a415d4df4e8c5b47fd8d2fa68f7a..43cf08cd5db4daddb870c28cac1e19a7f09913bb 100644 --- a/test/fuzztest/containersdequepoplast_fuzzer/BUILD.gn +++ b/test/fuzztest/containersdequepoplast_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersDequePopLastFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersdequepoplast_fuzzer" diff --git a/test/fuzztest/containershashmap_fuzzer/BUILD.gn b/test/fuzztest/containershashmap_fuzzer/BUILD.gn index 6c535b722319a9cce60106d6a6c2013a3d3d26ef..ad52f6f1f8dab219f6b6f10e572f1af015c7e00e 100644 --- a/test/fuzztest/containershashmap_fuzzer/BUILD.gn +++ b/test/fuzztest/containershashmap_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersHashMapFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containershashmap_fuzzer" diff --git a/test/fuzztest/containershashmapclear_fuzzer/BUILD.gn b/test/fuzztest/containershashmapclear_fuzzer/BUILD.gn index dafcdd0520d5a13d51067cd7e5952c474274c345..d2e01bdf34857c9932b3747ec7c135dd590de9c8 100644 --- a/test/fuzztest/containershashmapclear_fuzzer/BUILD.gn +++ b/test/fuzztest/containershashmapclear_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersHashMapClearFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containershashmapclear_fuzzer" diff --git a/test/fuzztest/containershashmapentries_fuzzer/BUILD.gn b/test/fuzztest/containershashmapentries_fuzzer/BUILD.gn index 53b14b8e169cc1c3606de990af80f55a954f43d8..bc3589930f211adf5e0a60473aba15ad05a58588 100644 --- a/test/fuzztest/containershashmapentries_fuzzer/BUILD.gn +++ b/test/fuzztest/containershashmapentries_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersHashMapEntriesFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containershashmapentries_fuzzer" diff --git a/test/fuzztest/containershashmapforeach_fuzzer/BUILD.gn b/test/fuzztest/containershashmapforeach_fuzzer/BUILD.gn index de13d68cf0ef9faea75238244d1110f178ca4d64..190929165fb726f431e53e720136bb30a725d4e1 100644 --- a/test/fuzztest/containershashmapforeach_fuzzer/BUILD.gn +++ b/test/fuzztest/containershashmapforeach_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersHashMapForEachFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containershashmapforeach_fuzzer" diff --git a/test/fuzztest/containershashmapget_fuzzer/BUILD.gn b/test/fuzztest/containershashmapget_fuzzer/BUILD.gn index 6c8fee94cfa1d6891af413dca701deaefd66d976..e9875b272be9a57b80fff177d165ba7dc065ca5f 100644 --- a/test/fuzztest/containershashmapget_fuzzer/BUILD.gn +++ b/test/fuzztest/containershashmapget_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersHashMapGetFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containershashmapget_fuzzer" diff --git a/test/fuzztest/containershashmapgetlength_fuzzer/BUILD.gn b/test/fuzztest/containershashmapgetlength_fuzzer/BUILD.gn index 2c1d67beb6a3085a01f32d66ff3898ba3ff2bd7a..20e6e3fa3878ca633913396b6fd2318152f936a8 100644 --- a/test/fuzztest/containershashmapgetlength_fuzzer/BUILD.gn +++ b/test/fuzztest/containershashmapgetlength_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersHashMapGetLengthFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containershashmapgetlength_fuzzer" diff --git a/test/fuzztest/containershashmaphaskey_fuzzer/BUILD.gn b/test/fuzztest/containershashmaphaskey_fuzzer/BUILD.gn index 2d225a92565fe69f0988f7b4fff4ede0d54d9679..7c451756be1f460f87ff60fd93dbfb28bf0fdd8f 100644 --- a/test/fuzztest/containershashmaphaskey_fuzzer/BUILD.gn +++ b/test/fuzztest/containershashmaphaskey_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersHashMapHasKeyFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containershashmaphaskey_fuzzer" diff --git a/test/fuzztest/containershashmaphasvalue_fuzzer/BUILD.gn b/test/fuzztest/containershashmaphasvalue_fuzzer/BUILD.gn index 7cd26bc4558bef547d6c306f24ca85451806eba8..27c62471f32b209e83a869e48f221e96ef5a05c6 100644 --- a/test/fuzztest/containershashmaphasvalue_fuzzer/BUILD.gn +++ b/test/fuzztest/containershashmaphasvalue_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersHashMapHasValueFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containershashmaphasvalue_fuzzer" diff --git a/test/fuzztest/containershashmapisempty_fuzzer/BUILD.gn b/test/fuzztest/containershashmapisempty_fuzzer/BUILD.gn index c035bded2b8a2547a1a5874ac8598c9a9f1ab3f9..63833934fa523e47ba011afbae7d264ee4b99bcb 100644 --- a/test/fuzztest/containershashmapisempty_fuzzer/BUILD.gn +++ b/test/fuzztest/containershashmapisempty_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersHashMapIsEmptyFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containershashmapisempty_fuzzer" diff --git a/test/fuzztest/containershashmapkeys_fuzzer/BUILD.gn b/test/fuzztest/containershashmapkeys_fuzzer/BUILD.gn index 3e05a0ff99c218a9e1ebafcda53759f8c2bfefe7..c7e2453914888cad2fe408773b8c79d656ae9d14 100644 --- a/test/fuzztest/containershashmapkeys_fuzzer/BUILD.gn +++ b/test/fuzztest/containershashmapkeys_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersHashMapKeysFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containershashmapkeys_fuzzer" diff --git a/test/fuzztest/containershashmapremove_fuzzer/BUILD.gn b/test/fuzztest/containershashmapremove_fuzzer/BUILD.gn index d9c0043bb2e7043f9083a79a06ac231444d8d876..478dbf6048fe5eec8e13f6fcd45c1a43cddb24e4 100644 --- a/test/fuzztest/containershashmapremove_fuzzer/BUILD.gn +++ b/test/fuzztest/containershashmapremove_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersHashMapRemoveFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containershashmapremove_fuzzer" diff --git a/test/fuzztest/containershashmapreplace_fuzzer/BUILD.gn b/test/fuzztest/containershashmapreplace_fuzzer/BUILD.gn index 5fee3d06467bbacbb5252d0812ae040cfca7ddd2..ed8e23b79dd8ab103b8509cb473d6b2fc1fc083b 100644 --- a/test/fuzztest/containershashmapreplace_fuzzer/BUILD.gn +++ b/test/fuzztest/containershashmapreplace_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersHashMapReplaceFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containershashmapreplace_fuzzer" diff --git a/test/fuzztest/containershashmapset_fuzzer/BUILD.gn b/test/fuzztest/containershashmapset_fuzzer/BUILD.gn index 923b9fda3369556f3ec113b7d68bbbf1e4ccc910..b040bc0d34bcc4eda07f72ca99f6f02b99148402 100644 --- a/test/fuzztest/containershashmapset_fuzzer/BUILD.gn +++ b/test/fuzztest/containershashmapset_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersHashMapSetFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containershashmapset_fuzzer" diff --git a/test/fuzztest/containershashmapsetall_fuzzer/BUILD.gn b/test/fuzztest/containershashmapsetall_fuzzer/BUILD.gn index 6eebba493d1597ae68abed41423f53fd2e150deb..d980b4f97aed59b024baeaf210624e1337d8d98f 100644 --- a/test/fuzztest/containershashmapsetall_fuzzer/BUILD.gn +++ b/test/fuzztest/containershashmapsetall_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersHashMapSetAllFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containershashmapsetall_fuzzer" diff --git a/test/fuzztest/containershashmapvalues_fuzzer/BUILD.gn b/test/fuzztest/containershashmapvalues_fuzzer/BUILD.gn index c64f1a705766c6b82ea10dc91bc5c5d36ce80e5e..99ae37dc7ed7dfe51a52a995366873345be1ecee 100644 --- a/test/fuzztest/containershashmapvalues_fuzzer/BUILD.gn +++ b/test/fuzztest/containershashmapvalues_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersHashMapValuesFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containershashmapvalues_fuzzer" diff --git a/test/fuzztest/containershashsetadd_fuzzer/BUILD.gn b/test/fuzztest/containershashsetadd_fuzzer/BUILD.gn index 75314cf1eac73350774d23e9e8cc147bda320fa1..9ec596f94af884e39237f8b281525c90b84aed1b 100644 --- a/test/fuzztest/containershashsetadd_fuzzer/BUILD.gn +++ b/test/fuzztest/containershashsetadd_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersHashSetAddFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containershashsetadd_fuzzer" diff --git a/test/fuzztest/containershashsetclear_fuzzer/BUILD.gn b/test/fuzztest/containershashsetclear_fuzzer/BUILD.gn index 5fbb9ba23e780192d57ac0896e0c0361dcd36ff3..27db9f8075dd6554f064917be927206ac054716a 100644 --- a/test/fuzztest/containershashsetclear_fuzzer/BUILD.gn +++ b/test/fuzztest/containershashsetclear_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersHashSetClearFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containershashsetclear_fuzzer" diff --git a/test/fuzztest/containershashsetconstructor_fuzzer/BUILD.gn b/test/fuzztest/containershashsetconstructor_fuzzer/BUILD.gn index 1d370128f625c69b5f9aecb659b4c93ad5c5f62c..8c8faa4c5d378274c73ddc4dad844b308469a4af 100644 --- a/test/fuzztest/containershashsetconstructor_fuzzer/BUILD.gn +++ b/test/fuzztest/containershashsetconstructor_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersHashSetConstructorFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containershashsetconstructor_fuzzer" diff --git a/test/fuzztest/containershashsetentries_fuzzer/BUILD.gn b/test/fuzztest/containershashsetentries_fuzzer/BUILD.gn index 939b5103fd0f79e7a732c918a8d1d245ad9d719e..b88aaf71c8d9ceec008bdad44073bc05449dc751 100644 --- a/test/fuzztest/containershashsetentries_fuzzer/BUILD.gn +++ b/test/fuzztest/containershashsetentries_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersHashSetEntriesFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containershashsetentries_fuzzer" diff --git a/test/fuzztest/containershashsetforeach_fuzzer/BUILD.gn b/test/fuzztest/containershashsetforeach_fuzzer/BUILD.gn index ad148185f9c053f7662f5d4546d500192ee55acb..eadc893319f46ffbfb22dbb3907408f819fc8557 100644 --- a/test/fuzztest/containershashsetforeach_fuzzer/BUILD.gn +++ b/test/fuzztest/containershashsetforeach_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersHashSetForEachFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containershashsetforeach_fuzzer" diff --git a/test/fuzztest/containershashsetgetlength_fuzzer/BUILD.gn b/test/fuzztest/containershashsetgetlength_fuzzer/BUILD.gn index 6884596661057613c3fffd8d35e9ca1394e9b01e..9e21d8fb6e827602408ed97c28b6d9597b573e32 100644 --- a/test/fuzztest/containershashsetgetlength_fuzzer/BUILD.gn +++ b/test/fuzztest/containershashsetgetlength_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersHashSetGetLengthFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containershashsetgetlength_fuzzer" diff --git a/test/fuzztest/containershashsethas_fuzzer/BUILD.gn b/test/fuzztest/containershashsethas_fuzzer/BUILD.gn index b6958314e55da9d644e1c6cb1947c30f24288b6a..9a0d4c0df9f7a9011abcdd846130ed6024c22ea8 100644 --- a/test/fuzztest/containershashsethas_fuzzer/BUILD.gn +++ b/test/fuzztest/containershashsethas_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersHashSetHasFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containershashsethas_fuzzer" diff --git a/test/fuzztest/containershashsetisempty_fuzzer/BUILD.gn b/test/fuzztest/containershashsetisempty_fuzzer/BUILD.gn index addb9fba6eae7c01f479dbc4325754c4a5046d7e..8b28992273dfe2cf1559fa652e51debc9d4d336d 100644 --- a/test/fuzztest/containershashsetisempty_fuzzer/BUILD.gn +++ b/test/fuzztest/containershashsetisempty_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersHashSetIsEmptyFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containershashsetisempty_fuzzer" diff --git a/test/fuzztest/containershashsetremove_fuzzer/BUILD.gn b/test/fuzztest/containershashsetremove_fuzzer/BUILD.gn index 359ca9c07fe93ead502a9f1b590fe55469046400..98c7746fa4a0842e03ef65900e32c2021e00c83b 100644 --- a/test/fuzztest/containershashsetremove_fuzzer/BUILD.gn +++ b/test/fuzztest/containershashsetremove_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersHashSetRemoveFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containershashsetremove_fuzzer" diff --git a/test/fuzztest/containershashsetvalues_fuzzer/BUILD.gn b/test/fuzztest/containershashsetvalues_fuzzer/BUILD.gn index b00f6bc9e28b2006f1fceda1deff00c9758b95c1..c544eb468464fa14b07c3aa5b525b3b1137766dc 100644 --- a/test/fuzztest/containershashsetvalues_fuzzer/BUILD.gn +++ b/test/fuzztest/containershashsetvalues_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersHashSetValuesFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containershashsetvalues_fuzzer" diff --git a/test/fuzztest/containerslightweightmap_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightmap_fuzzer/BUILD.gn index 0164080ceae164bef3c79d16a53ea5d0422a05f2..8bdd626aa7b036fa444d822b3f89a4380f10060b 100644 --- a/test/fuzztest/containerslightweightmap_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightmap_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightMapFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightmap_fuzzer" diff --git a/test/fuzztest/containerslightweightmapat_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightmapat_fuzzer/BUILD.gn index 4c29e5fd12e97b80866d60084db2eb972121cb63..c4a0b3f8b78935ac2f1c522314591c2a11e21441 100644 --- a/test/fuzztest/containerslightweightmapat_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightmapat_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightMapAtFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightmapat_fuzzer" diff --git a/test/fuzztest/containerslightweightmapclear_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightmapclear_fuzzer/BUILD.gn index a7284df9b02a519877d17086cc33fd60b2b15b7e..b9aad0dc78d5463993296044946efe4b91132d47 100644 --- a/test/fuzztest/containerslightweightmapclear_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightmapclear_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightMapClearFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightmapclear_fuzzer" diff --git a/test/fuzztest/containerslightweightmapentries_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightmapentries_fuzzer/BUILD.gn index 6f9ccac8db38fcc41e995069f761e943c8594196..0ef366c20dd08821890cf6fc5e2e5a2b78a009b2 100644 --- a/test/fuzztest/containerslightweightmapentries_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightmapentries_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightMapEntriesFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightmapentries_fuzzer" diff --git a/test/fuzztest/containerslightweightmapforeach_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightmapforeach_fuzzer/BUILD.gn index 5819474c8a4f34200df6b864c510be58aa689d62..021175e8b5e367b99e9358778b3669c11fab3903 100644 --- a/test/fuzztest/containerslightweightmapforeach_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightmapforeach_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightMapForEachFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightmapforeach_fuzzer" diff --git a/test/fuzztest/containerslightweightmaphasall_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightmaphasall_fuzzer/BUILD.gn index 6dfb1d04c91ac0ecf4bbbddd68d27cf8b6bc7052..85bc4af77e5d97f7d3e64c1e3d48f78f7203dd0a 100644 --- a/test/fuzztest/containerslightweightmaphasall_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightmaphasall_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightMapHasAllFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightmaphasall_fuzzer" diff --git a/test/fuzztest/containerslightweightmapisempty_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightmapisempty_fuzzer/BUILD.gn index 749a67d57f57a4cd5ab5bc35365122040b415a1d..4080b430a4dd2a12ea4ef031a15149f2ab34e17b 100644 --- a/test/fuzztest/containerslightweightmapisempty_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightmapisempty_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightMapIsEmptyFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightmapisempty_fuzzer" diff --git a/test/fuzztest/containerslightweightmapkeys_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightmapkeys_fuzzer/BUILD.gn index d0450c9a411f175b37e2533d189b7503fc2152c1..70bb26b595638e260bf80e45078c1074e4859525 100644 --- a/test/fuzztest/containerslightweightmapkeys_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightmapkeys_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightMapKeysFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightmapkeys_fuzzer" diff --git a/test/fuzztest/containerslightweightmaplength_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightmaplength_fuzzer/BUILD.gn index 00983e33ec7148f89e78b91469bf07617818472d..b4f91d6e3ccabf604181a8b7b0065632495789d9 100644 --- a/test/fuzztest/containerslightweightmaplength_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightmaplength_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightMapLengthFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightmaplength_fuzzer" diff --git a/test/fuzztest/containerslightweightmapremove_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightmapremove_fuzzer/BUILD.gn index 7c535163413caaba2c0e7ec2f2c193ff832f3d11..710dec5b850b27a99af19e0ad0d844e547f203f5 100644 --- a/test/fuzztest/containerslightweightmapremove_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightmapremove_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightMapRemoveFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightmapremove_fuzzer" diff --git a/test/fuzztest/containerslightweightmapsetall_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightmapsetall_fuzzer/BUILD.gn index f7f7a12cdd64987effd2691f4b62219c87c11f69..e96528e9043127aafab6ea009306c8f44654bad0 100644 --- a/test/fuzztest/containerslightweightmapsetall_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightmapsetall_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightMapSetAllFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightmapsetall_fuzzer" diff --git a/test/fuzztest/containerslightweightmapsetandget_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightmapsetandget_fuzzer/BUILD.gn index 771f04ef8e1fa5fbe8ec42cf6bd196c5a32ff9f6..505187dec054d311ee41bc0f027272a2e033a40d 100644 --- a/test/fuzztest/containerslightweightmapsetandget_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightmapsetandget_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightMapSetAndGetFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightmapsetandget_fuzzer" diff --git a/test/fuzztest/containerslightweightmaptostring_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightmaptostring_fuzzer/BUILD.gn index b99dd8ec4ec1e792806c62e349348a9c4a889cc0..4e2e07c515aa13b77e2d3d534e6a953478b07aef 100644 --- a/test/fuzztest/containerslightweightmaptostring_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightmaptostring_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightMapToStringFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightmaptostring_fuzzer" diff --git a/test/fuzztest/containerslightweightmapvalues_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightmapvalues_fuzzer/BUILD.gn index c4c8e84ca59e3ec1366803ab587265c675e6b33a..a3c90b9c45fd1a82d28d5dd3d6f8b880fde531e3 100644 --- a/test/fuzztest/containerslightweightmapvalues_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightmapvalues_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightMapValuesFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightmapvalues_fuzzer" diff --git a/test/fuzztest/containerslightweightset_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightset_fuzzer/BUILD.gn index 00cdaf4556cd755fc8dbbce2bd363eb09bd820e8..761eb2ac234172ae5edd5ec8d9eb93034227affb 100644 --- a/test/fuzztest/containerslightweightset_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightset_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightSetFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightset_fuzzer" diff --git a/test/fuzztest/containerslightweightsetadd_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightsetadd_fuzzer/BUILD.gn index c7833c7f2bf45888413838e72f420c1b02c58a9b..884ff389d15e530cde36e8b9ec9f6c251d73f0f7 100644 --- a/test/fuzztest/containerslightweightsetadd_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightsetadd_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightSetAddFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightsetadd_fuzzer" diff --git a/test/fuzztest/containerslightweightsetaddall_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightsetaddall_fuzzer/BUILD.gn index 5253f36ee3e4ff9ac468501aade16f96134c7d7d..872e7c2a17d2b81e28967a9cc801b4807c918768 100644 --- a/test/fuzztest/containerslightweightsetaddall_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightsetaddall_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightSetAddAllFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightsetaddall_fuzzer" diff --git a/test/fuzztest/containerslightweightsetclear_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightsetclear_fuzzer/BUILD.gn index 71b298d5f1b639c890ff3db7f7bbec40215e3c62..cf47264b7699faa67c357225072677da943c5aa5 100644 --- a/test/fuzztest/containerslightweightsetclear_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightsetclear_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightSetClearFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightsetclear_fuzzer" diff --git a/test/fuzztest/containerslightweightsetentries_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightsetentries_fuzzer/BUILD.gn index 6a960fedff842a195d09643a5a911c42e2ee1ea3..4ace0207079fad44d1cdd422178c0d291cc99d3c 100644 --- a/test/fuzztest/containerslightweightsetentries_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightsetentries_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightSetEntriesFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightsetentries_fuzzer" diff --git a/test/fuzztest/containerslightweightsetequal_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightsetequal_fuzzer/BUILD.gn index 7b2989b2bbccfa4af69c4a324e953d1b8c9c53fd..8485ba501ff41ad7a886b6b581227c2a64837123 100644 --- a/test/fuzztest/containerslightweightsetequal_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightsetequal_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightSetEqualFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightsetequal_fuzzer" diff --git a/test/fuzztest/containerslightweightsetforeach_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightsetforeach_fuzzer/BUILD.gn index 9ea1a479c035fb42d1b97d77d537d44501e32d68..b81294db8c4ffac8c957e8de9059be7129995017 100644 --- a/test/fuzztest/containerslightweightsetforeach_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightsetforeach_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightSetForeachFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightsetforeach_fuzzer" diff --git a/test/fuzztest/containerslightweightsetgetindexof_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightsetgetindexof_fuzzer/BUILD.gn index d73b9a5f19da2778c4a1a2530a6f51ab2c27c624..924fbbe5d4fc77ea629393ef2f8ac161e083d216 100644 --- a/test/fuzztest/containerslightweightsetgetindexof_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightsetgetindexof_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightSetGetIndexOfFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightsetgetindexof_fuzzer" diff --git a/test/fuzztest/containerslightweightsetgetiteratorobj_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightsetgetiteratorobj_fuzzer/BUILD.gn index 00125fc48de48d67f8f40d3b18bed9ae66f36188..ae03d4d3bd0589a97d0e6daff156a9754de4527a 100644 --- a/test/fuzztest/containerslightweightsetgetiteratorobj_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightsetgetiteratorobj_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightSetGetIteratorObjFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightsetgetiteratorobj_fuzzer" diff --git a/test/fuzztest/containerslightweightsetgetsize_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightsetgetsize_fuzzer/BUILD.gn index 30a2fe3d32d2136213fdcdec0b7ef9bf8ad70937..fdd0ff36ed14ea35e8a59a28ff34e4cc70c69585 100644 --- a/test/fuzztest/containerslightweightsetgetsize_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightsetgetsize_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightSetGetSizeFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightsetgetsize_fuzzer" diff --git a/test/fuzztest/containerslightweightsetgetvalueat_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightsetgetvalueat_fuzzer/BUILD.gn index 0a6d91ab76bdfa4d58f025503393b4f7a289a8a0..70b4a9b7e42b44e5a9dc7b5ec7f4b41ff4a32607 100644 --- a/test/fuzztest/containerslightweightsetgetvalueat_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightsetgetvalueat_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightSetGetValueAtFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightsetgetvalueat_fuzzer" diff --git a/test/fuzztest/containerslightweightsethas_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightsethas_fuzzer/BUILD.gn index f8ebb4d6033906e9cfbe6104125f0e3caaafc049..56d9c5b401c01eff6806a63c762706d4540fb710 100644 --- a/test/fuzztest/containerslightweightsethas_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightsethas_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightSetHasFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightsethas_fuzzer" diff --git a/test/fuzztest/containerslightweightsethasall_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightsethasall_fuzzer/BUILD.gn index 23e436ed30f274d6f6d935070c059bbfc94297ad..a4a69daf430a8b3cc59c1bfb2f146e7f7c32485c 100644 --- a/test/fuzztest/containerslightweightsethasall_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightsethasall_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightSetHasAllFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightsethasall_fuzzer" diff --git a/test/fuzztest/containerslightweightsethashash_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightsethashash_fuzzer/BUILD.gn index 90cc844384835a234d241446e7df337d93c5fd19..27ece0783a1045690aea94ace6442642332d4350 100644 --- a/test/fuzztest/containerslightweightsethashash_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightsethashash_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightSetHasHashFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightsethashash_fuzzer" diff --git a/test/fuzztest/containerslightweightsetisempty_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightsetisempty_fuzzer/BUILD.gn index 6f66326500c5fa9038c7e264766f39f814170e03..ac532ffc886073bcd156270c339aa8fa0be99a8c 100644 --- a/test/fuzztest/containerslightweightsetisempty_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightsetisempty_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightSetIsEmptyFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightsetisempty_fuzzer" diff --git a/test/fuzztest/containerslightweightsetremove_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightsetremove_fuzzer/BUILD.gn index 562ca1e2dacb027b019aa00ed2227427fbc6356b..3500e13da73d7b6bc48241132df528b91c61537e 100644 --- a/test/fuzztest/containerslightweightsetremove_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightsetremove_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightSetRemoveFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightsetremove_fuzzer" diff --git a/test/fuzztest/containerslightweightsettoarray_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightsettoarray_fuzzer/BUILD.gn index cf2deb19a59df68f710250734bfcb56cf95ce8f9..e1d6881abf8c039295ed58cca7c27e6ad7457c53 100644 --- a/test/fuzztest/containerslightweightsettoarray_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightsettoarray_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightSetToArrayFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightsettoarray_fuzzer" diff --git a/test/fuzztest/containerslightweightsettostring_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightsettostring_fuzzer/BUILD.gn index 93a480292b843f7a39b470ee90f4856bb9bdd0e3..9f0213b81dbd1ca2e5310b2a9a9031eb5858530f 100644 --- a/test/fuzztest/containerslightweightsettostring_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightsettostring_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightSetToStringFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightsettostring_fuzzer" diff --git a/test/fuzztest/containerslightweightsetvalues_fuzzer/BUILD.gn b/test/fuzztest/containerslightweightsetvalues_fuzzer/BUILD.gn index 81b70df0b603fc1d4742784eed74ce2dd5b26a8b..8f6e47a660906117f92684f0741d243983622f42 100644 --- a/test/fuzztest/containerslightweightsetvalues_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslightweightsetvalues_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLightWeightSetValuesFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslightweightsetvalues_fuzzer" diff --git a/test/fuzztest/containerslinkedlistadd_fuzzer/BUILD.gn b/test/fuzztest/containerslinkedlistadd_fuzzer/BUILD.gn index 54794044670dd5c5194e8ea5990d3a7c80959469..a5e6367291c39b5cbd3631b20572aa2252d33b19 100644 --- a/test/fuzztest/containerslinkedlistadd_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslinkedlistadd_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLinkedListAddFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslinkedlistadd_fuzzer" diff --git a/test/fuzztest/containerslinkedlistaddfirst_fuzzer/BUILD.gn b/test/fuzztest/containerslinkedlistaddfirst_fuzzer/BUILD.gn index ec902904eb644b3375bcbe40592a5702b648037d..409022525477fb27609ae583b66115a1739e877c 100644 --- a/test/fuzztest/containerslinkedlistaddfirst_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslinkedlistaddfirst_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLinkedListAddFirstFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslinkedlistaddfirst_fuzzer" diff --git a/test/fuzztest/containerslinkedlistclear_fuzzer/BUILD.gn b/test/fuzztest/containerslinkedlistclear_fuzzer/BUILD.gn index 395007f385b5bbaebfa433f71d9c8a028d6e592c..326de2393c7d3d3baf417f08617389430ea13724 100644 --- a/test/fuzztest/containerslinkedlistclear_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslinkedlistclear_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLinkedListClearFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslinkedlistclear_fuzzer" diff --git a/test/fuzztest/containerslinkedlistclone_fuzzer/BUILD.gn b/test/fuzztest/containerslinkedlistclone_fuzzer/BUILD.gn index 61e009b5074ef35541c616ff1ae8e5a4dccadddb..008738cb0ae8f60d52be42ec9a7fc5a321d36119 100644 --- a/test/fuzztest/containerslinkedlistclone_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslinkedlistclone_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLinkedListCloneFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslinkedlistclone_fuzzer" diff --git a/test/fuzztest/containerslinkedlistconverttoarray_fuzzer/BUILD.gn b/test/fuzztest/containerslinkedlistconverttoarray_fuzzer/BUILD.gn index 8fafc91dfdb9392dd87a0827169f4def1667cdc8..c6a47fd5149ee5b7d005d78096f7cc3172386e3f 100644 --- a/test/fuzztest/containerslinkedlistconverttoarray_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslinkedlistconverttoarray_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLinkedListConvertToArrayFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslinkedlistconverttoarray_fuzzer" diff --git a/test/fuzztest/containerslinkedlistforeach_fuzzer/BUILD.gn b/test/fuzztest/containerslinkedlistforeach_fuzzer/BUILD.gn index 0c091e7335fd228e1fdaacd21c4711c3be9c4e19..f037a08c59ff862960298d7fc425ec0ffb1662b5 100644 --- a/test/fuzztest/containerslinkedlistforeach_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslinkedlistforeach_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLinkedListForEachFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslinkedlistforeach_fuzzer" diff --git a/test/fuzztest/containerslinkedlistget_fuzzer/BUILD.gn b/test/fuzztest/containerslinkedlistget_fuzzer/BUILD.gn index b36d077b0628d6a7ea08ea2d76b6c4b29fb27435..0188b141672dd9fe16f3392ca7f8cb960d698225 100644 --- a/test/fuzztest/containerslinkedlistget_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslinkedlistget_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLinkedListGetFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslinkedlistget_fuzzer" diff --git a/test/fuzztest/containerslinkedlistgetfirst_fuzzer/BUILD.gn b/test/fuzztest/containerslinkedlistgetfirst_fuzzer/BUILD.gn index 211664cd3a586a3a018a04331f985c9129901d32..b0d239d035e7c311475f83bde0bc551bf96fe8e0 100644 --- a/test/fuzztest/containerslinkedlistgetfirst_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslinkedlistgetfirst_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLinkedListGetFirstFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslinkedlistgetfirst_fuzzer" diff --git a/test/fuzztest/containerslinkedlistgetindexof_fuzzer/BUILD.gn b/test/fuzztest/containerslinkedlistgetindexof_fuzzer/BUILD.gn index 6827d239756483fed871b264cf49a330f2db4161..48032c6e635858f79e8d4d0fb2ac84f38cdabff6 100644 --- a/test/fuzztest/containerslinkedlistgetindexof_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslinkedlistgetindexof_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLinkedListGetIndexOfFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslinkedlistgetindexof_fuzzer" diff --git a/test/fuzztest/containerslinkedlistgetiteratorobj_fuzzer/BUILD.gn b/test/fuzztest/containerslinkedlistgetiteratorobj_fuzzer/BUILD.gn index 9a6894c76ec0d05f15de44159a6f45645145ee93..8e47420f3d1f9f9daab12af1c881888e6885b512 100644 --- a/test/fuzztest/containerslinkedlistgetiteratorobj_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslinkedlistgetiteratorobj_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLinkedListGetIteratorObjFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslinkedlistgetiteratorobj_fuzzer" diff --git a/test/fuzztest/containerslinkedlistgetlast_fuzzer/BUILD.gn b/test/fuzztest/containerslinkedlistgetlast_fuzzer/BUILD.gn index aa48d1a670934f25944407de37cc6b5398beb719..aed3d783f7bee695aa3d60c07d8c515f67889546 100644 --- a/test/fuzztest/containerslinkedlistgetlast_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslinkedlistgetlast_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLinkedListGetLastFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslinkedlistgetlast_fuzzer" diff --git a/test/fuzztest/containerslinkedlistgetlastindexof_fuzzer/BUILD.gn b/test/fuzztest/containerslinkedlistgetlastindexof_fuzzer/BUILD.gn index ced374ed16eea7d471d55a0de3eeb02f202d6884..ae8fd165e1298649d7d64e34e7988bc12404161c 100644 --- a/test/fuzztest/containerslinkedlistgetlastindexof_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslinkedlistgetlastindexof_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLinkedListGetLastIndexOfFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslinkedlistgetlastindexof_fuzzer" diff --git a/test/fuzztest/containerslinkedlisthas_fuzzer/BUILD.gn b/test/fuzztest/containerslinkedlisthas_fuzzer/BUILD.gn index 2c76015d4d12fd11fab9f15085f9ccd740869034..a64bf15e842886decd57539138f09636dd20779a 100644 --- a/test/fuzztest/containerslinkedlisthas_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslinkedlisthas_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLinkedListHasFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslinkedlisthas_fuzzer" diff --git a/test/fuzztest/containerslinkedlistinsert_fuzzer/BUILD.gn b/test/fuzztest/containerslinkedlistinsert_fuzzer/BUILD.gn index dfa720307ff4dd4e1f2e3fa6187f42a68a610fc2..b64c3284275fa9d07395d6bb98ac402ed5ca81e7 100644 --- a/test/fuzztest/containerslinkedlistinsert_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslinkedlistinsert_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLinkedListInsertFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslinkedlistinsert_fuzzer" diff --git a/test/fuzztest/containerslinkedlistlength_fuzzer/BUILD.gn b/test/fuzztest/containerslinkedlistlength_fuzzer/BUILD.gn index 01961104a28dc505da8ecfae0ac11ded4bcf0cd2..c633b2864ced4f10cc8a0654c62b7f0beb5bd45a 100644 --- a/test/fuzztest/containerslinkedlistlength_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslinkedlistlength_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLinkedListLengthFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslinkedlistlength_fuzzer" diff --git a/test/fuzztest/containerslinkedlistremove_fuzzer/BUILD.gn b/test/fuzztest/containerslinkedlistremove_fuzzer/BUILD.gn index 1a6695c2f63bd6ce2e42653aaf32a6711f0b6692..06931a2ceb250a11b3ee54e478fa3be298959d7f 100644 --- a/test/fuzztest/containerslinkedlistremove_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslinkedlistremove_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLinkedListRemoveFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslinkedlistremove_fuzzer" diff --git a/test/fuzztest/containerslinkedlistremovebyindex_fuzzer/BUILD.gn b/test/fuzztest/containerslinkedlistremovebyindex_fuzzer/BUILD.gn index 276608482592a6d065d7eafbce4c77b209f69940..326ec1467cd7002ac587ff5fc8bc2f3ef7de346b 100644 --- a/test/fuzztest/containerslinkedlistremovebyindex_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslinkedlistremovebyindex_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLinkedListRemoveByIndexFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslinkedlistremovebyindex_fuzzer" diff --git a/test/fuzztest/containerslinkedlistremovefirst_fuzzer/BUILD.gn b/test/fuzztest/containerslinkedlistremovefirst_fuzzer/BUILD.gn index d727892bf9ca837ed4650261b123e03e0706de11..23361015e5e4b625f1363a98da24ebe5a680c831 100644 --- a/test/fuzztest/containerslinkedlistremovefirst_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslinkedlistremovefirst_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLinkedListRemoveFirstFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslinkedlistremovefirst_fuzzer" diff --git a/test/fuzztest/containerslinkedlistremovefirstfound_fuzzer/BUILD.gn b/test/fuzztest/containerslinkedlistremovefirstfound_fuzzer/BUILD.gn index b834ced618c00b7e526e5216a532aabb8b4a8641..61f94af0d8ac145f99df3affc873bb60f65c44d4 100644 --- a/test/fuzztest/containerslinkedlistremovefirstfound_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslinkedlistremovefirstfound_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLinkedListRemoveFirstFoundFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslinkedlistremovefirstfound_fuzzer" diff --git a/test/fuzztest/containerslinkedlistremovelast_fuzzer/BUILD.gn b/test/fuzztest/containerslinkedlistremovelast_fuzzer/BUILD.gn index ad199b7d7df8078cc26cd17c94a3c5ef079b4154..72cc0cc4d677a8447ede6bda643a5820fb75cc1c 100644 --- a/test/fuzztest/containerslinkedlistremovelast_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslinkedlistremovelast_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLinkedListRemoveLastFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslinkedlistremovelast_fuzzer" diff --git a/test/fuzztest/containerslinkedlistremovelastfound_fuzzer/BUILD.gn b/test/fuzztest/containerslinkedlistremovelastfound_fuzzer/BUILD.gn index 6aca6e3763e9cb8071bbf11c8498389bd55c86a2..0309c8c51ebc6c12e8446bec1d3cd06206dd74ed 100644 --- a/test/fuzztest/containerslinkedlistremovelastfound_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslinkedlistremovelastfound_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLinkedListRemoveLastFoundFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslinkedlistremovelastfound_fuzzer" diff --git a/test/fuzztest/containerslinkedlistset_fuzzer/BUILD.gn b/test/fuzztest/containerslinkedlistset_fuzzer/BUILD.gn index 29d92cfc739f62128712fd46e2c22cec045c6088..82cf5b6f0ebb4802d198d929410a930f5e8bfd2e 100644 --- a/test/fuzztest/containerslinkedlistset_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslinkedlistset_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersLinkedListSetFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslinkedlistset_fuzzer" diff --git a/test/fuzztest/containerslistadd_fuzzer/BUILD.gn b/test/fuzztest/containerslistadd_fuzzer/BUILD.gn index beeeac8eac699a60eb6e4011430f02d65239b05c..99212a38eaa6c75eda9e8699d52c314a16c06861 100644 --- a/test/fuzztest/containerslistadd_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslistadd_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainerslistAddFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslistadd_fuzzer" diff --git a/test/fuzztest/containerslistclear_fuzzer/BUILD.gn b/test/fuzztest/containerslistclear_fuzzer/BUILD.gn index ced485bde838552dff2182fff93bacdb7ec45a3e..a11d73d1ed2b9631f51c7dd87ffe535095b58f7b 100644 --- a/test/fuzztest/containerslistclear_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslistclear_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainerslistClearFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslistclear_fuzzer" diff --git a/test/fuzztest/containerslistconstructor_fuzzer/BUILD.gn b/test/fuzztest/containerslistconstructor_fuzzer/BUILD.gn index 2a4419f4afb9bfeef9b1f6fe79e265ca934cd038..5d4e2d1143fa4aa57695929164fbc15ef3ecbccd 100644 --- a/test/fuzztest/containerslistconstructor_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslistconstructor_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainerslistConStructorFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslistconstructor_fuzzer" diff --git a/test/fuzztest/containerslistconverttoarray_fuzzer/BUILD.gn b/test/fuzztest/containerslistconverttoarray_fuzzer/BUILD.gn index d33dfdbbd218edf2cf13e8865b4838e6c75f64a1..9048cb23983cf580b5d5206769171e9cf73fd7e9 100644 --- a/test/fuzztest/containerslistconverttoarray_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslistconverttoarray_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainerslistConvertToArrayFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslistconverttoarray_fuzzer" diff --git a/test/fuzztest/containerslistequal_fuzzer/BUILD.gn b/test/fuzztest/containerslistequal_fuzzer/BUILD.gn index 46e6192518082346a8887bcf32962b2220c69d2a..f0187b59c8840c8e89a224a661ec05b82ee25607 100644 --- a/test/fuzztest/containerslistequal_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslistequal_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainerslistEqualFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslistequal_fuzzer" diff --git a/test/fuzztest/containerslistforeach_fuzzer/BUILD.gn b/test/fuzztest/containerslistforeach_fuzzer/BUILD.gn index d02a45ea2ff8166ba8f21bce40c98fb236a48dac..062283efddf6d8137409eb6aa8c11a8af24a0642 100644 --- a/test/fuzztest/containerslistforeach_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslistforeach_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainerslistForEachFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslistforeach_fuzzer" diff --git a/test/fuzztest/containerslistget_fuzzer/BUILD.gn b/test/fuzztest/containerslistget_fuzzer/BUILD.gn index 70e1ce0b908fa8ce6b8883a96dcc6df56a75dfd8..69f3acc45559e92f2b71ba9e8fce72388f790db2 100644 --- a/test/fuzztest/containerslistget_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslistget_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainerslistGetFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslistget_fuzzer" diff --git a/test/fuzztest/containerslistgetindexof_fuzzer/BUILD.gn b/test/fuzztest/containerslistgetindexof_fuzzer/BUILD.gn index 7205e6f96500b1112725407aa542e12231617a47..f8b4d05c0cf9810d7605b7979fe16d1d4b99903f 100644 --- a/test/fuzztest/containerslistgetindexof_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslistgetindexof_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainerslistGetIndexOfFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslistgetindexof_fuzzer" diff --git a/test/fuzztest/containerslistgetiteratorobj_fuzzer/BUILD.gn b/test/fuzztest/containerslistgetiteratorobj_fuzzer/BUILD.gn index 4c8340af34e5db068959f141f5e33dc7ff0f97b9..2015b5a09e145a3cfabf3eb577f19af8d8ffdeb7 100644 --- a/test/fuzztest/containerslistgetiteratorobj_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslistgetiteratorobj_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainerslistGetIteratorObjFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslistgetiteratorobj_fuzzer" diff --git a/test/fuzztest/containerslistgetlastindexof_fuzzer/BUILD.gn b/test/fuzztest/containerslistgetlastindexof_fuzzer/BUILD.gn index 976646e12e0b0bf3b03fede189c95db3e2d7aa86..c13df0b7dd3f44f36189deb31a9eb54e17794776 100644 --- a/test/fuzztest/containerslistgetlastindexof_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslistgetlastindexof_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainerslistGetLastIndexOfFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslistgetlastindexof_fuzzer" diff --git a/test/fuzztest/containerslistgetsublist_fuzzer/BUILD.gn b/test/fuzztest/containerslistgetsublist_fuzzer/BUILD.gn index 036cbeb2094d34aaf47447f3a46230f0a8921bf2..9662b30e8b89d3833464b0066b597e2c55a562f6 100644 --- a/test/fuzztest/containerslistgetsublist_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslistgetsublist_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainerslistGetSubListFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslistgetsublist_fuzzer" diff --git a/test/fuzztest/containerslisthas_fuzzer/BUILD.gn b/test/fuzztest/containerslisthas_fuzzer/BUILD.gn index 155f93df37c2df6dcdbd72e0d0250be827ee4a89..835b7827d644026c66754c877d4f86d20af05150 100644 --- a/test/fuzztest/containerslisthas_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslisthas_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainerslistHasFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslisthas_fuzzer" diff --git a/test/fuzztest/containerslistinsert_fuzzer/BUILD.gn b/test/fuzztest/containerslistinsert_fuzzer/BUILD.gn index ea75a1e18eb89a30da56961a75c1622f51790567..e46889d6a6c095653825337faaa5864f68ac95be 100644 --- a/test/fuzztest/containerslistinsert_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslistinsert_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainerslistInsertFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslistinsert_fuzzer" diff --git a/test/fuzztest/containerslistisempty_fuzzer/BUILD.gn b/test/fuzztest/containerslistisempty_fuzzer/BUILD.gn index f3e8c24c42e79f4c1b8e9b4a9c9264dc7fb5c82c..d0dd985b7ae4d9b1d2224bddae131fe6036f7dcd 100644 --- a/test/fuzztest/containerslistisempty_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslistisempty_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainerslistIsemptyFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslistisempty_fuzzer" diff --git a/test/fuzztest/containerslistlength_fuzzer/BUILD.gn b/test/fuzztest/containerslistlength_fuzzer/BUILD.gn index 42c03f20849dde5a5f82ba01e1bb6287f745d9b8..1c31aa6e838e5817668f252dd522d4bdd9f3b970 100644 --- a/test/fuzztest/containerslistlength_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslistlength_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainerslistLengthFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslistlength_fuzzer" diff --git a/test/fuzztest/containerslistremove_fuzzer/BUILD.gn b/test/fuzztest/containerslistremove_fuzzer/BUILD.gn index 08f3b6aab197b8ef0c1ede8937da5f3fe390ede1..f216ebae0fd9f2aab972603c8e4aff60ee34be29 100644 --- a/test/fuzztest/containerslistremove_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslistremove_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainerslistRemoveFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslistremove_fuzzer" diff --git a/test/fuzztest/containerslistremovebyindex_fuzzer/BUILD.gn b/test/fuzztest/containerslistremovebyindex_fuzzer/BUILD.gn index d5f0202e1b57b4ecd723f4b682ae4dba55bd6e2b..cb51c72ca3647eed0c64eb1167045862c95c5ebc 100644 --- a/test/fuzztest/containerslistremovebyindex_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslistremovebyindex_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainerslistRemoveByIndexFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslistremovebyindex_fuzzer" diff --git a/test/fuzztest/containerslistset_fuzzer/BUILD.gn b/test/fuzztest/containerslistset_fuzzer/BUILD.gn index 1948251be4fa32cc3ce3b2d3695e4b0f979bc8ef..f82fda624b779878f7f22783371b1cb07aea619c 100644 --- a/test/fuzztest/containerslistset_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslistset_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainerslistSetFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslistset_fuzzer" diff --git a/test/fuzztest/containerslistsort_fuzzer/BUILD.gn b/test/fuzztest/containerslistsort_fuzzer/BUILD.gn index 2f7792eb9fb323d10ca1ced7112b6da86f4887a6..ff7238e55bbab719ee6571797ee52c5ede082055 100644 --- a/test/fuzztest/containerslistsort_fuzzer/BUILD.gn +++ b/test/fuzztest/containerslistsort_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainerslistSortFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerslistsort_fuzzer" diff --git a/test/fuzztest/containersplainarray_fuzzer/BUILD.gn b/test/fuzztest/containersplainarray_fuzzer/BUILD.gn index b1b0a9f47bdf61a45663c53eeb3947ac09c0e6e0..72784d5fab109a87beaf1e00a79d524b45ab37e0 100644 --- a/test/fuzztest/containersplainarray_fuzzer/BUILD.gn +++ b/test/fuzztest/containersplainarray_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersPlainArrayFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersplainarray_fuzzer" diff --git a/test/fuzztest/containersprivatearraylist_fuzzer/BUILD.gn b/test/fuzztest/containersprivatearraylist_fuzzer/BUILD.gn index 10eba7bdfbdbf74490db6beaa9c4d16fa83bc58f..0a2f24e3fda0826c2ab0fc0b2c729dc66eedbffe 100644 --- a/test/fuzztest/containersprivatearraylist_fuzzer/BUILD.gn +++ b/test/fuzztest/containersprivatearraylist_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersPrivateArrayListFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersprivatearraylist_fuzzer" diff --git a/test/fuzztest/containersprivatedeque_fuzzer/BUILD.gn b/test/fuzztest/containersprivatedeque_fuzzer/BUILD.gn index 5d95f50c29dd8bdf3b793094b8df16df55d67a8c..fdb86b0d7ee7183fd89d457144bb1116df717678 100644 --- a/test/fuzztest/containersprivatedeque_fuzzer/BUILD.gn +++ b/test/fuzztest/containersprivatedeque_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersPrivateDequeFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersprivatedeque_fuzzer" diff --git a/test/fuzztest/containersprivatehashmap_fuzzer/BUILD.gn b/test/fuzztest/containersprivatehashmap_fuzzer/BUILD.gn index 20e8948f0f006812bb629be710ac25d6eb46d699..2a820ca5ea38c6a1273a196346d36201181fa059 100644 --- a/test/fuzztest/containersprivatehashmap_fuzzer/BUILD.gn +++ b/test/fuzztest/containersprivatehashmap_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersPrivateHashMapFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersprivatehashmap_fuzzer" diff --git a/test/fuzztest/containersprivatehashset_fuzzer/BUILD.gn b/test/fuzztest/containersprivatehashset_fuzzer/BUILD.gn index b5d7e556db36d10d59f5057343dc35d440240574..100d9ad9143ca4e0e850d8ad72f06a3159b45f9f 100644 --- a/test/fuzztest/containersprivatehashset_fuzzer/BUILD.gn +++ b/test/fuzztest/containersprivatehashset_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersPrivateHashSetFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersprivatehashset_fuzzer" diff --git a/test/fuzztest/containersprivatelightweightmap_fuzzer/BUILD.gn b/test/fuzztest/containersprivatelightweightmap_fuzzer/BUILD.gn index 6dc6951ddd3589f980b865def07173532d4cada1..b2cb747d8fc72b7990fe20b10046a394ded878e9 100644 --- a/test/fuzztest/containersprivatelightweightmap_fuzzer/BUILD.gn +++ b/test/fuzztest/containersprivatelightweightmap_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersPrivateLightWeightMapFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersprivatelightweightmap_fuzzer" diff --git a/test/fuzztest/containersprivatelightweightset_fuzzer/BUILD.gn b/test/fuzztest/containersprivatelightweightset_fuzzer/BUILD.gn index aa799ba2aaa19a0ea78707c6a886d651ddf3e05a..277ca395cbaafccd8670d8cceded5c3a4bb55615 100644 --- a/test/fuzztest/containersprivatelightweightset_fuzzer/BUILD.gn +++ b/test/fuzztest/containersprivatelightweightset_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersPrivateLightWeightSetFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersprivatelightweightset_fuzzer" diff --git a/test/fuzztest/containersprivatelinkedlist_fuzzer/BUILD.gn b/test/fuzztest/containersprivatelinkedlist_fuzzer/BUILD.gn index 921465653f0aa779e155673c505fc613b1a90c5e..8475535e89765695fc3c7137e210a9898f0bfb98 100644 --- a/test/fuzztest/containersprivatelinkedlist_fuzzer/BUILD.gn +++ b/test/fuzztest/containersprivatelinkedlist_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersPrivateLinkedListFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersprivatelinkedlist_fuzzer" diff --git a/test/fuzztest/containersprivatelist_fuzzer/BUILD.gn b/test/fuzztest/containersprivatelist_fuzzer/BUILD.gn index fb706113777e1a1ec6756aa41a5c450778dc9554..70cb2a0bb261112d588d02c95f56c6a07e3af288 100644 --- a/test/fuzztest/containersprivatelist_fuzzer/BUILD.gn +++ b/test/fuzztest/containersprivatelist_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersPrivateListFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersprivatelist_fuzzer" diff --git a/test/fuzztest/containersprivateload_fuzzer/BUILD.gn b/test/fuzztest/containersprivateload_fuzzer/BUILD.gn index 3345b24bcf0fed1e7f5308cbfeef299e65d4dbbc..520951228e60e9f56e65af6fdc34048647a9064b 100644 --- a/test/fuzztest/containersprivateload_fuzzer/BUILD.gn +++ b/test/fuzztest/containersprivateload_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersPrivateLoadFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersprivateload_fuzzer" diff --git a/test/fuzztest/containersprivateplainarray_fuzzer/BUILD.gn b/test/fuzztest/containersprivateplainarray_fuzzer/BUILD.gn index d63e0c66773fa3de69c2bc3fe329d268bed666c0..633fb7f0255ffb841fbde0bb3567663d6148459f 100644 --- a/test/fuzztest/containersprivateplainarray_fuzzer/BUILD.gn +++ b/test/fuzztest/containersprivateplainarray_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersPrivatePlainArrayFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersprivateplainarray_fuzzer" diff --git a/test/fuzztest/containersprivatequeue_fuzzer/BUILD.gn b/test/fuzztest/containersprivatequeue_fuzzer/BUILD.gn index f383a19401c0cafaa617d82d051a442dc8504d40..ac865121c6a34d9b39107cad2a26d4335bb2e372 100644 --- a/test/fuzztest/containersprivatequeue_fuzzer/BUILD.gn +++ b/test/fuzztest/containersprivatequeue_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersPrivateQueueFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersprivatequeue_fuzzer" diff --git a/test/fuzztest/containersprivatestack_fuzzer/BUILD.gn b/test/fuzztest/containersprivatestack_fuzzer/BUILD.gn index 6b81c3071fd9ee22542b68848247e58b289b309c..9582cede53ae23b05b293365f0edd9e6a5c21a0d 100644 --- a/test/fuzztest/containersprivatestack_fuzzer/BUILD.gn +++ b/test/fuzztest/containersprivatestack_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersPrivateStackFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersprivatestack_fuzzer" diff --git a/test/fuzztest/containersprivatetreemap_fuzzer/BUILD.gn b/test/fuzztest/containersprivatetreemap_fuzzer/BUILD.gn index 10d76684bbf13f8b8b6162e98ae77e8e84a89ac5..b4ba16e06eed0f23ce72879a5e4856ba2f2844e4 100644 --- a/test/fuzztest/containersprivatetreemap_fuzzer/BUILD.gn +++ b/test/fuzztest/containersprivatetreemap_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersPrivateTreeMapFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersprivatetreemap_fuzzer" diff --git a/test/fuzztest/containersprivatetreeset_fuzzer/BUILD.gn b/test/fuzztest/containersprivatetreeset_fuzzer/BUILD.gn index 54a934ac56eb7292c157bb89b2e0657e25339fab..4710427044b5d94f70793286bb6ab6278eb58d49 100644 --- a/test/fuzztest/containersprivatetreeset_fuzzer/BUILD.gn +++ b/test/fuzztest/containersprivatetreeset_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersPrivateTreeSetFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersprivatetreeset_fuzzer" diff --git a/test/fuzztest/containersprivatevector_fuzzer/BUILD.gn b/test/fuzztest/containersprivatevector_fuzzer/BUILD.gn index 84d37bb9790cf16c34bf5ed21273b89b48c35169..09351cce6892f10166aafc4419662bf45bfd11d6 100644 --- a/test/fuzztest/containersprivatevector_fuzzer/BUILD.gn +++ b/test/fuzztest/containersprivatevector_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersPrivateVectorFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersprivatevector_fuzzer" diff --git a/test/fuzztest/containersqueue_fuzzer/BUILD.gn b/test/fuzztest/containersqueue_fuzzer/BUILD.gn index 8727b352402b18d198d2662420278ab4d42aefb5..6f9852b71275d810e0582d8e367118e3657c27f6 100644 --- a/test/fuzztest/containersqueue_fuzzer/BUILD.gn +++ b/test/fuzztest/containersqueue_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersQueueFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersqueue_fuzzer" diff --git a/test/fuzztest/containersqueueadd_fuzzer/BUILD.gn b/test/fuzztest/containersqueueadd_fuzzer/BUILD.gn index 1623288de3ee38e9ae447566855ca105e4e590a6..8b9fc45b1f80b3b177612b8fc91971aa536a97e1 100644 --- a/test/fuzztest/containersqueueadd_fuzzer/BUILD.gn +++ b/test/fuzztest/containersqueueadd_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersQueueAddFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersqueueadd_fuzzer" diff --git a/test/fuzztest/containersqueueforeach_fuzzer/BUILD.gn b/test/fuzztest/containersqueueforeach_fuzzer/BUILD.gn index bcdae577c6359717fd2ed7d14205e223aa18a03e..fad65e870809657f9be3c49fdd851630eefbfef0 100644 --- a/test/fuzztest/containersqueueforeach_fuzzer/BUILD.gn +++ b/test/fuzztest/containersqueueforeach_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersQueueForEachFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersqueueforeach_fuzzer" diff --git a/test/fuzztest/containersqueuegetfirst_fuzzer/BUILD.gn b/test/fuzztest/containersqueuegetfirst_fuzzer/BUILD.gn index 64baa97152db6a93582e2f142982c4b405e43b8a..4782395fb9fd2c46010e4435dff1bcd70f9f2540 100644 --- a/test/fuzztest/containersqueuegetfirst_fuzzer/BUILD.gn +++ b/test/fuzztest/containersqueuegetfirst_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersQueueGetFirstFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersqueuegetfirst_fuzzer" diff --git a/test/fuzztest/containersqueuegetiteratorobj_fuzzer/BUILD.gn b/test/fuzztest/containersqueuegetiteratorobj_fuzzer/BUILD.gn index 00c7c94f788e7e30648d303e37be73934c1c4380..41e7be4ec4902ae28d4c1643f2bd1a36ef4f3c2f 100644 --- a/test/fuzztest/containersqueuegetiteratorobj_fuzzer/BUILD.gn +++ b/test/fuzztest/containersqueuegetiteratorobj_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersQueueGetIteratorObjFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersqueuegetiteratorobj_fuzzer" diff --git a/test/fuzztest/containersqueuegetsize_fuzzer/BUILD.gn b/test/fuzztest/containersqueuegetsize_fuzzer/BUILD.gn index ec1ffe4270d5d8c071562f436f9dd2a5e03c41f2..9a117ff98759d97af86228c831588df74b44d052 100644 --- a/test/fuzztest/containersqueuegetsize_fuzzer/BUILD.gn +++ b/test/fuzztest/containersqueuegetsize_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersQueueGetSizeFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersqueuegetsize_fuzzer" diff --git a/test/fuzztest/containersqueuepop_fuzzer/BUILD.gn b/test/fuzztest/containersqueuepop_fuzzer/BUILD.gn index 322cc07844cb3a9d99c61cccb1786382699a931e..b9c10145ea1af142e4822db4a61105bad83d2450 100644 --- a/test/fuzztest/containersqueuepop_fuzzer/BUILD.gn +++ b/test/fuzztest/containersqueuepop_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersQueuePopFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersqueuepop_fuzzer" diff --git a/test/fuzztest/containersstack_fuzzer/BUILD.gn b/test/fuzztest/containersstack_fuzzer/BUILD.gn index 1f4c7ec87094aac8e24605f585d2adecf6e7505b..3cda00fbd5979285658f9862a72dab65f49cb19a 100644 --- a/test/fuzztest/containersstack_fuzzer/BUILD.gn +++ b/test/fuzztest/containersstack_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersStackFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersstack_fuzzer" diff --git a/test/fuzztest/containersstackforeach_fuzzer/BUILD.gn b/test/fuzztest/containersstackforeach_fuzzer/BUILD.gn index de07a9b9b30c813530000919ba52f098b85af3e6..cd94c4c323b21b3a902414bc4231154fd2e8ecd2 100644 --- a/test/fuzztest/containersstackforeach_fuzzer/BUILD.gn +++ b/test/fuzztest/containersstackforeach_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersStackForEachFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersstackforeach_fuzzer" diff --git a/test/fuzztest/containersstackgetlength_fuzzer/BUILD.gn b/test/fuzztest/containersstackgetlength_fuzzer/BUILD.gn index 3320d5c48e580ee0f53e572d26dd818ef2263873..4151c36ac36c2d221da40d7bf2ea7d4d38f18f50 100644 --- a/test/fuzztest/containersstackgetlength_fuzzer/BUILD.gn +++ b/test/fuzztest/containersstackgetlength_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersStackGetLengthFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersstackgetlength_fuzzer" diff --git a/test/fuzztest/containersstackisempty_fuzzer/BUILD.gn b/test/fuzztest/containersstackisempty_fuzzer/BUILD.gn index bb5411281706b3d9f270425438e3b322db8fa0f2..820d99a6458316a871670cb9e2203e6c399a6c72 100644 --- a/test/fuzztest/containersstackisempty_fuzzer/BUILD.gn +++ b/test/fuzztest/containersstackisempty_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersStackIsEmptyFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersstackisempty_fuzzer" diff --git a/test/fuzztest/containersstackiterator_fuzzer/BUILD.gn b/test/fuzztest/containersstackiterator_fuzzer/BUILD.gn index 6b36bcb0b5ca1f4f04dbe05f8e0de4783fda346c..de746e76c91ee6484ddc5426a0b137db6eebbf2b 100644 --- a/test/fuzztest/containersstackiterator_fuzzer/BUILD.gn +++ b/test/fuzztest/containersstackiterator_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersStackIteratorFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersstackiterator_fuzzer" diff --git a/test/fuzztest/containersstacklocate_fuzzer/BUILD.gn b/test/fuzztest/containersstacklocate_fuzzer/BUILD.gn index f3199afffe605c58d6095db8e440fe6cdb433b54..ce111e3c557aae23b63ef84e193d4eb8e884ceb9 100644 --- a/test/fuzztest/containersstacklocate_fuzzer/BUILD.gn +++ b/test/fuzztest/containersstacklocate_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersStackLocateFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersstacklocate_fuzzer" diff --git a/test/fuzztest/containersstackpeek_fuzzer/BUILD.gn b/test/fuzztest/containersstackpeek_fuzzer/BUILD.gn index 670c9afeb7efb68d900b2f82b548d9842aa30065..ee7daf1d7a2a5afc9ae4b69e732685381b01f99a 100644 --- a/test/fuzztest/containersstackpeek_fuzzer/BUILD.gn +++ b/test/fuzztest/containersstackpeek_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersStackPeekFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersstackpeek_fuzzer" diff --git a/test/fuzztest/containersstackpop_fuzzer/BUILD.gn b/test/fuzztest/containersstackpop_fuzzer/BUILD.gn index b61448067b8f5adcd0371e4fd0a2ae5e6cab8061..5af4e48fe441db9dc6e7781d83e0e7461c2f2156 100644 --- a/test/fuzztest/containersstackpop_fuzzer/BUILD.gn +++ b/test/fuzztest/containersstackpop_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersStackPopFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersstackpop_fuzzer" diff --git a/test/fuzztest/containersstackpush_fuzzer/BUILD.gn b/test/fuzztest/containersstackpush_fuzzer/BUILD.gn index 118cdbb222f6611de0bb552787dac966e362f83e..61ed3c54458848bd646febe00a93660c1c6b0dee 100644 --- a/test/fuzztest/containersstackpush_fuzzer/BUILD.gn +++ b/test/fuzztest/containersstackpush_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersStackPushFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersstackpush_fuzzer" diff --git a/test/fuzztest/containerstreemapclear_fuzzer/BUILD.gn b/test/fuzztest/containerstreemapclear_fuzzer/BUILD.gn index 7716dca2a9ba3099a2a76e0ad52640e25ebed7a0..c25c81d41f17125d0eede7bce322f66c3df5d054 100644 --- a/test/fuzztest/containerstreemapclear_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreemapclear_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeMapClearFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreemapclear_fuzzer" diff --git a/test/fuzztest/containerstreemapconstructor_fuzzer/BUILD.gn b/test/fuzztest/containerstreemapconstructor_fuzzer/BUILD.gn index f9eb08ca19905360bc5f68cefd8890ecc22be1ad..043152509b6aafe912bd34a742f0c137a818d48b 100644 --- a/test/fuzztest/containerstreemapconstructor_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreemapconstructor_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeMapConstructorFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreemapconstructor_fuzzer" diff --git a/test/fuzztest/containerstreemapentries_fuzzer/BUILD.gn b/test/fuzztest/containerstreemapentries_fuzzer/BUILD.gn index 1a7609edf08560ae7b80d3b1e81478ea58c3087d..ddd53f9b3c8b2eb3147779d77d0fda6467b07c76 100644 --- a/test/fuzztest/containerstreemapentries_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreemapentries_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeMapEntriesFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreemapentries_fuzzer" diff --git a/test/fuzztest/containerstreemapforeach_fuzzer/BUILD.gn b/test/fuzztest/containerstreemapforeach_fuzzer/BUILD.gn index 28fc7acf9323b3fb27ad1e14a8b093abf024bdc9..55994b343c37875b998b0df011d39f6ba1423845 100644 --- a/test/fuzztest/containerstreemapforeach_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreemapforeach_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeMapForEachFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreemapforeach_fuzzer" diff --git a/test/fuzztest/containerstreemapget_fuzzer/BUILD.gn b/test/fuzztest/containerstreemapget_fuzzer/BUILD.gn index ddd90ae81ac63028713ff3bad0e6c0998db07653..b04928f47291271c9f0814adf7828c0202aa758e 100644 --- a/test/fuzztest/containerstreemapget_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreemapget_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeMapGetFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreemapget_fuzzer" diff --git a/test/fuzztest/containerstreemapgetfirstkey_fuzzer/BUILD.gn b/test/fuzztest/containerstreemapgetfirstkey_fuzzer/BUILD.gn index 288599a2b1b521c49573442df5df8a4a89ed3bc4..559eca80d4352d3933aba7f9a36ee08388b59b94 100644 --- a/test/fuzztest/containerstreemapgetfirstkey_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreemapgetfirstkey_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeMapGetFirstKeyFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreemapgetfirstkey_fuzzer" diff --git a/test/fuzztest/containerstreemapgethigherkey_fuzzer/BUILD.gn b/test/fuzztest/containerstreemapgethigherkey_fuzzer/BUILD.gn index 4dd29d4dec1f2584bdb2d6ad610c0ab3751a7869..baf489f2bd2966807540d819b38f3da704208916 100644 --- a/test/fuzztest/containerstreemapgethigherkey_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreemapgethigherkey_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeMapGetHigherKeyFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreemapgethigherkey_fuzzer" diff --git a/test/fuzztest/containerstreemapgetlastkey_fuzzer/BUILD.gn b/test/fuzztest/containerstreemapgetlastkey_fuzzer/BUILD.gn index 4a6486d5fc1528c1194c0616d30232cfeded177c..79ca964202b6fd31ae1a54d14f4059beb6829365 100644 --- a/test/fuzztest/containerstreemapgetlastkey_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreemapgetlastkey_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeMapGetLastKeyFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreemapgetlastkey_fuzzer" diff --git a/test/fuzztest/containerstreemapgetlength_fuzzer/BUILD.gn b/test/fuzztest/containerstreemapgetlength_fuzzer/BUILD.gn index eab8cd185b76635bbb145635442db13e64a4415d..7e912e7f85a91dfb8a19488989fa150cf8b58ca7 100644 --- a/test/fuzztest/containerstreemapgetlength_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreemapgetlength_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeMapGetLengthFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreemapgetlength_fuzzer" diff --git a/test/fuzztest/containerstreemapgetlowerkey_fuzzer/BUILD.gn b/test/fuzztest/containerstreemapgetlowerkey_fuzzer/BUILD.gn index 30eb6234feb8f8285d6b4cd98c82381afd7dda63..4d23872a465952fc2a450c36a286c076e0d33ea4 100644 --- a/test/fuzztest/containerstreemapgetlowerkey_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreemapgetlowerkey_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeMapGetLowerKeyFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreemapgetlowerkey_fuzzer" diff --git a/test/fuzztest/containerstreemaphaskey_fuzzer/BUILD.gn b/test/fuzztest/containerstreemaphaskey_fuzzer/BUILD.gn index 422f61995333d828db74176dc58b1ba5f0c96233..4e76b17752655599a75c6f76b2f2baebe02b789e 100644 --- a/test/fuzztest/containerstreemaphaskey_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreemaphaskey_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeMapHasKeyFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreemaphaskey_fuzzer" diff --git a/test/fuzztest/containerstreemaphasvalue_fuzzer/BUILD.gn b/test/fuzztest/containerstreemaphasvalue_fuzzer/BUILD.gn index c71dcd66f5ea1d3150f10d4df49b6ae3376167cd..55aeb7ed8cdd6046307a67f225090b44ef2846ac 100644 --- a/test/fuzztest/containerstreemaphasvalue_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreemaphasvalue_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeMapHasValueFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreemaphasvalue_fuzzer" diff --git a/test/fuzztest/containerstreemapisempty_fuzzer/BUILD.gn b/test/fuzztest/containerstreemapisempty_fuzzer/BUILD.gn index 3500ceb948efe9d502d41316a4a82d67dbbb4aa5..1122f86931fd18e857ebec66d95c98cc6d6508ad 100644 --- a/test/fuzztest/containerstreemapisempty_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreemapisempty_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeMapIsEmptyFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreemapisempty_fuzzer" diff --git a/test/fuzztest/containerstreemapkeys_fuzzer/BUILD.gn b/test/fuzztest/containerstreemapkeys_fuzzer/BUILD.gn index a6d401c816f57eb6edb366e2f6294f3e6fc1e289..5d631d99b040da6220a1d170bfbf0d4942b74850 100644 --- a/test/fuzztest/containerstreemapkeys_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreemapkeys_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeMapKeysFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreemapkeys_fuzzer" diff --git a/test/fuzztest/containerstreemapremove_fuzzer/BUILD.gn b/test/fuzztest/containerstreemapremove_fuzzer/BUILD.gn index 9571dbdb69c018127232897d7e3b2ed064357890..5f9d0ecbfed6c2a90f754ca46fb17024bede076e 100644 --- a/test/fuzztest/containerstreemapremove_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreemapremove_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeMapRemoveFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreemapremove_fuzzer" diff --git a/test/fuzztest/containerstreemapreplace_fuzzer/BUILD.gn b/test/fuzztest/containerstreemapreplace_fuzzer/BUILD.gn index af450d6700292db22fdcafe5629a092fd729d689..b888dbec4dbe952af685e3d48321605326075c7f 100644 --- a/test/fuzztest/containerstreemapreplace_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreemapreplace_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeMapReplaceFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreemapreplace_fuzzer" diff --git a/test/fuzztest/containerstreemapset_fuzzer/BUILD.gn b/test/fuzztest/containerstreemapset_fuzzer/BUILD.gn index b6b26adc3acaae51a3c5246c6755e96ee99c99c9..44d442c967c055fd822b22a0b0aa92cef1cab975 100644 --- a/test/fuzztest/containerstreemapset_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreemapset_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeMapSetFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreemapset_fuzzer" diff --git a/test/fuzztest/containerstreemapsetall_fuzzer/BUILD.gn b/test/fuzztest/containerstreemapsetall_fuzzer/BUILD.gn index 54e310deacbc94eb74cb970776cceeb946bbf1f5..2cee7366fd1a6244d1d927d6a7c4fc1d78b60065 100644 --- a/test/fuzztest/containerstreemapsetall_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreemapsetall_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeMapSetAllFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreemapsetall_fuzzer" diff --git a/test/fuzztest/containerstreemapvalues_fuzzer/BUILD.gn b/test/fuzztest/containerstreemapvalues_fuzzer/BUILD.gn index b271ef689d737f17840838f5805e776b53a4f220..590e31564ae00429712ec5917b7b63dee722b0e6 100644 --- a/test/fuzztest/containerstreemapvalues_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreemapvalues_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeMapValuesFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreemapvalues_fuzzer" diff --git a/test/fuzztest/containerstreesetadd_fuzzer/BUILD.gn b/test/fuzztest/containerstreesetadd_fuzzer/BUILD.gn index 43f6dfd76e896800ee95e3ad74767aec6cf7549f..b5d436fc72093e44bb8f74cadfbb8c0149ceeead 100644 --- a/test/fuzztest/containerstreesetadd_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreesetadd_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeSetAddFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreesetadd_fuzzer" diff --git a/test/fuzztest/containerstreesetclear_fuzzer/BUILD.gn b/test/fuzztest/containerstreesetclear_fuzzer/BUILD.gn index 9a90b0953018c324c2e7ffa936f2705feb692a8b..82eb759fccece456215cfa122fe23edf6e2ec080 100644 --- a/test/fuzztest/containerstreesetclear_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreesetclear_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeSetClearFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreesetclear_fuzzer" diff --git a/test/fuzztest/containerstreesetconstructor_fuzzer/BUILD.gn b/test/fuzztest/containerstreesetconstructor_fuzzer/BUILD.gn index 84409dfb17ab934101e45a16f6f146da625c6019..6756f15de9a3a72ab343e641ab04533b29d37301 100644 --- a/test/fuzztest/containerstreesetconstructor_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreesetconstructor_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeSetConstructorFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreesetconstructor_fuzzer" diff --git a/test/fuzztest/containerstreesetentries_fuzzer/BUILD.gn b/test/fuzztest/containerstreesetentries_fuzzer/BUILD.gn index 6f38f1bb9c919a07d639d8ad965d78f5e7646cf9..4316fb6b6da8456e2ff8793fdca5e26751e3c281 100644 --- a/test/fuzztest/containerstreesetentries_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreesetentries_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeSetEntriesFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreesetentries_fuzzer" diff --git a/test/fuzztest/containerstreesetforeach_fuzzer/BUILD.gn b/test/fuzztest/containerstreesetforeach_fuzzer/BUILD.gn index 6e812b655337fc49639b8a6d948062fbb0acecee..c5886f90116efbb67b24d63e0e6b15eaa9ac17d9 100644 --- a/test/fuzztest/containerstreesetforeach_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreesetforeach_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeSetForEachFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreesetforeach_fuzzer" diff --git a/test/fuzztest/containerstreesetgetfirstvalue_fuzzer/BUILD.gn b/test/fuzztest/containerstreesetgetfirstvalue_fuzzer/BUILD.gn index 3f686d0d7b79c4732eca74741518cb5b5c3bd5c0..732210eb9b64563d6cb64b63eda454ea6f248768 100644 --- a/test/fuzztest/containerstreesetgetfirstvalue_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreesetgetfirstvalue_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeSetGetFirstValueFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreesetgetfirstvalue_fuzzer" diff --git a/test/fuzztest/containerstreesetgethighervalue_fuzzer/BUILD.gn b/test/fuzztest/containerstreesetgethighervalue_fuzzer/BUILD.gn index 39c10901952a5a567d068d2d85269b8be421e464..ebc8984ba3a6e1a84e1b292f925dfcc2dcd20263 100644 --- a/test/fuzztest/containerstreesetgethighervalue_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreesetgethighervalue_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeSetGetHigherValueFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreesetgethighervalue_fuzzer" diff --git a/test/fuzztest/containerstreesetgetlastvalue_fuzzer/BUILD.gn b/test/fuzztest/containerstreesetgetlastvalue_fuzzer/BUILD.gn index 527669eaeef2f0684a2df2d46e115a79bf0d426b..f2bdb29c42a3f12a23ed90ad66073ab0a1f4378f 100644 --- a/test/fuzztest/containerstreesetgetlastvalue_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreesetgetlastvalue_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeSetGetLastValueFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreesetgetlastvalue_fuzzer" diff --git a/test/fuzztest/containerstreesetgetlength_fuzzer/BUILD.gn b/test/fuzztest/containerstreesetgetlength_fuzzer/BUILD.gn index 86bbe8a0daada649cd00d9a84d93ba763c377fd2..bc99715742864bf5da9af7319093b53370020d04 100644 --- a/test/fuzztest/containerstreesetgetlength_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreesetgetlength_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeSetGetLengthFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreesetgetlength_fuzzer" diff --git a/test/fuzztest/containerstreesetgetlowervalue_fuzzer/BUILD.gn b/test/fuzztest/containerstreesetgetlowervalue_fuzzer/BUILD.gn index 644c34e513d1d19edff1a5656e9c8432b4fc3893..45e4ac3b3daeed2ba2d230da875c532f38c3ff32 100644 --- a/test/fuzztest/containerstreesetgetlowervalue_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreesetgetlowervalue_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeSetGetLowerValueFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreesetgetlowervalue_fuzzer" diff --git a/test/fuzztest/containerstreesethas_fuzzer/BUILD.gn b/test/fuzztest/containerstreesethas_fuzzer/BUILD.gn index 166c01d32fb0df4cc8846ce6a45cd9eab647097e..fbfa5f33cc0eab6df3d3a82df093008e693233f6 100644 --- a/test/fuzztest/containerstreesethas_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreesethas_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeSetHasFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreesethas_fuzzer" diff --git a/test/fuzztest/containerstreesetisempty_fuzzer/BUILD.gn b/test/fuzztest/containerstreesetisempty_fuzzer/BUILD.gn index 103445fe09ebd960401c0357f5f6a2c198fffe5b..f2d7fa4391f69a7b28e60e54fe8fc77ac0a7d2f6 100644 --- a/test/fuzztest/containerstreesetisempty_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreesetisempty_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeSetIsEmptyFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreesetisempty_fuzzer" diff --git a/test/fuzztest/containerstreesetpopfirst_fuzzer/BUILD.gn b/test/fuzztest/containerstreesetpopfirst_fuzzer/BUILD.gn index 02c0add804ba7e3ab256c8277e969c0f318c1f3e..75404c2924e25d230ddd87ffbaace91b16aed8ab 100644 --- a/test/fuzztest/containerstreesetpopfirst_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreesetpopfirst_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeSetPopFirstFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreesetpopfirst_fuzzer" diff --git a/test/fuzztest/containerstreesetpoplast_fuzzer/BUILD.gn b/test/fuzztest/containerstreesetpoplast_fuzzer/BUILD.gn index 162c6d09922f274dc5d8b613123d85d1573beee7..ef1f720b62b8692ef4bcaf75364e4fe4e4208889 100644 --- a/test/fuzztest/containerstreesetpoplast_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreesetpoplast_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeSetPopLastFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreesetpoplast_fuzzer" diff --git a/test/fuzztest/containerstreesetremove_fuzzer/BUILD.gn b/test/fuzztest/containerstreesetremove_fuzzer/BUILD.gn index 8b848d48903ba447120e4d476ffa96fe89185836..99078c863233ca855a5493699335d4531366273c 100644 --- a/test/fuzztest/containerstreesetremove_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreesetremove_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeSetRemoveFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreesetremove_fuzzer" diff --git a/test/fuzztest/containerstreesetvalues_fuzzer/BUILD.gn b/test/fuzztest/containerstreesetvalues_fuzzer/BUILD.gn index 6b241b21802ea9c4ea462d831984b23e0419a23d..ef35786be5bbacb9381e599e375779d796297675 100644 --- a/test/fuzztest/containerstreesetvalues_fuzzer/BUILD.gn +++ b/test/fuzztest/containerstreesetvalues_fuzzer/BUILD.gn @@ -20,7 +20,7 @@ import("//build/test.gni") ####################################fuzztest################################## ohos_fuzztest("ContainersTreeSetValuesFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containerstreesetvalues_fuzzer" diff --git a/test/fuzztest/containersvectoradd_fuzzer/BUILD.gn b/test/fuzztest/containersvectoradd_fuzzer/BUILD.gn index 0ea35bb4f0d4b266bfe46231a9c091756fc9c114..0062edc8df4e874ad4272180ca5f8c075b51e849 100644 --- a/test/fuzztest/containersvectoradd_fuzzer/BUILD.gn +++ b/test/fuzztest/containersvectoradd_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersVectorAddFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersvectoradd_fuzzer" diff --git a/test/fuzztest/containersvectorclear_fuzzer/BUILD.gn b/test/fuzztest/containersvectorclear_fuzzer/BUILD.gn index 0138de3776972c74928de860466bc38d49f5a494..ca12ea9776d6e0fd314fbcc46e0ece69d9a6653b 100644 --- a/test/fuzztest/containersvectorclear_fuzzer/BUILD.gn +++ b/test/fuzztest/containersvectorclear_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersVectorClearFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersvectorclear_fuzzer" diff --git a/test/fuzztest/containersvectorclone_fuzzer/BUILD.gn b/test/fuzztest/containersvectorclone_fuzzer/BUILD.gn index 214897fcbef2927ddb599f4ee9ae602d5a800275..7b8dee35a10583b4d3df5c90de4e5bc937f9e822 100644 --- a/test/fuzztest/containersvectorclone_fuzzer/BUILD.gn +++ b/test/fuzztest/containersvectorclone_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersVectorCloneFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersvectorclone_fuzzer" diff --git a/test/fuzztest/containersvectorcommon_fuzzer/containersvectorcommon_fuzzer.h b/test/fuzztest/containersvectorcommon_fuzzer/containersvectorcommon_fuzzer.h index 0300b69663fb53b95424f6813230943925acfc94..63659479d1c9a7f2173cfe763d52d09b55574bad 100644 --- a/test/fuzztest/containersvectorcommon_fuzzer/containersvectorcommon_fuzzer.h +++ b/test/fuzztest/containersvectorcommon_fuzzer/containersvectorcommon_fuzzer.h @@ -481,7 +481,7 @@ public: return JSTaggedValue::Undefined(); } }; - + static void ContainersVectorReplaceAllElementsFuzzTest(const uint8_t* data, size_t size) { if (size <= 0) { @@ -847,7 +847,7 @@ public: JSNApi::DestroyJSVM(vm); } - + }; } #endif \ No newline at end of file diff --git a/test/fuzztest/containersvectorconverttoarray_fuzzer/BUILD.gn b/test/fuzztest/containersvectorconverttoarray_fuzzer/BUILD.gn index 0671d47c4b9af28b7246584eb7adf7bb781a56d1..e735c1f3858ba0d31e5fc32d052b54d3df3b4507 100644 --- a/test/fuzztest/containersvectorconverttoarray_fuzzer/BUILD.gn +++ b/test/fuzztest/containersvectorconverttoarray_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersVectorConvertToArrayFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersvectorconverttoarray_fuzzer" diff --git a/test/fuzztest/containersvectorcopytoarray_fuzzer/BUILD.gn b/test/fuzztest/containersvectorcopytoarray_fuzzer/BUILD.gn index c1344d571bf68b00458794791edb7a865452efda..1bff4f90def51a9d16060aa830d2d7be01ed0fe5 100644 --- a/test/fuzztest/containersvectorcopytoarray_fuzzer/BUILD.gn +++ b/test/fuzztest/containersvectorcopytoarray_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersVectorCopyToArrayFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersvectorcopytoarray_fuzzer" diff --git a/test/fuzztest/containersvectorforeach_fuzzer/BUILD.gn b/test/fuzztest/containersvectorforeach_fuzzer/BUILD.gn index 3d1c64139cb5f9e05e1269723a442a9a8ce7dd9b..ef2cb6d5177f3314b175c7369587284fc9012904 100644 --- a/test/fuzztest/containersvectorforeach_fuzzer/BUILD.gn +++ b/test/fuzztest/containersvectorforeach_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersVectorForEachFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersvectorforeach_fuzzer" diff --git a/test/fuzztest/containersvectorgetcapacity_fuzzer/BUILD.gn b/test/fuzztest/containersvectorgetcapacity_fuzzer/BUILD.gn index 3112fe79cc4f541dc89dd668aa532ef9ff5072bf..097df4559ebb9711a82a9a5170980128cb6cb2ba 100644 --- a/test/fuzztest/containersvectorgetcapacity_fuzzer/BUILD.gn +++ b/test/fuzztest/containersvectorgetcapacity_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersVectorGetCapacityFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersvectorgetcapacity_fuzzer" diff --git a/test/fuzztest/containersvectorgetfirstelement_fuzzer/BUILD.gn b/test/fuzztest/containersvectorgetfirstelement_fuzzer/BUILD.gn index 5ed01da065c607cced27585944bd4d5fb316213a..d8f614db666330f77d57869018aaa34d0f131fc7 100644 --- a/test/fuzztest/containersvectorgetfirstelement_fuzzer/BUILD.gn +++ b/test/fuzztest/containersvectorgetfirstelement_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersVectorGetFirstElementFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersvectorgetfirstelement_fuzzer" diff --git a/test/fuzztest/containersvectorgetindexfrom_fuzzer/BUILD.gn b/test/fuzztest/containersvectorgetindexfrom_fuzzer/BUILD.gn index a30027104da6b0d79cc2de06f0444db58cd52107..943dc5d13d648f124629e2d32b9e0d139caeea42 100644 --- a/test/fuzztest/containersvectorgetindexfrom_fuzzer/BUILD.gn +++ b/test/fuzztest/containersvectorgetindexfrom_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersVectorGetIndexFromFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersvectorgetindexfrom_fuzzer" diff --git a/test/fuzztest/containersvectorgetindexof_fuzzer/BUILD.gn b/test/fuzztest/containersvectorgetindexof_fuzzer/BUILD.gn index 6ffc5ca47e7ec528ae7c376c150ce4b49d1ff963..f2f332f7289a1151c0fc5191205919a051830759 100644 --- a/test/fuzztest/containersvectorgetindexof_fuzzer/BUILD.gn +++ b/test/fuzztest/containersvectorgetindexof_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersVectorGetIndexOfFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersvectorgetindexof_fuzzer" diff --git a/test/fuzztest/containersvectorgetlastelement_fuzzer/BUILD.gn b/test/fuzztest/containersvectorgetlastelement_fuzzer/BUILD.gn index 0ca393677e88ce7d36100661c3e6210c165f0598..8bd0be3a4f3225f1fd1cc73843a013779978cbc1 100644 --- a/test/fuzztest/containersvectorgetlastelement_fuzzer/BUILD.gn +++ b/test/fuzztest/containersvectorgetlastelement_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersVectorGetLastElementFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersvectorgetlastelement_fuzzer" diff --git a/test/fuzztest/containersvectorgetlastindexfrom_fuzzer/BUILD.gn b/test/fuzztest/containersvectorgetlastindexfrom_fuzzer/BUILD.gn index 372a74029f2aeaac7ecca9b43dea6e8257891e19..4898fd7862edebfe7131f7f09fb2cad22ef91dbe 100644 --- a/test/fuzztest/containersvectorgetlastindexfrom_fuzzer/BUILD.gn +++ b/test/fuzztest/containersvectorgetlastindexfrom_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersVectorGetLastIndexFromFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersvectorgetlastindexfrom_fuzzer" diff --git a/test/fuzztest/containersvectorgetlastindexof_fuzzer/BUILD.gn b/test/fuzztest/containersvectorgetlastindexof_fuzzer/BUILD.gn index 4460868ebd048548dad36781cfa5db539ff54c3e..aefa8ef6ff3068c702ee48a3331c7be47f113308 100644 --- a/test/fuzztest/containersvectorgetlastindexof_fuzzer/BUILD.gn +++ b/test/fuzztest/containersvectorgetlastindexof_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersVectorGetLastIndexOfFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersvectorgetlastindexof_fuzzer" diff --git a/test/fuzztest/containersvectorhas_fuzzer/BUILD.gn b/test/fuzztest/containersvectorhas_fuzzer/BUILD.gn index fbc332435dc2a95a7235506c7b4f8e5631029537..9c93f8c45c2f020ce77136708845f129118dc4a0 100644 --- a/test/fuzztest/containersvectorhas_fuzzer/BUILD.gn +++ b/test/fuzztest/containersvectorhas_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersVectorHasFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersvectorhas_fuzzer" diff --git a/test/fuzztest/containersvectorincreasecapacityto_fuzzer/BUILD.gn b/test/fuzztest/containersvectorincreasecapacityto_fuzzer/BUILD.gn index 77e31768ff8e9fe75352e0badd5933cd83e648b6..58ffa322d10b5b15c73867c18b95e3742e26982d 100644 --- a/test/fuzztest/containersvectorincreasecapacityto_fuzzer/BUILD.gn +++ b/test/fuzztest/containersvectorincreasecapacityto_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersVectorIncreaseCapacityToFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersvectorincreasecapacityto_fuzzer" diff --git a/test/fuzztest/containersvectorinsert_fuzzer/BUILD.gn b/test/fuzztest/containersvectorinsert_fuzzer/BUILD.gn index 5660bf53bbb68a3cc824c32767f911651a8bfc82..d810be8196e4a94da63fa08a220314a0c301483a 100644 --- a/test/fuzztest/containersvectorinsert_fuzzer/BUILD.gn +++ b/test/fuzztest/containersvectorinsert_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersVectorInsertFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersvectorinsert_fuzzer" diff --git a/test/fuzztest/containersvectorisempty_fuzzer/BUILD.gn b/test/fuzztest/containersvectorisempty_fuzzer/BUILD.gn index c8970b3af113558799cac7e402a95a540da70bbe..5b13eadeea67ed941e0588d7537988e952259844 100644 --- a/test/fuzztest/containersvectorisempty_fuzzer/BUILD.gn +++ b/test/fuzztest/containersvectorisempty_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersVectorIsEmptyFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersvectorisempty_fuzzer" diff --git a/test/fuzztest/containersvectoriterator_fuzzer/BUILD.gn b/test/fuzztest/containersvectoriterator_fuzzer/BUILD.gn index 9819776629774f26fd264c50ccce0f8970421cd6..57ab27f514b6e30c7a4a93671261dcb8cb620ea8 100644 --- a/test/fuzztest/containersvectoriterator_fuzzer/BUILD.gn +++ b/test/fuzztest/containersvectoriterator_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersVectorIteratorFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersvectoriterator_fuzzer" diff --git a/test/fuzztest/containersvectorremove_fuzzer/BUILD.gn b/test/fuzztest/containersvectorremove_fuzzer/BUILD.gn index e262fb726b341ff311892bb447e4f1d51fa4a0f7..ef85b1030991e17a572829a4caacd001c1a6c8a5 100644 --- a/test/fuzztest/containersvectorremove_fuzzer/BUILD.gn +++ b/test/fuzztest/containersvectorremove_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersVectorRemoveFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersvectorremove_fuzzer" diff --git a/test/fuzztest/containersvectorremovebyrange_fuzzer/BUILD.gn b/test/fuzztest/containersvectorremovebyrange_fuzzer/BUILD.gn index baf8c6ba5d61331ff79db879e32aaae727744595..744b71e734258c5e0b24a3c766ac8c07f2b89473 100644 --- a/test/fuzztest/containersvectorremovebyrange_fuzzer/BUILD.gn +++ b/test/fuzztest/containersvectorremovebyrange_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersVectorRemoveByRangeFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersvectorremovebyrange_fuzzer" diff --git a/test/fuzztest/containersvectorreplaceallelements_fuzzer/BUILD.gn b/test/fuzztest/containersvectorreplaceallelements_fuzzer/BUILD.gn index b6813afd90770c4837164a955d131ed3c893187d..8ab00246fb5f8c357529e601a2fdc58453cdf37c 100644 --- a/test/fuzztest/containersvectorreplaceallelements_fuzzer/BUILD.gn +++ b/test/fuzztest/containersvectorreplaceallelements_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersVectorReplaceAllElementsFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersvectorreplaceallelements_fuzzer" diff --git a/test/fuzztest/containersvectorset_fuzzer/BUILD.gn b/test/fuzztest/containersvectorset_fuzzer/BUILD.gn index 07038c11ac335ec7575f0bf15dac57f98f2b8abf..b5e67eff57764bd3e44a93ace3cb0603bd4bc65c 100644 --- a/test/fuzztest/containersvectorset_fuzzer/BUILD.gn +++ b/test/fuzztest/containersvectorset_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersVectorSetFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersvectorset_fuzzer" diff --git a/test/fuzztest/containersvectorsetlength_fuzzer/BUILD.gn b/test/fuzztest/containersvectorsetlength_fuzzer/BUILD.gn index d222a189d0b02a4916e56f1b1296167642e69da6..e144f347ecf69ed00f2ef9f1c67fc94a277edca3 100644 --- a/test/fuzztest/containersvectorsetlength_fuzzer/BUILD.gn +++ b/test/fuzztest/containersvectorsetlength_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersVectorSetLengthFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersvectorsetlength_fuzzer" diff --git a/test/fuzztest/containersvectorsort_fuzzer/BUILD.gn b/test/fuzztest/containersvectorsort_fuzzer/BUILD.gn index 8f7280a93b173e2d80b24286adc440dfb412dc72..f8cec0a315fdec06e617c56571bd89c2bb354b1a 100644 --- a/test/fuzztest/containersvectorsort_fuzzer/BUILD.gn +++ b/test/fuzztest/containersvectorsort_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersVectorSortFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersvectorsort_fuzzer" diff --git a/test/fuzztest/containersvectorsubvector_fuzzer/BUILD.gn b/test/fuzztest/containersvectorsubvector_fuzzer/BUILD.gn index c94d91f5eaafa537a38950e0c4cedb41426c5457..f80e6c99039c189b062dab22f86fc046ee6b8d51 100644 --- a/test/fuzztest/containersvectorsubvector_fuzzer/BUILD.gn +++ b/test/fuzztest/containersvectorsubvector_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersVectorSubVectorFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersvectorsubvector_fuzzer" diff --git a/test/fuzztest/containersvectortostring_fuzzer/BUILD.gn b/test/fuzztest/containersvectortostring_fuzzer/BUILD.gn index d9307a82a17a7184fafeb1fd7f863fa9aa34ba1b..ae5806832cfedc86565479d0b37918025700187b 100644 --- a/test/fuzztest/containersvectortostring_fuzzer/BUILD.gn +++ b/test/fuzztest/containersvectortostring_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersVectorToStringFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersvectortostring_fuzzer" diff --git a/test/fuzztest/containersvectortrimtocurrentlength_fuzzer/BUILD.gn b/test/fuzztest/containersvectortrimtocurrentlength_fuzzer/BUILD.gn index 51ef80568f5fab6868d20b2526ccb61a97bd2d0a..97d3724807e7403f02cd98d23257c0a299dc1e7a 100644 --- a/test/fuzztest/containersvectortrimtocurrentlength_fuzzer/BUILD.gn +++ b/test/fuzztest/containersvectortrimtocurrentlength_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("ContainersVectorTrimToCurrentLengthFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/containersvectortrimtocurrentlength_fuzzer" diff --git a/test/fuzztest/createbigwords_fuzzer/BUILD.gn b/test/fuzztest/createbigwords_fuzzer/BUILD.gn index a5a8df80c36dcdce240fc7a4483f6d2c64c59546..71b8a35f683ec7918b26a87ea4a59b395388d4df 100644 --- a/test/fuzztest/createbigwords_fuzzer/BUILD.gn +++ b/test/fuzztest/createbigwords_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("CreateBigWordsFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/createbigwords_fuzzer" diff --git a/test/fuzztest/dataviewrefnew_fuzzer/BUILD.gn b/test/fuzztest/dataviewrefnew_fuzzer/BUILD.gn index 276f20f14d6fc119274c9028c66fe262ea74778a..c1ba4cb68198a1096c3cebcb3a15118c24a87664 100644 --- a/test/fuzztest/dataviewrefnew_fuzzer/BUILD.gn +++ b/test/fuzztest/dataviewrefnew_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("DataViewRefNewFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/dataviewrefnew_fuzzer" diff --git a/test/fuzztest/daterefnew_fuzzer/BUILD.gn b/test/fuzztest/daterefnew_fuzzer/BUILD.gn index 8c617d8a5980d8a6b460fcd7ac38e16afe66fcb2..83eec5baee94d7bf717ed1e8a49d6918448c68d1 100644 --- a/test/fuzztest/daterefnew_fuzzer/BUILD.gn +++ b/test/fuzztest/daterefnew_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("DateRefNewFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/daterefnew_fuzzer" diff --git a/test/fuzztest/dumpheapsnapshot1_fuzzer/BUILD.gn b/test/fuzztest/dumpheapsnapshot1_fuzzer/BUILD.gn index a3a55710c7141bf2f64c536e455ecebe9bfa4565..65d38321a4e43c524a42236c1538cb9aed1ce15a 100644 --- a/test/fuzztest/dumpheapsnapshot1_fuzzer/BUILD.gn +++ b/test/fuzztest/dumpheapsnapshot1_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("DumpHeapSnapshot1FuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/dumpheapsnapshot1_fuzzer" diff --git a/test/fuzztest/dumpheapsnapshot1_fuzzer/dumpheapsnapshot1_fuzzer.cpp b/test/fuzztest/dumpheapsnapshot1_fuzzer/dumpheapsnapshot1_fuzzer.cpp index 9f4c45e825ff311467095540d613f37c8df8afd1..b89b4618e41de1c0a4258d71cbc9c7841d3e72ce 100644 --- a/test/fuzztest/dumpheapsnapshot1_fuzzer/dumpheapsnapshot1_fuzzer.cpp +++ b/test/fuzztest/dumpheapsnapshot1_fuzzer/dumpheapsnapshot1_fuzzer.cpp @@ -43,7 +43,8 @@ namespace OHOS { } bool isVmMode = true; bool isPrivate = false; - DFXJSNApi::DumpHeapSnapshot(vm, input, isVmMode, isPrivate); + bool captureNumericValue = false; + DFXJSNApi::DumpHeapSnapshot(vm, input, isVmMode, isPrivate, captureNumericValue); JSNApi::DestroyJSVM(vm); } } diff --git a/test/fuzztest/dumpheapsnapshot2_fuzzer/BUILD.gn b/test/fuzztest/dumpheapsnapshot2_fuzzer/BUILD.gn index 8ba48d7b2da8bd4fe61225b3acf2bcda1022a6be..c65bb52d2e4747897d26f35ca089f1da375636f5 100644 --- a/test/fuzztest/dumpheapsnapshot2_fuzzer/BUILD.gn +++ b/test/fuzztest/dumpheapsnapshot2_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("DumpHeapSnapshot2FuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/dumpheapsnapshot2_fuzzer" diff --git a/test/fuzztest/dumpheapsnapshot2_fuzzer/dumpheapsnapshot2_fuzzer.cpp b/test/fuzztest/dumpheapsnapshot2_fuzzer/dumpheapsnapshot2_fuzzer.cpp index 2c48bafbd0740966aa5470ab5b1ffe1bb1f1956b..122db45f21fa19e217f9b945a7dd843f7561f534 100644 --- a/test/fuzztest/dumpheapsnapshot2_fuzzer/dumpheapsnapshot2_fuzzer.cpp +++ b/test/fuzztest/dumpheapsnapshot2_fuzzer/dumpheapsnapshot2_fuzzer.cpp @@ -43,8 +43,9 @@ namespace OHOS { } bool isVmMode = true; bool isPrivate = false; + bool captureNumericValue = false; std::string path(data, data + size); - DFXJSNApi::DumpHeapSnapshot(vm, input, path, isVmMode, isPrivate); + DFXJSNApi::DumpHeapSnapshot(vm, input, path, isVmMode, isPrivate, captureNumericValue); JSNApi::DestroyJSVM(vm); } } diff --git a/test/fuzztest/dumpheapsnapshot3_fuzzer/BUILD.gn b/test/fuzztest/dumpheapsnapshot3_fuzzer/BUILD.gn index cbc77836c4737c41590688c291e5114c4d9d56fc..a971c478a43f33ab5f823f1fe7841b40bdf28e53 100644 --- a/test/fuzztest/dumpheapsnapshot3_fuzzer/BUILD.gn +++ b/test/fuzztest/dumpheapsnapshot3_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("DumpHeapSnapshot3FuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/dumpheapsnapshot3_fuzzer" diff --git a/test/fuzztest/dumpheapsnapshot3_fuzzer/dumpheapsnapshot3_fuzzer.cpp b/test/fuzztest/dumpheapsnapshot3_fuzzer/dumpheapsnapshot3_fuzzer.cpp index 0c75ec8cf74fa47461a8130454882e016ff31d27..fba432d6e8d301b96de19c085571213238933574 100644 --- a/test/fuzztest/dumpheapsnapshot3_fuzzer/dumpheapsnapshot3_fuzzer.cpp +++ b/test/fuzztest/dumpheapsnapshot3_fuzzer/dumpheapsnapshot3_fuzzer.cpp @@ -45,10 +45,11 @@ namespace OHOS { } bool isVmMode = true; bool isPrivate = false; + bool captureNumericValue = false; std::string path(data, data + size); FileStream stream(path); Progress *progress = nullptr; - DFXJSNApi::DumpHeapSnapshot(vm, input, &stream, progress, isVmMode, isPrivate); + DFXJSNApi::DumpHeapSnapshot(vm, input, &stream, progress, isVmMode, isPrivate, captureNumericValue); JSNApi::DestroyJSVM(vm); } } diff --git a/test/fuzztest/execute_fuzzer/BUILD.gn b/test/fuzztest/execute_fuzzer/BUILD.gn index 5c1f296a90284efe679da6eaec4f0408e1b86d73..201de5e852d6a53e40eab36d19742e4ef1943c44 100644 --- a/test/fuzztest/execute_fuzzer/BUILD.gn +++ b/test/fuzztest/execute_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ############################fuzztest################################ ohos_fuzztest("ExecuteFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/execute_fuzzer" diff --git a/test/fuzztest/float32arrayrefnew_fuzzer/BUILD.gn b/test/fuzztest/float32arrayrefnew_fuzzer/BUILD.gn index 77d362c5da4a27cfc1494cb3c5e3027cf2994f7f..0ad598c7e364d200ca0f7de98b472e0e287bc055 100644 --- a/test/fuzztest/float32arrayrefnew_fuzzer/BUILD.gn +++ b/test/fuzztest/float32arrayrefnew_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("Float32ArrayRefNewFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/float32arrayrefnew_fuzzer" diff --git a/test/fuzztest/float64arrayrefnew_fuzzer/BUILD.gn b/test/fuzztest/float64arrayrefnew_fuzzer/BUILD.gn index ebeb349c3acccbb7f919edce9d73f75b2b42200f..3c499e97744e02d025f275bc7b9ecd00e5c96758 100644 --- a/test/fuzztest/float64arrayrefnew_fuzzer/BUILD.gn +++ b/test/fuzztest/float64arrayrefnew_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("Float64ArrayRefNewFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/float64arrayrefnew_fuzzer" diff --git a/test/fuzztest/functionrefcall_fuzzer/BUILD.gn b/test/fuzztest/functionrefcall_fuzzer/BUILD.gn index f3e90ba33e1866ba37630306dddcbacc237c203b..26dc8859d9a10af6bc6645aead8a6f18fa9c00f6 100644 --- a/test/fuzztest/functionrefcall_fuzzer/BUILD.gn +++ b/test/fuzztest/functionrefcall_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("FunctionRefCallFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/functionrefcall_fuzzer" diff --git a/test/fuzztest/functionrefconstructor_fuzzer/BUILD.gn b/test/fuzztest/functionrefconstructor_fuzzer/BUILD.gn index c6f02c00f9a009c054647e09d776a79dafa1c6a8..8d323c489be3e6e3a50da9519ae1fc92b0b7c115 100644 --- a/test/fuzztest/functionrefconstructor_fuzzer/BUILD.gn +++ b/test/fuzztest/functionrefconstructor_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("FunctionRefConstructorFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/functionrefconstructor_fuzzer" diff --git a/test/fuzztest/functionrefnew_fuzzer/BUILD.gn b/test/fuzztest/functionrefnew_fuzzer/BUILD.gn index 886930c4c6557cacd06d3f1e05afd1541d4b6fce..5a8decd08dc6ba9f3ee29e5be7d5561576113f64 100644 --- a/test/fuzztest/functionrefnew_fuzzer/BUILD.gn +++ b/test/fuzztest/functionrefnew_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("FunctionRefNewFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/functionrefnew_fuzzer" diff --git a/test/fuzztest/functionrefnewclassfunction_fuzzer/BUILD.gn b/test/fuzztest/functionrefnewclassfunction_fuzzer/BUILD.gn index b2b8a6495853cb7f716fc9113bae1de0b4d07820..9cb1d28aee3715103c4420520d2a9a40ba426424 100644 --- a/test/fuzztest/functionrefnewclassfunction_fuzzer/BUILD.gn +++ b/test/fuzztest/functionrefnewclassfunction_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("FunctionRefNewClassFunctionFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/functionrefnewclassfunction_fuzzer" diff --git a/test/fuzztest/getallocationprofile_fuzzer/BUILD.gn b/test/fuzztest/getallocationprofile_fuzzer/BUILD.gn index 286e68b55be12bbed6df81da8fd44901102d2c96..482d6b212714bbb37479405b1da34abd439eb6d9 100644 --- a/test/fuzztest/getallocationprofile_fuzzer/BUILD.gn +++ b/test/fuzztest/getallocationprofile_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("GetAllocationProfileFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "$js_root/test/fuzztest/getallocationprofile_fuzzer" diff --git a/test/fuzztest/getnativepointerfield_fuzzer/BUILD.gn b/test/fuzztest/getnativepointerfield_fuzzer/BUILD.gn index 8320cb85040c17a6b2f7702722f2b7f82ab3d481..d4d374bf1baced934ccf5e035ea60f355dcd22da 100644 --- a/test/fuzztest/getnativepointerfield_fuzzer/BUILD.gn +++ b/test/fuzztest/getnativepointerfield_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("GetNativePointerFieldFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/getnativepointerfield_fuzzer" diff --git a/test/fuzztest/getwordsarray_fuzzer/BUILD.gn b/test/fuzztest/getwordsarray_fuzzer/BUILD.gn index c461a2529111f8cddac1226528048b390f258214..bd9a6c181fb4d7292b1a189619d3025a03c73b06 100644 --- a/test/fuzztest/getwordsarray_fuzzer/BUILD.gn +++ b/test/fuzztest/getwordsarray_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("GetWordsArrayFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/getwordsarray_fuzzer" diff --git a/test/fuzztest/int16arrayrefnew_fuzzer/BUILD.gn b/test/fuzztest/int16arrayrefnew_fuzzer/BUILD.gn index a367de8bcaba93c10b081bef8fd0ac74258fd787..d512ed6c0922c36d34d09908520569f54be2303c 100644 --- a/test/fuzztest/int16arrayrefnew_fuzzer/BUILD.gn +++ b/test/fuzztest/int16arrayrefnew_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("Int16ArrayRefNewFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/int16arrayrefnew_fuzzer" diff --git a/test/fuzztest/int32arrayrefnew_fuzzer/BUILD.gn b/test/fuzztest/int32arrayrefnew_fuzzer/BUILD.gn index 7c25457d2e8ad3e429b7c13b5a87c81dc1bd3a1e..805bfb4377f7d5834a5176f2a71a4d66512d79d8 100644 --- a/test/fuzztest/int32arrayrefnew_fuzzer/BUILD.gn +++ b/test/fuzztest/int32arrayrefnew_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("Int32ArrayRefNewFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/int32arrayrefnew_fuzzer" diff --git a/test/fuzztest/int8arrayrefnew_fuzzer/BUILD.gn b/test/fuzztest/int8arrayrefnew_fuzzer/BUILD.gn index a0a2f2302f146fd10690f5e768b4f2c984d33a4c..e616dd97d267a78156a39d5e7ee199ba939d4323 100644 --- a/test/fuzztest/int8arrayrefnew_fuzzer/BUILD.gn +++ b/test/fuzztest/int8arrayrefnew_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("Int8ArrayRefNewFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/int8arrayrefnew_fuzzer" diff --git a/test/fuzztest/jsnapideleteserializationdata_fuzzer/BUILD.gn b/test/fuzztest/jsnapideleteserializationdata_fuzzer/BUILD.gn index 3f7aaec1a59a987ae5efb2c7af7384d60e384169..e3fcfb65f456b99d07088bcfbb052abd9ab45602 100644 --- a/test/fuzztest/jsnapideleteserializationdata_fuzzer/BUILD.gn +++ b/test/fuzztest/jsnapideleteserializationdata_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("JSNApiDeleteSerializationDataFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/jsnapideleteserializationdata_fuzzer" diff --git a/test/fuzztest/jsnapideserializevalue_fuzzer/BUILD.gn b/test/fuzztest/jsnapideserializevalue_fuzzer/BUILD.gn index 8a94c93e41fc3b61b256137dfc1c20c6d1f66cb8..d1eb1db23a941ad05b336cbf9bd15b1d095f3057 100644 --- a/test/fuzztest/jsnapideserializevalue_fuzzer/BUILD.gn +++ b/test/fuzztest/jsnapideserializevalue_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("JSNApiDeserializeValueFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/jsnapideserializevalue_fuzzer" diff --git a/test/fuzztest/jsnapisethostpromiserejectiontracker_fuzzer/BUILD.gn b/test/fuzztest/jsnapisethostpromiserejectiontracker_fuzzer/BUILD.gn index 0234e7cf4bf2721e123caf85f6e139f8bf0eab88..7d6c4a83486d3f5d5e0ea462e28bae9793feca52 100644 --- a/test/fuzztest/jsnapisethostpromiserejectiontracker_fuzzer/BUILD.gn +++ b/test/fuzztest/jsnapisethostpromiserejectiontracker_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("JSNApiSetHostPromiseRejectionTrackerFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/jsnapisethostpromiserejectiontracker_fuzzer" diff --git a/test/fuzztest/jsnapistartdebugger_fuzzer/BUILD.gn b/test/fuzztest/jsnapistartdebugger_fuzzer/BUILD.gn index 205e194d764b29132e038e9afac2e352b9d548c7..7fd05c568ebb4c5ea647c6643fda2fbeeab1af38 100644 --- a/test/fuzztest/jsnapistartdebugger_fuzzer/BUILD.gn +++ b/test/fuzztest/jsnapistartdebugger_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("JSNApiStartDebuggerFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/jsnapistartdebugger_fuzzer" diff --git a/test/fuzztest/jsonparse_fuzzer/BUILD.gn b/test/fuzztest/jsonparse_fuzzer/BUILD.gn index e4ea649df3393880109abefe1674f5c93a17adef..4a9755d657bd28f64e961150626daac3009124d0 100644 --- a/test/fuzztest/jsonparse_fuzzer/BUILD.gn +++ b/test/fuzztest/jsonparse_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("JSONParseFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/jsonparse_fuzzer" diff --git a/test/fuzztest/jsonstringify_fuzzer/BUILD.gn b/test/fuzztest/jsonstringify_fuzzer/BUILD.gn index 304b158780761fb3f04aae5ea8d3ab75a2e0a8bc..1d9eeca455fbedfda18e9f3abcc03e13bcb9b31e 100644 --- a/test/fuzztest/jsonstringify_fuzzer/BUILD.gn +++ b/test/fuzztest/jsonstringify_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("JSONStringifyFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/jsonstringify_fuzzer" diff --git a/test/fuzztest/nativepointernew1_fuzzer/BUILD.gn b/test/fuzztest/nativepointernew1_fuzzer/BUILD.gn index 4694089f142c03e68db58554387c233055f7ad78..52470da0a3d022f00df304260897bd0b5f86404e 100644 --- a/test/fuzztest/nativepointernew1_fuzzer/BUILD.gn +++ b/test/fuzztest/nativepointernew1_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("NativePointerNew1FuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/nativepointernew1_fuzzer" diff --git a/test/fuzztest/nativepointernew2_fuzzer/BUILD.gn b/test/fuzztest/nativepointernew2_fuzzer/BUILD.gn index 7eea6d369595b249e93ff11c0f553ec8faef17bb..6775693825dc7af0909ff35f1b76bc1eb1ef4bef 100644 --- a/test/fuzztest/nativepointernew2_fuzzer/BUILD.gn +++ b/test/fuzztest/nativepointernew2_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("NativePointerNew2FuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/nativepointernew2_fuzzer" diff --git a/test/fuzztest/newbigintbyint64_fuzzer/BUILD.gn b/test/fuzztest/newbigintbyint64_fuzzer/BUILD.gn index 501d284def0f3ee8810ff35a081695e2fb38e5a6..e76b26d52059dcc90811c07b74b1bad279300dc6 100644 --- a/test/fuzztest/newbigintbyint64_fuzzer/BUILD.gn +++ b/test/fuzztest/newbigintbyint64_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("NewBigIntByInt64FuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/newbigintbyint64_fuzzer" diff --git a/test/fuzztest/newbigintbyuint64_fuzzer/BUILD.gn b/test/fuzztest/newbigintbyuint64_fuzzer/BUILD.gn index 73204bde9a26ede63588e1da799c8fb4ac3a3484..34e13188417928fade5c5b81201d889022872817 100644 --- a/test/fuzztest/newbigintbyuint64_fuzzer/BUILD.gn +++ b/test/fuzztest/newbigintbyuint64_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("NewBigIntByUint64FuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/newbigintbyuint64_fuzzer" diff --git a/test/fuzztest/notifyapplicationstate_fuzzer/BUILD.gn b/test/fuzztest/notifyapplicationstate_fuzzer/BUILD.gn index a8079b6569a83ef730cb43b0f31b584aa2b9f9a7..f1ed9c7a41aa546502da3090f6c9dbee2ca3edb6 100644 --- a/test/fuzztest/notifyapplicationstate_fuzzer/BUILD.gn +++ b/test/fuzztest/notifyapplicationstate_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("NotifyApplicationStateFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "$js_root/test/fuzztest/notifyapplicationstate_fuzzer" diff --git a/test/fuzztest/notifyidletime_fuzzer/BUILD.gn b/test/fuzztest/notifyidletime_fuzzer/BUILD.gn index 799902c393091340112b643263202496eff3ef4b..11576c2472ffce4effe465853b470d0ac1a2c169 100644 --- a/test/fuzztest/notifyidletime_fuzzer/BUILD.gn +++ b/test/fuzztest/notifyidletime_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("NotifyIdleTimeFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "$js_root/test/fuzztest/notifyidletime_fuzzer" diff --git a/test/fuzztest/numberrefnewdouble_fuzzer/BUILD.gn b/test/fuzztest/numberrefnewdouble_fuzzer/BUILD.gn index 7ae308b0b51f43fd7f6b9ad949be2fd6536d60f2..554588d4cc940e03ff83144127562cde99235342 100644 --- a/test/fuzztest/numberrefnewdouble_fuzzer/BUILD.gn +++ b/test/fuzztest/numberrefnewdouble_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("NumberRefNewDoubleFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/numberrefnewdouble_fuzzer" diff --git a/test/fuzztest/numberrefnewint32_fuzzer/BUILD.gn b/test/fuzztest/numberrefnewint32_fuzzer/BUILD.gn index 291d2648889b39b82804ed0552b3efdbff73f011..bfbd284a8101cfde9d2a6370438b8efba04d8f36 100644 --- a/test/fuzztest/numberrefnewint32_fuzzer/BUILD.gn +++ b/test/fuzztest/numberrefnewint32_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("NumberRefNewInt32FuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/numberrefnewint32_fuzzer" diff --git a/test/fuzztest/numberrefnewint64_fuzzer/BUILD.gn b/test/fuzztest/numberrefnewint64_fuzzer/BUILD.gn index c1de609b34ea7cc22778b15670503b204181daf4..361a5e59459207bd64a3ee50c91b519169139bce 100644 --- a/test/fuzztest/numberrefnewint64_fuzzer/BUILD.gn +++ b/test/fuzztest/numberrefnewint64_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("NumberRefNewInt64FuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/numberrefnewint64_fuzzer" diff --git a/test/fuzztest/numberrefnewuint32_fuzzer/BUILD.gn b/test/fuzztest/numberrefnewuint32_fuzzer/BUILD.gn index e30192fe5e25d14d5fbe892c3a66b6223cb51eeb..f5be57262c362ce16cdb0c96e4b128e062b0afd0 100644 --- a/test/fuzztest/numberrefnewuint32_fuzzer/BUILD.gn +++ b/test/fuzztest/numberrefnewuint32_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("NumberRefNewUint32FuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/numberrefnewuint32_fuzzer" diff --git a/test/fuzztest/objectdelete_fuzzer/BUILD.gn b/test/fuzztest/objectdelete_fuzzer/BUILD.gn index ebe45a3f43cb550eab1725fbfc0f9a674839ed26..ce07a27155caf3641fec274285bb9e9364c470ce 100644 --- a/test/fuzztest/objectdelete_fuzzer/BUILD.gn +++ b/test/fuzztest/objectdelete_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("ObjectDeleteFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/objectdelete_fuzzer" diff --git a/test/fuzztest/objectget_fuzzer/BUILD.gn b/test/fuzztest/objectget_fuzzer/BUILD.gn index 83b817dbebee31ef9603968fafaf92d5e4dd2f25..658f1d9b2086edd3ea2e82464b45466d4ec6a8f8 100644 --- a/test/fuzztest/objectget_fuzzer/BUILD.gn +++ b/test/fuzztest/objectget_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("ObjectGetFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/objectget_fuzzer" diff --git a/test/fuzztest/objecthas_fuzzer/BUILD.gn b/test/fuzztest/objecthas_fuzzer/BUILD.gn index 3e2f568f8d4a6a7bef8cece9e9626fc7f2c38327..8de9b01f9fde03dd7e32ac42bb707392b75c43ef 100644 --- a/test/fuzztest/objecthas_fuzzer/BUILD.gn +++ b/test/fuzztest/objecthas_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("ObjectHasFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/objecthas_fuzzer" diff --git a/test/fuzztest/objectset_fuzzer/BUILD.gn b/test/fuzztest/objectset_fuzzer/BUILD.gn index a4e8c419384e47f94e14d518d61f8f71e770e34e..6102e3af818424e4493d40cb909e3c21a1b7a18d 100644 --- a/test/fuzztest/objectset_fuzzer/BUILD.gn +++ b/test/fuzztest/objectset_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("ObjectSetFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/objectset_fuzzer" diff --git a/test/fuzztest/setcpusamplinginterval_fuzzer/BUILD.gn b/test/fuzztest/setcpusamplinginterval_fuzzer/BUILD.gn index 6c98181a00dad704cfd3aa561e7212351985e657..8427355d30f7ae27a0a57e1a7d736fe5f48d3b84 100644 --- a/test/fuzztest/setcpusamplinginterval_fuzzer/BUILD.gn +++ b/test/fuzztest/setcpusamplinginterval_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("SetCpuSamplingIntervalFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "$js_root/test/fuzztest/setcpusamplinginterval_fuzzer" diff --git a/test/fuzztest/setnativepointerfield_fuzzer/BUILD.gn b/test/fuzztest/setnativepointerfield_fuzzer/BUILD.gn index d9a61f5c96f7850a13f7b03729cc398e19b9397f..f44fd2f0f77aeff603045c7af4df22f4e886b275 100644 --- a/test/fuzztest/setnativepointerfield_fuzzer/BUILD.gn +++ b/test/fuzztest/setnativepointerfield_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("SetNativePointerFieldFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/setnativepointerfield_fuzzer" diff --git a/test/fuzztest/setnativepointerfieldcount_fuzzer/BUILD.gn b/test/fuzztest/setnativepointerfieldcount_fuzzer/BUILD.gn index 7ea101513cd0a008bd239b93ccbe2188be25bbe8..3492d59c1aa1d2788212e9cd3b5a4919e2ae81d7 100644 --- a/test/fuzztest/setnativepointerfieldcount_fuzzer/BUILD.gn +++ b/test/fuzztest/setnativepointerfieldcount_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("SetNativePointerFieldCountFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "$js_root/test/fuzztest/setnativepointerfieldcount_fuzzer" diff --git a/test/fuzztest/snapshotserializebuiltins_fuzzer/BUILD.gn b/test/fuzztest/snapshotserializebuiltins_fuzzer/BUILD.gn index 29e86c747dd45c7539f79bacc183b4da99b6f1b0..ca69bb79833eee29a6f2be508087a4800ba16854 100644 --- a/test/fuzztest/snapshotserializebuiltins_fuzzer/BUILD.gn +++ b/test/fuzztest/snapshotserializebuiltins_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("SnapshotSerializeBuiltinsFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/snapshotserializebuiltins_fuzzer" diff --git a/test/fuzztest/snapshotserializedeserialize_fuzzer/BUILD.gn b/test/fuzztest/snapshotserializedeserialize_fuzzer/BUILD.gn index af8d5a41a7558f0da77e0b543f6df8fb009db91d..891f7af6f9080b227bc393bdf4f82b0be595f07e 100644 --- a/test/fuzztest/snapshotserializedeserialize_fuzzer/BUILD.gn +++ b/test/fuzztest/snapshotserializedeserialize_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("SnapshotSerializeDeserializeFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/snapshotserializedeserialize_fuzzer" diff --git a/test/fuzztest/snapshotserializehugeobject_fuzzer/BUILD.gn b/test/fuzztest/snapshotserializehugeobject_fuzzer/BUILD.gn index 6c74364132b41b37fed979782e96aaee5890944d..fe3a772dd1f2767425a0c85ba13e0d29c44a3f40 100644 --- a/test/fuzztest/snapshotserializehugeobject_fuzzer/BUILD.gn +++ b/test/fuzztest/snapshotserializehugeobject_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("SnapshotSerializeHugeObjectFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/snapshotserializehugeobject_fuzzer" diff --git a/test/fuzztest/snapshotserializerange_fuzzer/BUILD.gn b/test/fuzztest/snapshotserializerange_fuzzer/BUILD.gn index 7b4de74c0ac68d2b8cfa3c43163c70046aa7b524..76716ca5f5ddb3b1bd4a4196f5958f20017d97cb 100644 --- a/test/fuzztest/snapshotserializerange_fuzzer/BUILD.gn +++ b/test/fuzztest/snapshotserializerange_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("SnapshotSerializeRangeFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/snapshotserializerange_fuzzer" diff --git a/test/fuzztest/startcpuprofilerforfile_fuzzer/BUILD.gn b/test/fuzztest/startcpuprofilerforfile_fuzzer/BUILD.gn index 9d5d98241ffe58ffceb51db09b3a67524fd68b34..1c1ab332b33ea5a68754aade3c88194bc25ffffe 100644 --- a/test/fuzztest/startcpuprofilerforfile_fuzzer/BUILD.gn +++ b/test/fuzztest/startcpuprofilerforfile_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("StartCpuProfilerForFileFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/startcpuprofilerforfile_fuzzer" diff --git a/test/fuzztest/startsampling_fuzzer/BUILD.gn b/test/fuzztest/startsampling_fuzzer/BUILD.gn index 8e7ba473c37e150489837d59b03f0385ec47d867..a1c82763c096e154349b8acee07eda433effd33c 100644 --- a/test/fuzztest/startsampling_fuzzer/BUILD.gn +++ b/test/fuzztest/startsampling_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("StartSamplingFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "$js_root/test/fuzztest/startsampling_fuzzer" diff --git a/test/fuzztest/stopsampling_fuzzer/BUILD.gn b/test/fuzztest/stopsampling_fuzzer/BUILD.gn index d85d4673d6bf4b04f324f29932d29bca108e4d33..6f89735719f99dc40a1f28328c96ba99655ff8d7 100644 --- a/test/fuzztest/stopsampling_fuzzer/BUILD.gn +++ b/test/fuzztest/stopsampling_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ####################################fuzztest################################## ohos_fuzztest("StopSamplingFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "$js_root/test/fuzztest/stopsampling_fuzzer" diff --git a/test/fuzztest/stringrefnewfromutf8_fuzzer/BUILD.gn b/test/fuzztest/stringrefnewfromutf8_fuzzer/BUILD.gn index 8576789051fdc0767d1d6849071dd2892bde0ca0..d75edab5fc93461e0a027d973504cf30867550cd 100644 --- a/test/fuzztest/stringrefnewfromutf8_fuzzer/BUILD.gn +++ b/test/fuzztest/stringrefnewfromutf8_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("StringRefNewFromUtf8FuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/stringrefnewfromutf8_fuzzer" diff --git a/test/fuzztest/stringrefwriteutf8_fuzzer/BUILD.gn b/test/fuzztest/stringrefwriteutf8_fuzzer/BUILD.gn index 8757b2fe82e025a027f0acd4a703514c7d3c1170..f0c588e3934e7c84dd262cda61310a75764d474c 100644 --- a/test/fuzztest/stringrefwriteutf8_fuzzer/BUILD.gn +++ b/test/fuzztest/stringrefwriteutf8_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("StringRefWriteUtf8FuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/stringrefwriteutf8_fuzzer" diff --git a/test/fuzztest/uint16arrayrefnew_fuzzer/BUILD.gn b/test/fuzztest/uint16arrayrefnew_fuzzer/BUILD.gn index 9fd4cfd81f4beed53737ae0a70dec1c63a7d16df..ffc85999219f02fc0a5e5274f23d68492c99039e 100644 --- a/test/fuzztest/uint16arrayrefnew_fuzzer/BUILD.gn +++ b/test/fuzztest/uint16arrayrefnew_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("Uint16ArrayRefNewFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/uint16arrayrefnew_fuzzer" diff --git a/test/fuzztest/uint32arrayrefnew_fuzzer/BUILD.gn b/test/fuzztest/uint32arrayrefnew_fuzzer/BUILD.gn index c31bf5f9df9f0481721e55f7adf6af3b54885609..e57993cfb9c1dd55e74c764fc5eba9e61d00a6ff 100644 --- a/test/fuzztest/uint32arrayrefnew_fuzzer/BUILD.gn +++ b/test/fuzztest/uint32arrayrefnew_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("Uint32ArrayRefNewFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/uint32arrayrefnew_fuzzer" diff --git a/test/fuzztest/uint8arrayrefnew_fuzzer/BUILD.gn b/test/fuzztest/uint8arrayrefnew_fuzzer/BUILD.gn index f127324453190a9603f1288b1ee8932c19104958..20e0d67dc98a00630f2a91a2cf17faa4a1bc8143 100644 --- a/test/fuzztest/uint8arrayrefnew_fuzzer/BUILD.gn +++ b/test/fuzztest/uint8arrayrefnew_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("Uint8ArrayRefNewFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/uint8arrayrefnew_fuzzer" diff --git a/test/fuzztest/uint8clampedarrayrefnew_fuzzer/BUILD.gn b/test/fuzztest/uint8clampedarrayrefnew_fuzzer/BUILD.gn index 1310706aa8966017397f74c0f0a6bc3d087e1bba..5f0253b0345a61aa5c084b53c954b1c880a545b7 100644 --- a/test/fuzztest/uint8clampedarrayrefnew_fuzzer/BUILD.gn +++ b/test/fuzztest/uint8clampedarrayrefnew_fuzzer/BUILD.gn @@ -19,7 +19,7 @@ import("//build/ohos.gni") ##################################fuzztest##################################### ohos_fuzztest("Uint8ClampedArrayRefNewFuzzTest") { - module_out_path = "arkcompiler/ets_runtime" + module_out_path = ets_runtime_output_path fuzz_config_file = "//arkcompiler/ets_runtime/test/fuzztest/uint8clampedarrayrefnew_fuzzer" diff --git a/test/moduletest/BUILD.gn b/test/moduletest/BUILD.gn index f32842f8deeae8794b72f45e7aa07f06c5ad639a..0caea5a8d0fbfc4c2047d7ccea6e838f5e74dfdc 100644 --- a/test/moduletest/BUILD.gn +++ b/test/moduletest/BUILD.gn @@ -17,8 +17,14 @@ group("ark_js_moduletest") { test_list = [ "allocatearraybuffer", "array", + "arrayfindlast", "arrayforeach", "arrayjoin", + "arraytoreversed", + "arraytospliced", + "arraywith", + "arraysort", + "arrayprotochange", "assignproxy", "async", "asyncgenerator", @@ -43,16 +49,22 @@ group("ark_js_moduletest") { "ecmastringtable", "equal", "errorhelper", + "errorcause", + "flatten", "forawaitof", "forin", "fortest", + "funcprotochangeobjectandnew", + "functionapply", "generator", "getpropertybyindex", "getunmappedargs", + "global", "globalaccessor", "globalrecord", "globalthis", - "helloworld", + + # "helloworld", "instanceofic", "intl", "jsonparser", @@ -74,27 +86,43 @@ group("ark_js_moduletest") { "newobjdynrange", "objectcloneproperties", "objoperate", + "objseal", "promise", + "propertydetector", "protobuf", "proxy", "regexpcallthrow", + "regexpflagd", "require", "setobjectwithproto", "spreadoperator", "stackoverflow", + "storeicbyname", + "string", "stubbuilder", "throwdyn", "trycatch", "typearray", + "typedarrayat", + "typedarrayfindlast", + "typedarraynan", + "typedarraytosorted", + "typedarraywith", "watch", + "weakcollectionswithsymbol", "wrapperclassfunc", "yieldstar", "esmnestedimportcjs", + "regexp", + "deregistermodule", ] deps = [] foreach(test, test_list) { - deps += [ "${test}:${test}Action" ] + deps += [ + "${test}:${test}Action", + "${test}:${test}ContextAction", + ] } if (!is_debug) { @@ -108,7 +136,10 @@ group("ark_js_moduletest") { ] foreach(test, release_test_list) { - deps += [ "${test}:${test}Action" ] + deps += [ + "${test}:${test}Action", + "${test}:${test}ContextAction", + ] } } } @@ -119,8 +150,11 @@ group("ark_asm_test") { test_list = [ "allocatearraybuffer", "array", + "arrayfindlast", "arrayforeach", "arrayjoin", + "arraysort", + "arrayprotochange", "asmstackoverflow", "assignproxy", "async", @@ -141,14 +175,20 @@ group("ark_asm_test") { "dyninstruction", "ecmastringtable", "equal", + "errorcause", + "flatten", "forin", "fortest", + "funcprotochangeobjectandnew", + "functionapply", "generator", "getunmappedargs", + "global", "globalaccessor", "globalrecord", "globalthis", - "helloworld", + + # "helloworld", "instanceofic", "intl", "jsonparser", @@ -164,23 +204,37 @@ group("ark_asm_test") { "newobjdynrange", "objectcloneproperties", "objoperate", + "objseal", "promise", + "propertydetector", "proxy", "regexpcallthrow", + "regexpflagd", "setobjectwithproto", "spreadoperator", "stackoverflow", "stubbuilder", "throwdyn", "trycatch", + "typedarrayat", + "typedarrayfindlast", + "typedarraynan", + "typedarraytosorted", + "typedarraywith", "watch", + "weakcollectionswithsymbol", "wrapperclassfunc", "yieldstar", + "regexp", + "deregistermodule", ] deps = [] foreach(test, test_list) { - deps += [ "${test}:${test}AsmAction" ] + deps += [ + "${test}:${test}AsmAction", + "${test}:${test}AsmContextAction", + ] } if (!is_debug) { @@ -194,7 +248,10 @@ group("ark_asm_test") { ] foreach(test, release_test_list) { - deps += [ "${test}:${test}AsmAction" ] + deps += [ + "${test}:${test}AsmAction", + "${test}:${test}AsmContextAction", + ] } } } @@ -204,8 +261,10 @@ group("ark_asm_single_step_test") { test_list = [ "allocatearraybuffer", + "arrayfindlast", "arrayforeach", "arrayjoin", + "arrayprotochange", "asmstackoverflow", "assignproxy", "async", @@ -222,14 +281,19 @@ group("ark_asm_single_step_test") { "dynamicimport", "dyninstruction", "ecmastringtable", + "errorcause", "forin", "fortest", + "funcprotochangeobjectandnew", + "functionapply", "generator", "getunmappedargs", + "global", "globalaccessor", "globalrecord", "globalthis", - "helloworld", + + # "helloworld", "instanceofic", "jsonparser", "jsonstringifier", @@ -243,22 +307,34 @@ group("ark_asm_single_step_test") { "newobjdynrange", "objectcloneproperties", "objoperate", + "objseal", "promise", + "propertydetector", "proxy", "regexpcallthrow", + "regexpflagd", "setobjectwithproto", "spreadoperator", "stackoverflow", "stubbuilder", "throwdyn", "trycatch", + "typedarrayat", + "typedarrayfindlast", + "typedarraynan", + "typedarraytosorted", + "typedarraywith", "watch", + "weakcollectionswithsymbol", "yieldstar", ] deps = [] foreach(test, test_list) { - deps += [ "${test}:${test}AsmSingleStepAction" ] + deps += [ + "${test}:${test}AsmSingleStepAction", + "${test}:${test}AsmSingleStepContextAction", + ] } if (!is_debug) { @@ -271,7 +347,10 @@ group("ark_asm_single_step_test") { ] foreach(test, release_test_list) { - deps += [ "${test}:${test}AsmSingleStepAction" ] + deps += [ + "${test}:${test}AsmSingleStepAction", + "${test}:${test}AsmSingleStepContextAction", + ] } } } diff --git a/test/moduletest/allocatearraybuffer/allocatearraybuffer.js b/test/moduletest/allocatearraybuffer/allocatearraybuffer.js index 811e444e7f987398e92f5ada2ad7e09a7b49b57e..6f1ecf359ce1262cb926d686e8d60dc8f43aae39 100644 --- a/test/moduletest/allocatearraybuffer/allocatearraybuffer.js +++ b/test/moduletest/allocatearraybuffer/allocatearraybuffer.js @@ -21,4 +21,14 @@ */ var newTarget = function() {}.bind(null); var arrayBuffer = Reflect.construct(ArrayBuffer, [16], newTarget); -print(arrayBuffer.length); \ No newline at end of file +print(arrayBuffer.length); + +/* + * @tc.name:allocatearraybuffer + * @tc.desc:test DataView + * @tc.type: FUNC + * @tc.require: issueI5NO8G + */ +const dataview = new DataView(new ArrayBuffer(64)); +dataview.setInt16(0,-1); +print(dataview.getFloat64("cas")); \ No newline at end of file diff --git a/test/moduletest/allocatearraybuffer/expect_output.txt b/test/moduletest/allocatearraybuffer/expect_output.txt index 7645b4eb0ea1170d80ab5c913c07216458fb4ee7..0dd1a545853d79683f0d10b83534b1dd6f8430e9 100644 --- a/test/moduletest/allocatearraybuffer/expect_output.txt +++ b/test/moduletest/allocatearraybuffer/expect_output.txt @@ -12,3 +12,4 @@ # limitations under the License. undefined +NaN diff --git a/test/moduletest/array/array.js b/test/moduletest/array/array.js index 6a586294cb918d76338480d3e3181029dde15870..e87988497cc26e772c5f15eb77d1548dfa91b007 100644 --- a/test/moduletest/array/array.js +++ b/test/moduletest/array/array.js @@ -131,7 +131,218 @@ const v20 = new Array(2); let v21; try { v21 = v20.pop(); - print(v21) + print(v21); } catch (error) { } + +var arr21 = [1,2,3,4,,6]; +print(arr21.at(0)); +print(arr21.at(5)); +print(arr21.at(-1)); +print(arr21.at(6)); +print(arr21.at('1.9')); +print(arr21.at(true)); +var arr22 = arr21.toReversed(); +print(arr22) +print(arr21) +var arr23 = [1, 2, 3, 4, 5]; +print(arr23.with(2, 6)); // [1, 2, 6, 4, 5] +print(arr23); // [1, 2, 3, 4, 5] + +const months = ["Mar", "Jan", "Feb", "Dec"]; +const sortedMonths = months.toSorted(); +print(sortedMonths); // ['Dec', 'Feb', 'Jan', 'Mar'] +print(months); // ['Mar', 'Jan', 'Feb', 'Dec'] + +const values = [1, 10, 21, 2]; +const sortedValues = values.toSorted((a, b) => {return a- b}); +print(sortedValues); // [1, 2, 10, 21] +print(values); // [1, 10, 21, 2] + +const arrs = new Array(6); +for (let i = 0; i < 6; i++) { + var str = i.toString(); + if (i != 1) { + arrs[i] = str; + } +} +arrs.reverse(); +print(arrs); // 5,4,3,2,,0 + +function handleExpectedErrorCaught(prompt, e) { + if (e instanceof Error) { + print(`Expected ${e.name} caught in ${prompt}.`); + } else { + print(`Expected error message caught in ${prompt}.`); + } +} + +function handleUnexpectedErrorCaught(prompt, e) { + if (e instanceof Error) { + print(`Unexpected ${e.name} caught in ${prompt}: ${e.message}`); + if (typeof e.stack !== 'undefined') { + print(`Stacktrace:\n${e.stack}`); + } + } else { + print(`Unexpected error message caught in ${prompt}: ${e}`); + } +} + +// Test cases for reverse() +print("======== Begin: Array.prototype.reverse() ========"); +try { + const arr0 = []; + print(`arr0.reverse() === arr0 ? ${arr0.reverse() === arr0}`); // true + print(`arr0.length after reverse() called = ${arr0.length}`); // 0 + + const arr1 = [1]; + print(`arr1.reverse() === arr1 ? ${arr1.reverse() === arr1}`); // true + print(`arr1 after reverse() called = ${arr1}`); // 1 + + const arrWithHoles = []; + arrWithHoles[1] = 1; + arrWithHoles[4] = 4; + arrWithHoles[6] = undefined; + arrWithHoles.length = 10; + // arrWithHoles = [Hole, 1, Hole, Hole, 4, Hole, undefined, Hole, Hole, Hole] + print(`arrWithHoles before reverse() called = ${arrWithHoles}`); // ,1,,,4,,,,, + print(`arrWithHoles.reverse() = ${arrWithHoles.reverse()}`); // ,,,,,4,,,1, + print(`arrWithHoles after reverse() called = ${arrWithHoles}`); // ,,,,,4,,,1, +} catch (e) { + handleUnexpectedErrorCaught(e); +} +print("======== End: Array.prototype.reverse() ========"); + +print("======== Begin: Array.prototype.indexOf() & Array.prototype.lastIndexOf() ========"); +// Test case for indexOf() and lastIndexOf() +try { + const arr = [0, 10, 20]; + arr.length = 10; + arr[3] = 80; + arr[4] = 40; + arr[6] = undefined; + arr[7] = 10; + arr[8] = "80"; + print("arr = [0, 10, 20, 80, 40, Hole, undefined, 10, \"80\", Hole]"); + // prompt1, results1, prompt2, results2, ... + const resultGroups = [ + "Group 1: 0 <= fromIndex < arr.length", [ + arr.indexOf(40), // 4 + arr.indexOf(40, 5), // -1 + arr.indexOf(10), // 1 + arr.indexOf(10, 2), // 7 + arr.lastIndexOf(40), // 4 + arr.lastIndexOf(40, 3), // -1 + arr.lastIndexOf(10), // 7 + arr.lastIndexOf(10, 6), // 1 + ], + "Group 2: -arr.length <= fromIndex < 0", [ + arr.indexOf(40, -4), // -1 + arr.indexOf(40, -8), // 4 + arr.indexOf(10, -4), // 7 + arr.indexOf(10, -10), // 1 + arr.lastIndexOf(40, -4), // 4 + arr.lastIndexOf(40, -8), // -1 + arr.lastIndexOf(10, -4), // 1 + arr.lastIndexOf(10, -10), // -1 + arr.indexOf(0, -arr.length), // 0 + arr.indexOf(10, -arr.length), // 1 + arr.lastIndexOf(0, -arr.length), // 0 + arr.lastIndexOf(10, -arr.length), // -1 + ], + "Group 3: fromIndex >= arr.length", [ + arr.indexOf(0, arr.length), // -1 + arr.indexOf(0, arr.length + 10), // -1 + arr.indexOf(10, arr.length), + arr.indexOf(10, arr.length + 10), + arr.lastIndexOf(0, arr.length), // 0 + arr.lastIndexOf(0, arr.length + 10), // 0 + ], + "Group 4: fromIndex < -arr.length", [ + arr.indexOf(0, -arr.length - 10), // 0 + arr.lastIndexOf(0, -arr.length - 10) // -1 + ], + "Group 5: fromIndex in [Infinity, -Infinity]", [ + arr.indexOf(10, -Infinity), // 1 + arr.indexOf(10, +Infinity), // -1 + arr.lastIndexOf(10, -Infinity), // -1 + arr.lastIndexOf(10, +Infinity) // 7 + ], + "Group 6: fromIndex is NaN", [ + arr.indexOf(0, NaN), // 0 + arr.indexOf(10, NaN), // 1 + arr.lastIndexOf(0, NaN), // 0 + arr.lastIndexOf(10, NaN), // -1 + ], + "Group 7: fromIndex is not of type 'number'", [ + arr.indexOf(10, '2'), // 7 + arr.lastIndexOf(10, '2'), // 1 + arr.indexOf(10, { valueOf() { return 3; } }), // 7 + arr.indexOf(10, { valueOf() { return 3; } }), // 1 + ], + "Group 8: Strict equality checking", [ + arr.indexOf("80"), // 8 + arr.lastIndexOf(80), // 3 + ], + "Group 9: Searching undefined and null", [ + arr.indexOf(), // 6 + arr.indexOf(undefined), // 6 + arr.indexOf(null), // -1 + arr.lastIndexOf(), // 6 + arr.lastIndexOf(undefined), // 6 + arr.lastIndexOf(null) // -1 + ] + ]; + for (let i = 0; i < resultGroups.length; i += 2) { + print(`${resultGroups[i]}: ${resultGroups[i + 1]}`); + } + + print("Group 10: fromIndex with side effects:"); + let accessCount = 0; + const arrProxyHandler = { + has(target, key) { + accessCount += 1; + return key in target; + } + }; + // Details on why accessCount = 6 can be seen in ECMAScript specifications: + // https://tc39.es/ecma262/multipage/indexed-collections.html#sec-array.prototype.indexof + accessCount = 0; + const arr2 = new Proxy([10, 20, 30, 40, 50, 60], arrProxyHandler); + const result2 = arr2.indexOf(30, { + valueOf() { + arr2.length = 2; // Side effects to arr2 + return 0; + } + }); // Expected: -1 (with accessCount = 6) + print(` - indexOf: result = ${result2}, accessCount = ${accessCount}`); + // Details on why accessCount = 6 can be seen in ECMAScript specifications: + // https://tc39.es/ecma262/multipage/indexed-collections.html#sec-array.prototype.lastindexof + accessCount = 0; + const arr3 = new Proxy([15, 25, 35, 45, 55, 65], arrProxyHandler); + const result3 = arr3.lastIndexOf(45, { + valueOf() { + arr3.length = 2; // Side effects to arr3 + return 5; + } + }); // Expected: -1 (with accessCount = 6) + print(` - lastIndexOf: result = ${result3}, accesscount = ${accessCount}`); + + print("Group 11: fromIndex that triggers exceptions:"); + for (let [prompt, fromIndex] of [ ["bigint", 1n], ["symbol", Symbol()] ]) { + for (let method of [ Array.prototype.indexOf, Array.prototype.lastIndexOf ]) { + try { + const result = method.call(arr, 10, fromIndex); + print(`ERROR: Unexpected result (which is ${result}) returned by method '${method.name}': ` + + `Expects a TypeError thrown for fromIndex type '${prompt}'.`); + } catch (e) { + // Expects a TypeError thrown and caught here. + handleExpectedErrorCaught(`${method.name} when fromIndex is ${prompt}`, e); + } + } + } +} catch (e) { + handleUnexpectedErrorCaught(e); +} +print("======== End: Array.prototype.indexOf() & Array.prototype.lastIndexOf() ========"); diff --git a/test/moduletest/array/expect_output.txt b/test/moduletest/array/expect_output.txt index 9a11f2ddea13d5fb4cab0e93f88af0930d489308..121f17f4755e29e46fa899f2472f06b7d9889aa3 100644 --- a/test/moduletest/array/expect_output.txt +++ b/test/moduletest/array/expect_output.txt @@ -69,3 +69,47 @@ undefined -1 The NewTarget is undefined undefined +1 +6 +6 +undefined +2 +2 +6,,4,3,2,1 +1,2,3,4,,6 +1,2,6,4,5 +1,2,3,4,5 +Dec,Feb,Jan,Mar +Mar,Jan,Feb,Dec +1,2,10,21 +1,10,21,2 +5,4,3,2,,0 +======== Begin: Array.prototype.reverse() ======== +arr0.reverse() === arr0 ? true +arr0.length after reverse() called = 0 +arr1.reverse() === arr1 ? true +arr1 after reverse() called = 1 +arrWithHoles before reverse() called = ,1,,,4,,,,, +arrWithHoles.reverse() = ,,,,,4,,,1, +arrWithHoles after reverse() called = ,,,,,4,,,1, +======== End: Array.prototype.reverse() ======== +======== Begin: Array.prototype.indexOf() & Array.prototype.lastIndexOf() ======== +arr = [0, 10, 20, 80, 40, Hole, undefined, 10, "80", Hole] +Group 1: 0 <= fromIndex < arr.length: 4,-1,1,7,4,-1,7,1 +Group 2: -arr.length <= fromIndex < 0: -1,4,7,1,4,-1,1,-1,0,1,0,-1 +Group 3: fromIndex >= arr.length: -1,-1,-1,-1,0,0 +Group 4: fromIndex < -arr.length: 0,-1 +Group 5: fromIndex in [Infinity, -Infinity]: 1,-1,-1,7 +Group 6: fromIndex is NaN: 0,1,0,-1 +Group 7: fromIndex is not of type 'number': 7,1,7,7 +Group 8: Strict equality checking: 8,3 +Group 9: Searching undefined and null: 6,6,-1,6,6,-1 +Group 10: fromIndex with side effects: + - indexOf: result = -1, accessCount = 6 + - lastIndexOf: result = -1, accesscount = 6 +Group 11: fromIndex that triggers exceptions: +Expected TypeError caught in indexOf when fromIndex is bigint. +Expected TypeError caught in lastIndexOf when fromIndex is bigint. +Expected TypeError caught in indexOf when fromIndex is symbol. +Expected TypeError caught in lastIndexOf when fromIndex is symbol. +======== End: Array.prototype.indexOf() & Array.prototype.lastIndexOf() ======== diff --git a/test/moduletest/arrayfindlast/BUILD.gn b/test/moduletest/arrayfindlast/BUILD.gn new file mode 100755 index 0000000000000000000000000000000000000000..4afb17fad30f070b09e7dc491e44cf612ed1fbed --- /dev/null +++ b/test/moduletest/arrayfindlast/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("arrayfindlast") { + deps = [] +} diff --git a/test/moduletest/arrayfindlast/arrayfindlast.js b/test/moduletest/arrayfindlast/arrayfindlast.js new file mode 100644 index 0000000000000000000000000000000000000000..99af2fc00def15d04b3802a64e49190ba49faf55 --- /dev/null +++ b/test/moduletest/arrayfindlast/arrayfindlast.js @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * @tc.name:arrayfindlast + * @tc.desc:test Array.findLast and Array.findLastIndex + * @tc.type: FUNC + * @tc.require: issueI7LP2E + */ + +var arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; +var result = -1; +result = arr.findLast((element, index, array) => { + array.length = 5; + return (element == 5); +}); +print(result); +result = arr.findLast((element) => { + return element > 2; +}); +print(result); + +result = arr.findLastIndex((element, index, array) => { + if (array.length == 5) { + array.push(100); + } + return (element == 100); +}); +print(result); +result = arr.findLastIndex((element, index, array) => { + return (element == 100); +}); +print(result); + +var arr2 = new Array(1025); +result = arr2.findLast((element, index, array) => { + return (element == undefined); +}); +print(result); + +result = arr2.findLastIndex((element, index, array) => { + return (element == undefined); +}); +print(result); + +result = arr2.findLastIndex((element, index, array) => { + array[5] = 100; + return (element == 100); +}); +print(result); \ No newline at end of file diff --git a/test/moduletest/arrayfindlast/expect_output.txt b/test/moduletest/arrayfindlast/expect_output.txt new file mode 100755 index 0000000000000000000000000000000000000000..be01ebec875f447df375d388068faf3768a5efcb --- /dev/null +++ b/test/moduletest/arrayfindlast/expect_output.txt @@ -0,0 +1,20 @@ +# Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +undefined +4 +-1 +5 +undefined +1024 +5 diff --git a/test/moduletest/arrayjoin/arrayjoin.js b/test/moduletest/arrayjoin/arrayjoin.js index 909ca2a9ce4b53a43a2de4def6bbcb7e53abcdba..aa78bed849952d93866aabccbacd247802cd0f1b 100644 --- a/test/moduletest/arrayjoin/arrayjoin.js +++ b/test/moduletest/arrayjoin/arrayjoin.js @@ -28,4 +28,27 @@ print(str2); const arr = [] arr.length = 3 var str3 = JSON.stringify(arr.join("0")); -print(str3) \ No newline at end of file +print(str3) + +// test circular reference +var arr1 = [1]; +arr1.push(arr1); +arr1.push(arr1); +print(arr1.toString()); +print(arr1.toString()); + +var arr2 = [1]; +var arr3 = [2]; +arr2[10] = arr3; +arr3[10] = arr2; +print(arr2.toString()); +print(arr2.toString()); + +var arr4 = [1]; +var arr5 = [2]; +var arr6 = [3]; +arr4.push(arr5); +arr5.push(arr6); +arr6.push(arr4); +print(arr4.toString()); +print(arr4.toString()); \ No newline at end of file diff --git a/test/moduletest/arrayjoin/expect_output.txt b/test/moduletest/arrayjoin/expect_output.txt index 4826d7982b5b57afe2931175ff079b301575bb87..928d47a330ca8f9e6d97cc69bd1e8979fecb4fec 100644 --- a/test/moduletest/arrayjoin/expect_output.txt +++ b/test/moduletest/arrayjoin/expect_output.txt @@ -15,3 +15,9 @@ "00" "00" "00" +1,, +1,, +1,,,,,,,,,,2,,,,,,,,,, +1,,,,,,,,,,2,,,,,,,,,, +1,2,3, +1,2,3, diff --git a/test/moduletest/arrayprotochange/BUILD.gn b/test/moduletest/arrayprotochange/BUILD.gn new file mode 100755 index 0000000000000000000000000000000000000000..68f61dffc24ab4b2c10e5ca41a652cf0e79f4c70 --- /dev/null +++ b/test/moduletest/arrayprotochange/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("arrayprotochange") { + deps = [] +} diff --git a/test/moduletest/arrayprotochange/arrayprotochange.js b/test/moduletest/arrayprotochange/arrayprotochange.js new file mode 100644 index 0000000000000000000000000000000000000000..01e695fd51705af2d7c1bcd3dccee8bd92ae6932 --- /dev/null +++ b/test/moduletest/arrayprotochange/arrayprotochange.js @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * @tc.name:arrayprotochange + * @tc.desc:test Array function when prototype changes + * @tc.type: FUNC + * @tc.require: issueI7O616 + */ + +var arr = [0, 1, 2, 3, 4]; +var arr2 = [0, , , 3, 4]; +arr2.__proto__ = arr; +print(arr2[1]); +print(arr2.at(1)); \ No newline at end of file diff --git a/test/moduletest/arrayprotochange/expect_output.txt b/test/moduletest/arrayprotochange/expect_output.txt new file mode 100755 index 0000000000000000000000000000000000000000..a45895c8a7a1ae7490f767d88adbaf9667ba5130 --- /dev/null +++ b/test/moduletest/arrayprotochange/expect_output.txt @@ -0,0 +1,15 @@ +# Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +1 +1 diff --git a/test/moduletest/arraysort/BUILD.gn b/test/moduletest/arraysort/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..d1de44a5b06d4b67847d1a47ad89497a6663fc03 --- /dev/null +++ b/test/moduletest/arraysort/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("arraysort") { + deps = [] +} diff --git a/test/moduletest/arraysort/arraysort.js b/test/moduletest/arraysort/arraysort.js new file mode 100644 index 0000000000000000000000000000000000000000..bb142be248033aa2bf7e65063df92380b7a119eb --- /dev/null +++ b/test/moduletest/arraysort/arraysort.js @@ -0,0 +1,178 @@ +/* + * 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. + */ + +class DeepProxy { + constructor(obj, handler) { + return new Proxy(obj, handler); + } +} +class ClassB { + constructor(n) { + this.n = 0; + this.n = n; + } +} + +let nextFreeId = 0; +class ClassA { + constructor(a, b) { + this.a = a; + this.b = new ClassB(b); + this.id = nextFreeId++; + } +} + +// Testing the proxy situation. +let data1 = [new ClassA(1, 10), new ClassA(3, 30), new ClassA(4, 40), new ClassA(2, 20), new ClassA(11, 250)]; +let objHandler1 = new DeepProxy(data1, {}); +print(JSON.stringify(objHandler1)); +objHandler1.sort((a, b) => { + return a.b.n - b.b.n; +}) +print(JSON.stringify(objHandler1)); + +// Testing cases with both proxy and hole. +let data2 = [new ClassA(1, 10), , new ClassA(3, 30), , new ClassA(4, 40), new ClassA(2, 20), new ClassA(11, 250)]; +let objHandler2 = new DeepProxy(data2, { + deleteProperty(target, prop) { + print(`delete ${prop.toString()}`); + return Reflect.deleteProperty(target, prop); + } +}); +objHandler2.sort((a, b) => { + return a.b.n - b.b.n; +}) +print(JSON.stringify(objHandler2)); + +/* + * Test Case Description: + * 1. This use case is used to verify the logical processing order of the binary insertion sorting algorithm. + * 2. If there are any changes to the use case, please confirm if the use case needs to be modified. + */ +let arr1 = [1, 3, 2]; +arr1.sort((a, b) => { + print(`comparing a = ${a}, b = ${b}`); + return a - b; +}); +print(JSON.stringify(arr1)); + +// Modification of objects during the comparison process. +let arr2 = [1, 3, 2]; +arr2.sort((a, b) => { + if (a == 1 || b == 1) { + arr2[0] == 2; + } + return a - b; +}); +print(JSON.stringify(arr2)); + +let arr3 = [1, 3, 2]; +arr3.sort((a, b) => { + if (a == 1 || b == 1) { + arr3[4] == 2; + } + return a - b; +}); +print(JSON.stringify(arr3)); + +// Testing the situation where this is an Object +let obj1 = {0: 1, 1: 3, a: 6, 2: 2, length: 3}; +Array.prototype.sort.call(obj1, (a, b) => { + return a - b; +}); +print(JSON.stringify(obj1)); + +let obj2 = {0: 1, 1: 3, a: 6, 2: 2, length: 3}; +Array.prototype.sort.call(obj2, (a, b) => { + if (a == 1 || b == 1) { + obj2.a = 60; + } + return a - b; +}); +print(obj2.a == 60); +print(JSON.stringify(obj2)); + +let obj3 = {0: 1, 1: 3, a: 6, 2: 2, length: 2}; +Array.prototype.sort.call(obj3, (a, b) => { + return a - b; +}); +print(obj3[1] == 3) +print(JSON.stringify(obj3)); + +let obj4 = {0: 1, 1: 3, a: 6, 3: 2, length: 4}; +Array.prototype.sort.call(obj4, (a, b) => { + return a - b; +}); +print(obj4[2] == 3) +print(JSON.stringify(obj4)); + +// Test if this is a Map type; +let map1 = new Map(); +map1.set(0, 1); +map1.set(1, 3); +map1.set(2, 2); +map1.set("a", 6); +map1.set("length", 3); +Array.prototype.sort.call(map1, (a, b) => { + return a - b; +}); +print(JSON.stringify(map1)); + +let map2 = new Map(); +map2.set(0, 1); +map2.set(1, 3); +map2.set(2, 2); +map2.set("a", 6); +map2.set("length", 3); +Array.prototype.sort.call(map2, (a, b) => { + if (a == 1 || b == 1) { + map2.set("a", 60); + } + return a - b; +}); +print(JSON.stringify(map2)); + +// Test prototype +let child1 = [1, 3, 2]; +let proto1 = [4, 7, 5]; +child1.__proto__ = proto1; +child1.sort((a, b) => { + return a - b; +}); +print(JSON.stringify(child1)); + +let child2 = [1, , 2]; +child2.__proto__ = proto1; +child2.sort((a, b) => { + return a - b; +}); +print(child2.hasOwnProperty('1')); +print(JSON.stringify(child2)); + +let child3 = [1, 3, 2]; +let proto2 = [4, , 5]; +child3.__proto__ = proto2; +child3.sort((a, b) => { + return a - b; +}); +print(JSON.stringify(child3)); + +let child4 = [1, , 2]; +child4.__proto__ = proto2; +child4.sort((a, b) => { + return a - b; +}); +print(child4.hasOwnProperty('2')); +print(JSON.stringify(child4)); diff --git a/test/moduletest/arraysort/expect_output.txt b/test/moduletest/arraysort/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..4c36f231593af3ee0f57dff947303f077576762f --- /dev/null +++ b/test/moduletest/arraysort/expect_output.txt @@ -0,0 +1,39 @@ +# 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. + +[{"a":1,"b":{"n":10},"id":0},{"a":3,"b":{"n":30},"id":1},{"a":4,"b":{"n":40},"id":2},{"a":2,"b":{"n":20},"id":3},{"a":11,"b":{"n":250},"id":4}] +[{"a":1,"b":{"n":10},"id":0},{"a":2,"b":{"n":20},"id":3},{"a":3,"b":{"n":30},"id":1},{"a":4,"b":{"n":40},"id":2},{"a":11,"b":{"n":250},"id":4}] +delete 5 +delete 6 +[{"a":1,"b":{"n":10},"id":5},{"a":2,"b":{"n":20},"id":8},{"a":3,"b":{"n":30},"id":6},{"a":4,"b":{"n":40},"id":7},{"a":11,"b":{"n":250},"id":9},null,null] +comparing a = 1, b = 3 +comparing a = 3, b = 2 +comparing a = 1, b = 2 +[1,2,3] +[1,2,3] +[1,2,3] +{"0":1,"1":2,"2":3,"a":6,"length":3} +true +{"0":1,"1":2,"2":3,"a":60,"length":3} +true +{"0":1,"1":3,"2":2,"a":6,"length":2} +true +{"0":1,"1":2,"2":3,"a":6,"length":4} +{} +{} +[1,2,3] +true +[1,2,7] +[1,2,3] +false +[1,2,5] diff --git a/test/moduletest/arraytoreversed/BUILD.gn b/test/moduletest/arraytoreversed/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..641465d45372f39710f686030cb6a60cf95f3877 --- /dev/null +++ b/test/moduletest/arraytoreversed/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("arraytoreversed") { + deps = [] +} diff --git a/ecmascript/compiler/test_stubs_signature.h b/test/moduletest/arraytoreversed/arraytoreversed.js similarity index 43% rename from ecmascript/compiler/test_stubs_signature.h rename to test/moduletest/arraytoreversed/arraytoreversed.js index 402fab7fd280ff57fa7903a0a587bc74591f2906..a0242a2ebc893e33d4a0d1515f7979fca7047688 100644 --- a/ecmascript/compiler/test_stubs_signature.h +++ b/test/moduletest/arraytoreversed/arraytoreversed.js @@ -12,25 +12,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef ECMASCRIPT_COMPILER_TEST_STUBS_SIGNATURE_H -#define ECMASCRIPT_COMPILER_TEST_STUBS_SIGNATURE_H -namespace panda::ecmascript::kungfu { -#ifndef NDEBUG -#define TEST_STUB_SIGNATRUE_LIST(V) \ - V(FooAOT) \ - V(BarAOT) \ - V(Foo1AOT) \ - V(Foo2AOT) \ - V(FooNativeAOT) \ - V(FooBoundAOT) \ - V(Bar1AOT) \ - V(FooProxyAOT) \ - V(FooProxy2AOT) \ - V(Bar2AOT) \ - V(TestAbsoluteAddressRelocation) -#else - #define TEST_STUB_SIGNATRUE_LIST(V) -#endif -} // namespace panda::ecmascript::kungfu -#endif +/* + * @tc.name:arraytoreversed + * @tc.desc:test Array.toReversed + * @tc.type: FUNC + * @tc.require: issueI5NO8G + */ + +const arr0 = [0,undefined, , 1]; +print(arr0.indexOf(undefined)); +const arr1 = arr0.toReversed(); +print(arr0.indexOf(undefined)); +print(arr1.indexOf(undefined)); + +const arr2 = new Array(1025); +for(let i = 0; i < 1025; i = i + 1) + arr2[i] = i; +const arr3 = arr2.toReversed(); \ No newline at end of file diff --git a/test/moduletest/arraytoreversed/expect_output.txt b/test/moduletest/arraytoreversed/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..1f64242b66507e4ef7e46d663948938e97ce8f56 --- /dev/null +++ b/test/moduletest/arraytoreversed/expect_output.txt @@ -0,0 +1,16 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +1 +1 +1 diff --git a/test/moduletest/arraytospliced/BUILD.gn b/test/moduletest/arraytospliced/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..0914afd3beaf5ccc29931e9321e9e9956541d64a --- /dev/null +++ b/test/moduletest/arraytospliced/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("arraytospliced") { + deps = [] +} diff --git a/test/moduletest/arraytospliced/arraytospliced.js b/test/moduletest/arraytospliced/arraytospliced.js new file mode 100644 index 0000000000000000000000000000000000000000..62dea5934782463a01eef3bd031382db2d5b7017 --- /dev/null +++ b/test/moduletest/arraytospliced/arraytospliced.js @@ -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. + */ + +/* + * @tc.name:arraytospliced + * @tc.desc:test Array.toSpliced + * @tc.type: FUNC + * @tc.require: issueI5NO8G + */ + +const arr0 = [1, , 3, 4, , 6, undefined]; +print(arr0.indexOf(undefined)); +const arr1 = arr0.toSpliced(1, 2); +print(arr0.indexOf(undefined)); +print(arr1.indexOf(undefined)); + +const arr2 = new Array(1025); +for(let i = 0; i < 1025; i = i + 1) + arr2[i] = i; +const arr3 = arr2.toSpliced(0, 0); \ No newline at end of file diff --git a/test/moduletest/arraytospliced/expect_output.txt b/test/moduletest/arraytospliced/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..7ea2b47285090a24cf4828f24f2ba439a6eba5b9 --- /dev/null +++ b/test/moduletest/arraytospliced/expect_output.txt @@ -0,0 +1,16 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +6 +6 +2 diff --git a/test/moduletest/arraywith/BUILD.gn b/test/moduletest/arraywith/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..2b216e0ecec6291b6d0b653d843dfaf7c8d55c77 --- /dev/null +++ b/test/moduletest/arraywith/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("arraywith") { + deps = [] +} diff --git a/test/moduletest/arraywith/arraywith.js b/test/moduletest/arraywith/arraywith.js new file mode 100644 index 0000000000000000000000000000000000000000..cb848224ee99c68a4bf7aae0e676c1a1c39da2a8 --- /dev/null +++ b/test/moduletest/arraywith/arraywith.js @@ -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. + */ + +/* + * @tc.name:arraywith + * @tc.desc:test Array.with + * @tc.type: FUNC + * @tc.require: issueI5NO8G + */ + +const arr0 = [0, , undefined, 1]; +print(arr0.indexOf(undefined)); +const arr1 = arr0.with(0, 0); +print(arr0.indexOf(undefined)); +print(arr1.indexOf(undefined)); + +const arr2 = new Array(1025); +for(let i = 0; i < 1025; i = i + 1) + arr2[i] = i; +const arr3 = arr2.with(0, 0); diff --git a/test/moduletest/arraywith/expect_output.txt b/test/moduletest/arraywith/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..269605c2f96e3f493a7431921a5c536a1fc40675 --- /dev/null +++ b/test/moduletest/arraywith/expect_output.txt @@ -0,0 +1,16 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +2 +2 +1 diff --git a/test/moduletest/asmstackoverflow/asmstackoverflow.js b/test/moduletest/asmstackoverflow/asmstackoverflow.js index ccd0fc290119ed98d2b7a00a43f88beca80b41e1..5a650d1d1d5b645c9145b3f1eea4e5baa94e7c12 100644 --- a/test/moduletest/asmstackoverflow/asmstackoverflow.js +++ b/test/moduletest/asmstackoverflow/asmstackoverflow.js @@ -30,3 +30,14 @@ try { print("stack overflow2!"); } } + +const obj = {}; +const pro = new Proxy({}, obj); +obj.__proto__ = pro; +try { + obj[10]; +} catch (e) { + if (e instanceof RangeError) { + print("proxy stackoverflow!"); + } +} \ No newline at end of file diff --git a/test/moduletest/asmstackoverflow/expect_output.txt b/test/moduletest/asmstackoverflow/expect_output.txt index e4e6aa4c2a93219b776499bc148d9d94aa07270b..be2162686994fd0df7a69591199226adbe4724c6 100644 --- a/test/moduletest/asmstackoverflow/expect_output.txt +++ b/test/moduletest/asmstackoverflow/expect_output.txt @@ -12,3 +12,4 @@ # limitations under the License. stack overflow2! +proxy stackoverflow! diff --git a/test/moduletest/asyncgenerator/asyncgeneratornext.js b/test/moduletest/asyncgenerator/asyncgeneratornext.js index 433f38a9ed3214c08b6a38e4391956aebf2c9890..df03f3d52b8142bebd752e43115f09fcb8d252eb 100644 --- a/test/moduletest/asyncgenerator/asyncgeneratornext.js +++ b/test/moduletest/asyncgenerator/asyncgeneratornext.js @@ -26,8 +26,17 @@ async function *a() { print(c); } +async function *f() { + return 0; +} + let b = a(); print("asyncgenerator next start"); b.next(); b.next(3); + +const g = f(); +g.next(); +g.next(); +print("asyncgenerator double next"); print("asyncgenerator next end"); \ No newline at end of file diff --git a/test/moduletest/asyncgenerator/expect_output.txt b/test/moduletest/asyncgenerator/expect_output.txt index 7582685b9cc484acfdc0acdc0ac418820cc582ad..db6d77b20f755bb1a72313e2d433a3d17b7e97fb 100644 --- a/test/moduletest/asyncgenerator/expect_output.txt +++ b/test/moduletest/asyncgenerator/expect_output.txt @@ -14,6 +14,7 @@ async generator start asyncgenerator next start 1 +asyncgenerator double next asyncgenerator next end 3 asyncgenerator return start diff --git a/test/moduletest/bigint/bigint.js b/test/moduletest/bigint/bigint.js index 710b9083d1764b5ca94f1d966ee84ab6c5b47e39..9fe6825635aaa6683d3b4b99e8885faf7a0e5bb2 100644 --- a/test/moduletest/bigint/bigint.js +++ b/test/moduletest/bigint/bigint.js @@ -27,4 +27,46 @@ try { } const n1 = Number(0x1fffeffafbfcfen); -print(n1); \ No newline at end of file +print(n1); + +try { + print((9007199254740991n + 9007199254740991n) ** 0n); + print(0n ** (9007199254740991n + 9007199254740991n)); + print((9007199254740991n + 9007199254740991n) ** 9007199254740991n); +} catch (err) { + print(err.name); +} + +try { + print(0x4141414141414141n << 0n); + print(0n << 0x4141414141414141n); + print(0x4141414141414141n << 0x4141414141414141n); +} catch (err) { + print(err.name); +} + +try { + const atom = this.Atomics; + atom.and(new BigInt64Array(3807), atom, atom); +} catch (err) { + print(err.name) +} + +const v33 = String.fromCharCode(48).padStart(48, String.fromCharCode(48)); +const v35 = [-1073741824, 2, -9007199254740992]; +const v42 = Symbol.toPrimitive; +Symbol.for(v42.description); +function f36(a37, a38) { + try { + a37.lastIndexOf(0); + } catch (err) { + print(err); + } + return v33; +} +v35[v42] = f36; +let v49 = -12n; +v49--; +const v52 = (v49 >> v49).constructor; +const t48 = v52.__defineSetter__; +print(v52(v35)); \ No newline at end of file diff --git a/test/moduletest/bigint/expect_output.txt b/test/moduletest/bigint/expect_output.txt index 4302cda9d5d6636b5d088e9cb3831462662aa382..f9b57a4853d8989e0ddd6ccd067d64769c0b6c0c 100644 --- a/test/moduletest/bigint/expect_output.txt +++ b/test/moduletest/bigint/expect_output.txt @@ -13,3 +13,11 @@ test successful 9007130451115262 +1 +0 +RangeError +4702111234474983745 +0 +RangeError +SyntaxError +0 diff --git a/test/moduletest/builtins/BUILD.gn b/test/moduletest/builtins/BUILD.gn index 74428a8cfc3e8bb4a75635439b2c061e8360177a..2e6c417cda24550c543ed37d0a09c231713336b8 100644 --- a/test/moduletest/builtins/BUILD.gn +++ b/test/moduletest/builtins/BUILD.gn @@ -29,16 +29,28 @@ host_moduletest_action("builtinsregexp") { deps = [] } +host_moduletest_action("builtinsobject") { + deps = [] +} + +host_moduletest_action("builtinsobjectaddproperty") { + deps = [] +} + host_moduletest_action("builtins") { extra_modules = [ "builtinsstring", "builtinsir", "builtinsnumber", "builtinsregexp", + "builtinsobject", + "builtinsobjectaddproperty", ] deps = [ ":gen_builtinsir_abc", ":gen_builtinsnumber_abc", + ":gen_builtinsobject_abc", + ":gen_builtinsobjectaddproperty_abc", ":gen_builtinsregexp_abc", ":gen_builtinsstring_abc", ] diff --git a/test/moduletest/builtins/builtinsir.js b/test/moduletest/builtins/builtinsir.js index 1a5437df6759832d4d32b0db507a1318dbc466ae..73c74f42ebec0c7a45e88a1e8445dc41910b8571 100644 --- a/test/moduletest/builtins/builtinsir.js +++ b/test/moduletest/builtins/builtinsir.js @@ -45,4 +45,16 @@ lastName: "Gates" } var a = new Uint32Array([1,2]); print(person.fullName.apply(person1, a)); +// xxxConstructor +var a = { + test() { + this.foo(); + } +} +a.foo = Array; +a.test(); +a.foo = Boolean; +a.test(); +a.foo = Date; +a.test(); print("builtins ir end"); diff --git a/test/moduletest/builtins/builtinsnumber.js b/test/moduletest/builtins/builtinsnumber.js index 26c58e6acd5110a5d2d74acd51a3fa1c6080fc85..8fdd1f07154ccc8434ce0cb74f5f531f25373da7 100644 --- a/test/moduletest/builtins/builtinsnumber.js +++ b/test/moduletest/builtins/builtinsnumber.js @@ -24,4 +24,13 @@ print("builtins number start"); print("parseInt result = " + result); print(1 / 0.75 * 0.6); print(1 / (-1 * 0)); -print("builtins number end"); + +// math.atanh +try { + const bigIntTest = -2147483647n; + const test = Math.atanh(bigIntTest); +} catch(e) { + print(e); +}; + +print("builtins number end"); \ No newline at end of file diff --git a/test/moduletest/builtins/builtinsobject.js b/test/moduletest/builtins/builtinsobject.js new file mode 100644 index 0000000000000000000000000000000000000000..245a9f17ad495b4a6798268432d1b6027ba55f18 --- /dev/null +++ b/test/moduletest/builtins/builtinsobject.js @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * @tc.name:builtinsobject + * @tc.desc:test builtinsobject.hasOwn + * @tc.type: FUNC + * @tc.require: + */ +print("builtins object begin"); +const object1 = { + prop: 'exists' +}; + +print(Object.hasOwn(object1, 'prop')); +// Expected output: true + +print(Object.hasOwn(object1, 'toString')); +// Expected output: false + +print(Object.hasOwn(object1, 'undeclaredPropertyValue')); +// Expected output: false + +print("builtins object end"); diff --git a/test/moduletest/builtins/builtinsobjectaddproperty.js b/test/moduletest/builtins/builtinsobjectaddproperty.js new file mode 100644 index 0000000000000000000000000000000000000000..1805627f36d9aa6a4cb41571536e1e2b1f38e127 --- /dev/null +++ b/test/moduletest/builtins/builtinsobjectaddproperty.js @@ -0,0 +1,39 @@ +/* + * 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. + */ + +/* + * @tc.name:builtinsobjectaddproperty + * @tc.desc:test builtinsobject addproperty + * @tc.type: FUNC + * @tc.require: + */ +print("builtins object add property begin"); +var object = { + "aa": "aa", + "bb": "bb", + "cc": "cc", + "dd": "dd", + "ee": "ee", + "ff": "ff" +} + +var str = "g"; +for (var i = 0; i < 1100; i++) { + object[str + i] = str; +} + +print(Reflect.ownKeys(object).length); +// Expected output: 1106 +print("builtins object add property end"); \ No newline at end of file diff --git a/test/moduletest/builtins/builtinsregexp.js b/test/moduletest/builtins/builtinsregexp.js index 7339995899389ccd7d4fb4a9b6b9241ad2e0591b..fd6aa85bb945f952ea2d4f8790a2721dd9747cb2 100644 --- a/test/moduletest/builtins/builtinsregexp.js +++ b/test/moduletest/builtins/builtinsregexp.js @@ -65,4 +65,10 @@ var reg7 = /\/(?:\B)/gm; var str7 = "/"; print(reg7.test(str7)); +// Test 8 - RegExp.test $1 +var regexp = /(\d+)/g +var str8 = "Test1234" +print(regexp.test(str8)); +print(RegExp.$1); + print("builtins regexp end"); diff --git a/test/moduletest/builtins/builtinsstring.js b/test/moduletest/builtins/builtinsstring.js index a4be6e027fd4b22c4439b85cfa212ce2573e7304..7565ec50fd4fc7716bb4656dc0aeafcac7046025 100644 --- a/test/moduletest/builtins/builtinsstring.js +++ b/test/moduletest/builtins/builtinsstring.js @@ -22,6 +22,8 @@ print("builtins string start"); print(String.fromCharCode(65)); print(String.fromCharCode(65, 66, 66, 65)); +print(String.fromCharCode(0x2014)); +print(String.fromCharCode(0x12014)); class NewString extends String { } print(new NewString('') instanceof NewString); diff --git a/test/moduletest/builtins/expect_output.txt b/test/moduletest/builtins/expect_output.txt index 91804e8fc596436ea7bc8e4d1a4432d56a4b7ad3..ab5eb0937e279b015feac5557dc044052930f650 100644 --- a/test/moduletest/builtins/expect_output.txt +++ b/test/moduletest/builtins/expect_output.txt @@ -15,6 +15,8 @@ builtins test start builtins string start A ABBA +— +— true builtins string end builtins ir start @@ -37,10 +39,11 @@ builtins number start parseInt result = 16947500000 0.7999999999999999 -Infinity +TypeError: Cannot convert a BigInt value to a number builtins number end builtins regexp start 20210608_5V0J5lVh4xVNYx0AUE.jpg,.jpg -RegExp $1 is read-only +.jpg st2 a @@ -49,4 +52,14 @@ AAA AAA AAA true +true +1234 builtins regexp end +builtins object begin +true +false +false +builtins object end +builtins object add property begin +1106 +builtins object add property end diff --git a/test/moduletest/container/container_vector.js b/test/moduletest/container/container_vector.js index 090a2289968fefd18de9ad1cf7f015e759dc4760..0bcdfb4b08a8c97a2a2ca5adea43a2449fb91d11 100644 --- a/test/moduletest/container/container_vector.js +++ b/test/moduletest/container/container_vector.js @@ -274,6 +274,20 @@ if (globalThis["ArkPrivate"] != undefined) { proxy1.trimToCurrentLength() map.set("test vector trimToCurrentLength:", proxy1.getCapacity() === 5) + + let v = new FastVector(); + for (let i = 0; i < 10; i++) { + v.add(i); + } + v.replaceAllElements((value, key, t) => { + if (key == 8) { + t.clear(); + t.trimToCurrentLength(); + return 0; + } + return value; + }, v); + map.set("test vector replaceAllElements redefine:", v.length == 0); flag = undefined; function elements(value, key, map) { diff --git a/test/moduletest/dataproperty/dataproperty.js b/test/moduletest/dataproperty/dataproperty.js index ee734bf2ad432aad65e9b353bc1d1e37a85e3d76..e8a39f5d70fe79bdd8af13c64e935c042756209b 100644 --- a/test/moduletest/dataproperty/dataproperty.js +++ b/test/moduletest/dataproperty/dataproperty.js @@ -21,6 +21,12 @@ */ // CopyDataProperties +this[1024] = 4; +const o = { + ...this, +}; +print(JSON.stringify(o)); + var s = "abc"; var t = {...s}; print(t[1]); diff --git a/test/moduletest/dataproperty/expect_output.txt b/test/moduletest/dataproperty/expect_output.txt index 95de93efedebfd89a3558411910b9be09dbb8987..40e5412952ff10c1c218625aae4c504fc07b8706 100644 --- a/test/moduletest/dataproperty/expect_output.txt +++ b/test/moduletest/dataproperty/expect_output.txt @@ -11,4 +11,5 @@ # See the License for the specific language governing permissions and # limitations under the License. +{"1024":4} b diff --git a/test/moduletest/dataview/BUILD.gn b/test/moduletest/dataview/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..9c89fb5b920409d1cb1e27b7aae68e86215c7aa1 --- /dev/null +++ b/test/moduletest/dataview/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("dataview") { + deps = [] +} diff --git a/test/moduletest/dataview/dataview.js b/test/moduletest/dataview/dataview.js new file mode 100644 index 0000000000000000000000000000000000000000..5a8d47743351c1dd4e5db58d9e779fead7861f94 --- /dev/null +++ b/test/moduletest/dataview/dataview.js @@ -0,0 +1,25 @@ +/* + * 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. + */ + +/* + * @tc.name:dataview + * @tc.desc:test dataview + * @tc.type: FUNC + * @tc.require: issue#I7NUZM + */ +const buffer = new ArrayBuffer(16); +const view = new DataView(buffer); +view.setInt32({}, 0x1337, {}); +print(view.getInt32({}, {})); \ No newline at end of file diff --git a/test/moduletest/dataview/expect_output.txt b/test/moduletest/dataview/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..9899a0124f50742fa1c1922b73d5105c55538d95 --- /dev/null +++ b/test/moduletest/dataview/expect_output.txt @@ -0,0 +1,14 @@ +# 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. + +4919 diff --git a/test/moduletest/datecase/datecase.js b/test/moduletest/datecase/datecase.js index 69de5be182cf56fde4b1d19867261b5515dfafc0..d7c95604db65ed58f98b0716ac71482d6ba9af43 100644 --- a/test/moduletest/datecase/datecase.js +++ b/test/moduletest/datecase/datecase.js @@ -29,6 +29,7 @@ var d6 = new Date(1680278400000); //daylight savings offset var d7 = new Date(1949, 4, 1); var d8 = new Date(1947, 9, 31, 16); +var d9 = new Date(1970, 1, 1); print(d1.getFullYear() == 2022); print(d1.getMonth() == 0); @@ -53,4 +54,9 @@ print(d6.getFullYear() == 2023); print(d6.getMonth() == 3); print(d7.getFullYear(),d7.getMonth(),d7.getDate(),d7.getHours()); -print(d8.getFullYear(),d8.getMonth(),d8.getDate(),d8.getHours()); \ No newline at end of file +print(d8.getFullYear(),d8.getMonth(),d8.getDate(),d8.getHours()); + +print(d9.getFullYear() == 1970); +print(d9.getMonth() == 1); +print(d9.getDate() == 1); +print(d9.getHours() == 0); \ No newline at end of file diff --git a/test/moduletest/datecase/expect_output.txt b/test/moduletest/datecase/expect_output.txt index 7dbd4ab478cd12e55f090eee2fb1700e93811a7f..d67e098380b0130b73cde4e5f302d1f006bbcd4d 100644 --- a/test/moduletest/datecase/expect_output.txt +++ b/test/moduletest/datecase/expect_output.txt @@ -29,3 +29,7 @@ true true 1949 4 1 0 1947 9 31 16 +true +true +true +true diff --git a/test/moduletest/deregistermodule/BUILD.gn b/test/moduletest/deregistermodule/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..9987467d66956858dcbfa271076d5a6d90ca2906 --- /dev/null +++ b/test/moduletest/deregistermodule/BUILD.gn @@ -0,0 +1,23 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("test") { + deps = [] + is_module = true +} + +host_moduletest_action("deregistermodule") { + deps = [ ":gen_test_abc" ] +} diff --git a/test/moduletest/deregistermodule/deregistermodule.js b/test/moduletest/deregistermodule/deregistermodule.js new file mode 100644 index 0000000000000000000000000000000000000000..8167a61bfb0598bde256db21bd97866aac5c47e8 --- /dev/null +++ b/test/moduletest/deregistermodule/deregistermodule.js @@ -0,0 +1,34 @@ +/* + * 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. + */ + +async function f(number) { + let module = await import('./test.js'); + print(module.name + number); + markModuleCollectable(module); +} + +function deregister (){ + return new Promise((resolve, reject)=> { + resolve("done"); + }) +} + +deregister().then(r=>{ + f(1).then(()=>{ // test for esm functioned well when second loading. + print("second load"); + ArkTools.forceFullGC(); + f(2); // test whether the module has been successfully uninstalled. + }); + print(r);}); \ No newline at end of file diff --git a/test/moduletest/deregistermodule/expect_output.txt b/test/moduletest/deregistermodule/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..252974f17500ef1013898c438690c5296445ef1b --- /dev/null +++ b/test/moduletest/deregistermodule/expect_output.txt @@ -0,0 +1,19 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +done +loading test.js... +Tyrion1 +second load +loading test.js... +Tyrion2 diff --git a/test/moduletest/deregistermodule/test.js b/test/moduletest/deregistermodule/test.js new file mode 100644 index 0000000000000000000000000000000000000000..6bec991a70b98534caecee17901f53b5fd6e73f9 --- /dev/null +++ b/test/moduletest/deregistermodule/test.js @@ -0,0 +1,18 @@ +/* + * 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. + */ + +export {name}; +const name = 'Tyrion'; +print("loading test.js...") \ No newline at end of file diff --git a/test/moduletest/errorcause/BUILD.gn b/test/moduletest/errorcause/BUILD.gn new file mode 100755 index 0000000000000000000000000000000000000000..989440cfb3119254a7bdc9555054561ca6732c4a --- /dev/null +++ b/test/moduletest/errorcause/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("errorcause") { + deps = [] +} diff --git a/test/moduletest/errorcause/errorcause.js b/test/moduletest/errorcause/errorcause.js new file mode 100755 index 0000000000000000000000000000000000000000..0b0c33bfc340bac8e84361c2a0e2d22f4102484c --- /dev/null +++ b/test/moduletest/errorcause/errorcause.js @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * @tc.name:errorcause + * @tc.desc:test Error Constructors with cause + * @tc.type: FUNC + * @tc.require: issueI7F4Y5 + */ +[ + Error, + EvalError, + RangeError, + ReferenceError, + SyntaxError, + TypeError, + URIError, +].forEach(function (ctor, i) { + if (testErrorCause(ctor)) { + print(ctor.name + " test success !!!") + } else { + print(ctor.name + " test fail !!!") + } +}); + +function testErrorCause(ctor) { + try { + let err = new ctor("message", { cause: "error cause" }); + throw err; + } catch (e) { + if (e.cause == "error cause") { + return true; + } else { + return false; + } + } +} + +let err2 = new AggregateError([], "message", { cause: "error cause" }); +try { + throw err2; +} catch (e) { + if (e.cause == "error cause") { + print(e.name + " test success !!!") + } else { + print(e.name + " test fail !!!") + } +} \ No newline at end of file diff --git a/test/moduletest/errorcause/expect_output.txt b/test/moduletest/errorcause/expect_output.txt new file mode 100755 index 0000000000000000000000000000000000000000..b0629cc119eb8931c357eb4fb161ed25860f050d --- /dev/null +++ b/test/moduletest/errorcause/expect_output.txt @@ -0,0 +1,21 @@ +# Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Error test success !!! +EvalError test success !!! +RangeError test success !!! +ReferenceError test success !!! +SyntaxError test success !!! +TypeError test success !!! +URIError test success !!! +AggregateError test success !!! diff --git a/test/moduletest/errorhelper/errorhelper.js b/test/moduletest/errorhelper/errorhelper.js index 8c6853fd7bd7f18a234e0bddfc20a889ae8351bc..d3ecffdde4757dada6a4cd7f3581ae546f7761e3 100644 --- a/test/moduletest/errorhelper/errorhelper.js +++ b/test/moduletest/errorhelper/errorhelper.js @@ -29,4 +29,12 @@ v40.constructor(v40, v34, C37, v40); const v46 = e45.toString; const v50 = v46.call(v34); print(v50); -} \ No newline at end of file +} + +// Be sure to put this case at the end because this case will change prototype of error constructor +try { + 0(); +} catch (err) { + err.constructor.prototype.name = 123456789; +} +0(); diff --git a/test/moduletest/flatten/BUILD.gn b/test/moduletest/flatten/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..946ad9d5474b80951a47db8d18e99e621f313365 --- /dev/null +++ b/test/moduletest/flatten/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("flatten") { + deps = [] +} diff --git a/test/moduletest/flatten/expect_output.txt b/test/moduletest/flatten/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..9f3e2df3957206da62bec278b0349f4685795977 --- /dev/null +++ b/test/moduletest/flatten/expect_output.txt @@ -0,0 +1,14 @@ +# 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. + +TypeError: 00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00 diff --git a/test/moduletest/flatten/flatten.js b/test/moduletest/flatten/flatten.js new file mode 100644 index 0000000000000000000000000000000000000000..59fe5fcebab744d67104c195b155276ab18bce6a --- /dev/null +++ b/test/moduletest/flatten/flatten.js @@ -0,0 +1,33 @@ +/* + * 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. + */ + +/* + * @tc.name:Flatten + * @tc.desc:test Flatten + * @tc.type: FUNC + * @tc.require: issueI7CTF7 + */ +const v14 = new Uint8ClampedArray(521); +let v16 = v14[1973679951]; +v16 ||= v14; +const v17 = new Int32Array(); +const v18 = v14.join(v16); +try { + Int32Array(); +} catch(e21) { + e21.message = v18; + print(e21); + v17.set(e21, e21); +} \ No newline at end of file diff --git a/test/moduletest/forin/expect_output.txt b/test/moduletest/forin/expect_output.txt index 4ab8f7fce487422cdcdb2068f09608cf771c3327..15984924c30e17767fd0334a536028c2ca453a96 100644 --- a/test/moduletest/forin/expect_output.txt +++ b/test/moduletest/forin/expect_output.txt @@ -27,3 +27,4 @@ hhh 1 2 true +true diff --git a/test/moduletest/forin/forin.js b/test/moduletest/forin/forin.js index e0e2464609885cd407ae73ca551f5924f4329a93..30bfd5295b5b137f4102c6051315bbb59a663892 100644 --- a/test/moduletest/forin/forin.js +++ b/test/moduletest/forin/forin.js @@ -112,3 +112,17 @@ try { } catch (err) { print(err instanceof TypeError); } + +// PoC testcase +try { + class C { + + } + C.getPrototypeOf = 3014; + const proxy = new Proxy([7], C); + for (const v in proxy) { + + } +} catch (err) { + print(err instanceof TypeError); +} \ No newline at end of file diff --git a/test/moduletest/fortest/expect_output.txt b/test/moduletest/fortest/expect_output.txt index 3b9bff4a9296e48610bf6f41e5cf98ee3de41363..22a1b50dcfef356b3f83fbf36742b79b0035eebd 100644 --- a/test/moduletest/fortest/expect_output.txt +++ b/test/moduletest/fortest/expect_output.txt @@ -31,3 +31,35 @@ 3 2 1 +p0 0 +p1 1 +p2 2 +p3 3 +p4 4 +p5 5 +p6 6 +p7 7 +p8 8 +p9 9 +p10 10 +p11 11 +p12 12 +p13 13 +p14 14 +p15 15 +p16 16 +p17 17 +p18 18 +p19 19 +p20 20 +p21 21 +p22 22 +p23 23 +p24 24 +p25 25 +p26 26 +p27 27 +p28 28 +p29 29 +p30 30 +p31 31 diff --git a/test/moduletest/fortest/fortest.js b/test/moduletest/fortest/fortest.js index f7df2caca24b03ffe9641ac2bc46481925bc31ca..b6cf3fb2154203ef13199c7190a7f7d92cbee00c 100644 --- a/test/moduletest/fortest/fortest.js +++ b/test/moduletest/fortest/fortest.js @@ -35,3 +35,17 @@ while (i > 0) { i--; } +function F0() { +} +const v5 = new F0(); +for (let index = 0; index < 32; index++) { + function F17() { + this.__proto__ = v5; + } + const v28 = new F17(); + const v29 = "p" + index; + v5[v29] = index; +} +for (let k in v5) { + print(k, v5[k]); +} diff --git a/test/moduletest/funcprotochangeobjectandnew/BUILD.gn b/test/moduletest/funcprotochangeobjectandnew/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..7cbf8b37302d1d8c0983214099fd1f6c12492fa6 --- /dev/null +++ b/test/moduletest/funcprotochangeobjectandnew/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("funcprotochangeobjectandnew") { + deps = [] +} diff --git a/test/moduletest/funcprotochangeobjectandnew/expect_output.txt b/test/moduletest/funcprotochangeobjectandnew/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..491c2d586d0a84b625e79fa48f15bbe56b5ebd70 --- /dev/null +++ b/test/moduletest/funcprotochangeobjectandnew/expect_output.txt @@ -0,0 +1,14 @@ +# 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. + +TypeError: Constructor is false diff --git a/test/moduletest/funcprotochangeobjectandnew/funcprotochangeobjectandnew.js b/test/moduletest/funcprotochangeobjectandnew/funcprotochangeobjectandnew.js new file mode 100644 index 0000000000000000000000000000000000000000..36b6d6f268d3055512e817832f2c97583fd55132 --- /dev/null +++ b/test/moduletest/funcprotochangeobjectandnew/funcprotochangeobjectandnew.js @@ -0,0 +1,30 @@ +/* + * 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. + */ + +try { + function f0() { + } + + class C9 extends f0 { + constructor() { + super(); + } + } + let c = {} + C9.__proto__ = c; + new C9(); +} catch (error) { + print(error); +} \ No newline at end of file diff --git a/test/moduletest/functionapply/BUILD.gn b/test/moduletest/functionapply/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..0069310cfa749d36925b3d2bdc1cf85a7687031d --- /dev/null +++ b/test/moduletest/functionapply/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("functionapply") { + deps = [] +} diff --git a/test/moduletest/functionapply/expect_output.txt b/test/moduletest/functionapply/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..0284d9d095b876c3adc4de0561f51e2574a1d45a --- /dev/null +++ b/test/moduletest/functionapply/expect_output.txt @@ -0,0 +1,16 @@ +# 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. + +,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,, +0 diff --git a/test/moduletest/functionapply/functionapply.js b/test/moduletest/functionapply/functionapply.js new file mode 100644 index 0000000000000000000000000000000000000000..19d8cfa248fa9b6e26d566a5e35239cbf8a8214a --- /dev/null +++ b/test/moduletest/functionapply/functionapply.js @@ -0,0 +1,24 @@ +/* + * 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. + */ + +function createArray(len) { + return Array.apply(null, {length: len}); +} +let arr = createArray(10); +let a1 = createArray(23.2); +let a2 = createArray(); +print(arr); +print(a1); +print(a2.length); \ No newline at end of file diff --git a/test/moduletest/global/BUILD.gn b/test/moduletest/global/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..a42d46ac95b3986d70eb8ec3fdc4b4d8d606a4ed --- /dev/null +++ b/test/moduletest/global/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("global") { + deps = [] +} diff --git a/test/moduletest/global/expect_output.txt b/test/moduletest/global/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..281a59acf34ddf7fd90bce986233ea7825579b7f --- /dev/null +++ b/test/moduletest/global/expect_output.txt @@ -0,0 +1,17 @@ +# Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +%26 +%u4F60%u597D +& +你好 diff --git a/test/moduletest/global/global.js b/test/moduletest/global/global.js new file mode 100644 index 0000000000000000000000000000000000000000..91a9323288267b5ecc2b531edd64cb171b2b51a8 --- /dev/null +++ b/test/moduletest/global/global.js @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +print(escape("&")) +print(escape('你好')) +print(unescape("%26")) +print(unescape('%u4F60%u597D')) diff --git a/test/moduletest/hugearray/expect_output.txt b/test/moduletest/hugearray/expect_output.txt index 254258da966d02ef5b60ef42b7dbb4fd3dd85cfb..efddbbf4d7b084a6bb8d6aab355a515b5afe88e6 100644 --- a/test/moduletest/hugearray/expect_output.txt +++ b/test/moduletest/hugearray/expect_output.txt @@ -12,3 +12,5 @@ # limitations under the License. 33333 +18001 +0 diff --git a/test/moduletest/hugearray/hugearray.js b/test/moduletest/hugearray/hugearray.js index 4639c36ee159b9b1a6f124fa2bdfc0c07f03234a..7ac4122ad3a154f4264e0bd8c5099ea9c663b55f 100644 --- a/test/moduletest/hugearray/hugearray.js +++ b/test/moduletest/hugearray/hugearray.js @@ -28,3 +28,21 @@ for (let i = 0; i < 100000; i++) { } } print(arr.length); + +let v2 = new Array(2); +let t = 0 +do { + try { + v2["lastIndexOf"](...16); + } catch (e) { + + } +} while (t++ < 18000) +print(t); + +let bytes = new Uint8Array(new ArrayBuffer(43584)); +let arr2 = [].slice.call(bytes); +while (arr2.length) { + arr2.pop(); +} +print(arr2.length); diff --git a/test/moduletest/instanceofic/expect_output.txt b/test/moduletest/instanceofic/expect_output.txt index 4f73baf00a4d8cb82952a0aa6b8c8a1eba3e8e1b..579116f7443cb587d1914fb75f9516f7efe2102e 100644 --- a/test/moduletest/instanceofic/expect_output.txt +++ b/test/moduletest/instanceofic/expect_output.txt @@ -14,3 +14,4 @@ instanceoficsuccess instanceoficsuccess test success +test success diff --git a/test/moduletest/instanceofic/instanceofic.js b/test/moduletest/instanceofic/instanceofic.js index 5ebcab526a1800fbba184f19bf71b9cdda9c0e5e..e74ff9dd59102a948653579c5223c2b0bdd7cd69 100644 --- a/test/moduletest/instanceofic/instanceofic.js +++ b/test/moduletest/instanceofic/instanceofic.js @@ -53,3 +53,16 @@ for (let index = 0; index <= 30; index++) { } } print("test success") + +for (let index = 0; index <= 30; index++) { + function foo() { + try { + const obj = {}; + obj instanceof {}; + } catch (e) { + + } + } + foo(); +} +print("test success") \ No newline at end of file diff --git a/test/moduletest/jsonparser/expect_output.txt b/test/moduletest/jsonparser/expect_output.txt index 29ca31e64a5729c2d166f96275511addebb51521..4cb432dd1e2f123cf453dc5af3b22964d938a602 100644 --- a/test/moduletest/jsonparser/expect_output.txt +++ b/test/moduletest/jsonparser/expect_output.txt @@ -18,3 +18,6 @@ https://www.a.com https://www.b.com https://www.c.com [object Object] +Infinity +-Infinity +123 diff --git a/test/moduletest/jsonparser/jsonparser.js b/test/moduletest/jsonparser/jsonparser.js index be9a1ddb816b3c04f5bc7e9766f16514468b8576..0715c7a9bf572d7bbcb35d02efc18b8e6c630eb4 100644 --- a/test/moduletest/jsonparser/jsonparser.js +++ b/test/moduletest/jsonparser/jsonparser.js @@ -40,4 +40,16 @@ print(res["00000000"]); var a = `{"code": 0, "msg": "ok"}` function reviver(k, v) { return v; } var o = JSON.parse(a, reviver); -print(o); \ No newline at end of file +print(o); + +let strData2 = "1.7976971348623157e+308"; +let res2 = JSON.parse(strData2); +print(res2); + +let strData3 = "-1.7976971348623157e+308"; +let res3 = JSON.parse(strData3); +print(res3); + +let strData4 = "123"; +let res4 = JSON.parse(strData4); +print(res4); \ No newline at end of file diff --git a/test/moduletest/jsonstringifier/expect_output.txt b/test/moduletest/jsonstringifier/expect_output.txt index 41accf03ae2bce61831441f63259a6d2914454a6..b386584197295660e30eda1861448f63bc1be116 100644 --- a/test/moduletest/jsonstringifier/expect_output.txt +++ b/test/moduletest/jsonstringifier/expect_output.txt @@ -12,3 +12,6 @@ # limitations under the License. test successful +{"2147483648":2289} +{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0} +{"g":9,"f1":1,"f2":1,"f3":1,"f4":1,"f5":1,"f6":1,"f7":1,"f8":1} diff --git a/test/moduletest/jsonstringifier/jsonstringifier.js b/test/moduletest/jsonstringifier/jsonstringifier.js index 0653f5a0a89fb58304d2711cdd00fe1044e1d8fc..1dedf81035e871383631c81cbfe10cabdc792a45 100644 --- a/test/moduletest/jsonstringifier/jsonstringifier.js +++ b/test/moduletest/jsonstringifier/jsonstringifier.js @@ -30,4 +30,30 @@ try { JSON.stringify(v2); } catch (e) { print("test successful"); -} \ No newline at end of file +} + +var obj = { + 2147483648: 2289 +} +print(JSON.stringify(obj)); + +const a = new Uint32Array(0x10); +let b = a.__proto__; +b[1073741823] = {} +print(JSON.stringify(a)) + +let o = { + get g() { + this[1225] |= 4294967295; + return 9; + }, + "f1":1, + "f2":1, + "f3":1, + "f4":1, + "f5":1, + "f6":1, + "f7":1, + "f8":1, +} +print(JSON.stringify(o)) diff --git a/test/moduletest/loadicbyvalue/expect_output.txt b/test/moduletest/loadicbyvalue/expect_output.txt index aa7ff3ba40aeaffb485d35f587370b442fe0303e..5460830b67b6e5c973fb126c5539800627308509 100644 --- a/test/moduletest/loadicbyvalue/expect_output.txt +++ b/test/moduletest/loadicbyvalue/expect_output.txt @@ -12,3 +12,10 @@ # limitations under the License. icsuccess +test successful !!! +0 0 +2147483647 1 +2147483648 2 +4294967295 3 +4294967296 4 +ic load success diff --git a/test/moduletest/loadicbyvalue/loadicbyvalue.js b/test/moduletest/loadicbyvalue/loadicbyvalue.js index 0840de4b5e128e79535e45c3317f7a8f530e30e0..aaf10b68b6b973e09ccb7c8696ca559f1f884d7a 100644 --- a/test/moduletest/loadicbyvalue/loadicbyvalue.js +++ b/test/moduletest/loadicbyvalue/loadicbyvalue.js @@ -29,4 +29,54 @@ for (let j = 300; j > 0; j--) { func1(obj); } -print(obj[i]); \ No newline at end of file +print(obj[i]); + +// PoC testcase +const arr = [] +for (let i = 0; i < 20; i ++) { + const v0 = "p" + i; + const v1 = Symbol.iterator; + const v2 = { + [v1]() {}, + }; + arr[v0] = i; +} +const v3 = new Uint8Array(128); +v3[arr]; +print("test successful !!!"); + +var obj1 = { + 0: 0, + 2147483647: 1, + 2147483648: 2, + 4294967295: 3, + 4294967296: 4, +} + +for (let item in obj1) { + print(item + " " + obj1[item]); +} +// test ic +var lineStr = "方舟ArK TypeScript RuntimekTS运行时(ARK TypeScript Runtime)K TypeScript Runtime是OppeScript RK TypeScripK TypeScript Runtimet Runtimeuntime)是OpeK TypeScript RuntimenHarmony上ArkTS应用使用的运行时。包含ArkTS/JS对象的分配enHarmonypeScript Runtime)是OpenHarmony上ArkTS应用使用的运行时。包含ArkTS/JS对象的分配上ArkTS应用使用peScript Runtime)是OpenHarmony上ArkTS应用使用的运行时。包含ArkTS/JS对象的分配的运行时。包含ArkTS/JS对象的分配器以及垃圾回收器(GC)、符合ECMAScript规范的标准库、用于运行ARK前端组件生成的方舟字节码(ARK Bytecode简称abc)的解释器、用于加速的内联缓存、静态类型编译器、运行时的C++/C函数接口(NAPI)等模块."; +var str = "方舟ArkTS运行时(ARK TypeScript Runtime)是OpenHarmony上ArkTS应用使用的运行时。包含ArkTS/JS对象的分配器以及垃圾回收器(GC)、符合ECMAScript规范的标准库、用于运行ARK前端组件生成的方舟字节码(ARK Bytecode简称abc)的解释器、用于加速的内联缓存、静态类型编译器、运行时的C++/C函数接口(NAPI)等模块."; +str += str; +str += str; +str += str; +var c; +var l; +var strC; +for (let i = 0;i < 100;i++){ + strC = str.split("C"); + c = str[i]; + l = str.length; + strC = lineStr.split("C"); + c = lineStr[i]; + l = lineStr.length; +} +var strObj = new String("方舟ArkTS运行时(ARK TypeScript Runtime)是OpenHarmony上ArkTS应用使用的运行时。包含ArkTS/JS对象的分配器以及垃圾回收器(GC)、符合ECMAScript规范的标准库、用于运行ARK前端组件生成的方舟字节码(ARK Bytecode简称abc)的解释器、用于加速的内联缓存、静态类型编译器、运行时的C++/C函数接口(NAPI)等模块."); +for (let i = 0;i < 100;i++){ + strC = strObj.split("C"); + c = strObj[i]; + l = strObj.length; + } +print("ic load success"); diff --git a/test/moduletest/moduleUseCjs/BUILD.gn b/test/moduletest/moduleUseCjs/BUILD.gn index 2de56c405259dbd9a88b9bdab5501c68859f48eb..033cf7b3a30a1e68a96039a91abc92bb02b9974e 100644 --- a/test/moduletest/moduleUseCjs/BUILD.gn +++ b/test/moduletest/moduleUseCjs/BUILD.gn @@ -28,9 +28,15 @@ host_moduletest_action("someArgsCjs") { is_commonjs = true } +host_moduletest_action("CjsDictionaryMode") { + deps = [] + is_commonjs = true +} + host_moduletest_action("moduleUseCjs") { extra_modules = [ "Cjs" ] deps = [ + ":gen_CjsDictionaryMode_abc", ":gen_Cjs_abc", ":gen_cjsWithoutExports_abc", ":gen_someArgsCjs_abc", diff --git a/test/moduletest/moduleUseCjs/CjsDictionaryMode.js b/test/moduletest/moduleUseCjs/CjsDictionaryMode.js new file mode 100644 index 0000000000000000000000000000000000000000..bd52d68df44fa5a30ec2807e28770a55174dfd5d --- /dev/null +++ b/test/moduletest/moduleUseCjs/CjsDictionaryMode.js @@ -0,0 +1,29 @@ +/* + * 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. + */ + +/* + * @tc.name:moduleUseCjs + * @tc.desc:test module CJS + * @tc.type: FUNC + * @tc.require: issueI5NO8G + */ +Object.defineProperty(exports,"SUCCESS", { + enumerable: true, + get:function () { + return "success"; + } +}) +exports[0] = 1; +exports[1099] = 1; \ No newline at end of file diff --git a/test/moduletest/moduleUseCjs/expect_output.txt b/test/moduletest/moduleUseCjs/expect_output.txt index 19508f07e598cadb50c13b87d01fbd462fbdb9e4..308a5bea85167dfb84a9745d4c9393e5cad13ed9 100644 --- a/test/moduletest/moduleUseCjs/expect_output.txt +++ b/test/moduletest/moduleUseCjs/expect_output.txt @@ -17,3 +17,4 @@ execute cjsWithoutExports.js success! jscall 1 true hello world +success diff --git a/test/moduletest/moduleUseCjs/moduleUseCjs.js b/test/moduletest/moduleUseCjs/moduleUseCjs.js index 0ca98081a241082ea2904369da7e64fc5d2fec3f..7994dce76f9df29f8af8a16dd1d2c29691cbba85 100644 --- a/test/moduletest/moduleUseCjs/moduleUseCjs.js +++ b/test/moduletest/moduleUseCjs/moduleUseCjs.js @@ -23,9 +23,11 @@ import cjs from "./Cjs" import * as ns from "./someArgsCjs" import {json, fun} from "./someArgsCjs" import "./cjsWithoutExports.js" +import {SUCCESS} from "./CjsDictionaryMode" print(JSON.stringify(cjs)); print(JSON.stringify(json)); fun(); print(ns.tag); -ns.con(); \ No newline at end of file +ns.con(); +print(SUCCESS); \ No newline at end of file diff --git a/test/moduletest/negintmin/expect_output.txt b/test/moduletest/negintmin/expect_output.txt index a779d3e6a4de42aed1a14c76e5b19c054ddfddd8..dd1e83fce2009f4052baaa1250a6b711f836dbc9 100644 --- a/test/moduletest/negintmin/expect_output.txt +++ b/test/moduletest/negintmin/expect_output.txt @@ -13,3 +13,5 @@ -2147483648 2147483648 +Infinity +-Infinity diff --git a/test/moduletest/negintmin/negintmin.js b/test/moduletest/negintmin/negintmin.js index 6ebaf4b42325977741f94401726ca6086ceb31fd..d620896cb184f7af11e47556ac4c1b6bc5c40f6f 100644 --- a/test/moduletest/negintmin/negintmin.js +++ b/test/moduletest/negintmin/negintmin.js @@ -18,4 +18,8 @@ let negone = -1; let intmin = negone - intmax; let negintmin = -intmin; print(intmin); -print(negintmin); \ No newline at end of file +print(negintmin); + +let doublezero = 0 * 0.1; +print(1 / doublezero); +print(1 / -doublezero); diff --git a/test/moduletest/objseal/BUILD.gn b/test/moduletest/objseal/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..bfef2d887e973cefb0f0c5532547c9bb0094be4f --- /dev/null +++ b/test/moduletest/objseal/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("objseal") { + deps = [] +} diff --git a/test/moduletest/objseal/expect_output.txt b/test/moduletest/objseal/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..acf7908bc337d484ae9d0cf1f7b4939c9ae886b2 --- /dev/null +++ b/test/moduletest/objseal/expect_output.txt @@ -0,0 +1,14 @@ +# 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. + +test successful !!! diff --git a/test/moduletest/objseal/objseal.js b/test/moduletest/objseal/objseal.js new file mode 100644 index 0000000000000000000000000000000000000000..0dd3534cefcc39b98f42ac2975b04cd5007a4d38 --- /dev/null +++ b/test/moduletest/objseal/objseal.js @@ -0,0 +1,23 @@ +/* + * 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. + */ + +/* + * @tc.name:objseal + * @tc.desc:test object seal + * @tc.type: FUNC + * @tc.require: issue#I7F9ZT + */ +Object.seal(this); +print("test successful !!!"); \ No newline at end of file diff --git a/test/moduletest/propertydetector/BUILD.gn b/test/moduletest/propertydetector/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..7db171f7780c061e6ef586fab48b9c5e45b51106 --- /dev/null +++ b/test/moduletest/propertydetector/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("propertydetector") { + deps = [] +} diff --git a/test/moduletest/propertydetector/expect_output.txt b/test/moduletest/propertydetector/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..a3426b91cf377353444e31ec0e92161308200406 --- /dev/null +++ b/test/moduletest/propertydetector/expect_output.txt @@ -0,0 +1,23 @@ +# Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +这是一段原始文本,"3这要替换4"! +true +true +true +这是一段原始文本,"3c这要替换4d"! +true +aaa +false +4 +false diff --git a/test/moduletest/propertydetector/propertydetector.js b/test/moduletest/propertydetector/propertydetector.js new file mode 100644 index 0000000000000000000000000000000000000000..6a9bd9487a8c17db6cb817c02b77c58f5210ab63 --- /dev/null +++ b/test/moduletest/propertydetector/propertydetector.js @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * @tc.name:property detector + * @tc.desc:test property detector + * @tc.type: FUNC + * @tc.require: issueI83B0E + */ + +let str = '这是一段原始文本,"3c这要替换4d"!'; +let regexp = /([0-9])([a-z])/g +let newStr1 = str.replace(regexp, "$1" ); +print(newStr1) +print(ArkTools.isRegExpReplaceDetectorValid()) + +let p = RegExp.prototype +let unused = p[Symbol.replace] +print(ArkTools.isRegExpReplaceDetectorValid()) + +p["replace"] = function () {return "abc"} +print(ArkTools.isRegExpReplaceDetectorValid()) + +regexp.__proto__ = {} +let newStr2 = str.replace(regexp, "$2" ); +print(newStr2) +print(ArkTools.isRegExpReplaceDetectorValid()) + +// resume regexp.__proto__ +regexp.__proto__ = p +p[Symbol.replace] = function () {return "aaa"} +let newStr3 = str.replace(regexp, "$3" ); +print(newStr3) +print(ArkTools.isRegExpReplaceDetectorValid()) + +p[Symbol.replace] = function () {return 4} +let newStr4 = str.replace( /([0-9])([a-z])/g,"$4" ); +print(newStr4) +print(ArkTools.isRegExpReplaceDetectorValid()) diff --git a/test/moduletest/proxy/expect_output.txt b/test/moduletest/proxy/expect_output.txt index ed3028e111a176c4c59c0a23d9a436d822bff957..7c6fdaefcc3de11e27411883166bc023149f15b4 100644 --- a/test/moduletest/proxy/expect_output.txt +++ b/test/moduletest/proxy/expect_output.txt @@ -16,3 +16,7 @@ true true true true +name +name +name +1,2,3,4 diff --git a/test/moduletest/proxy/proxy.js b/test/moduletest/proxy/proxy.js index abfd6201afd67dbf851bbff8cd709863b5965347..5baf5dd10524247b49d654a15d6e0204a040cb8a 100644 --- a/test/moduletest/proxy/proxy.js +++ b/test/moduletest/proxy/proxy.js @@ -45,4 +45,19 @@ const a2 = { }; const a3 = new Proxy(a1, a2); Object.freeze(a3); -print(Object.isFrozen(a3)); \ No newline at end of file +print(Object.isFrozen(a3)); + +// CreateObjectWithExcludedKeysDealWithProxy +const v0 = { + value : 'name' +}; +const v5 = new Proxy(v0, {}); +const {...v1} = v5; +print(v5.value); +print(v1.value); +print(v0.value); +const arr2 = [1, 2, 3]; +const handler = {}; +const proxy = new Proxy(arr2, handler); +const arr3 = proxy.constructor(1, 2, 3, 4); +print(arr3); diff --git a/test/moduletest/regexp/BUILD.gn b/test/moduletest/regexp/BUILD.gn index c59af61a9e62ab28e2258047aca2fe84c041548f..47b02caa42858837871b8db82acc7de0f2d8e3e0 100644 --- a/test/moduletest/regexp/BUILD.gn +++ b/test/moduletest/regexp/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright (c) 2021 Huawei Device Co., Ltd. +# 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 diff --git a/test/moduletest/regexp/expect_output.txt b/test/moduletest/regexp/expect_output.txt index 80c17bd8c4ae05705d89ba0cac175db7df0c7433..1df4e01d6a24a4096a48701cf84da043124ca1f4 100644 --- a/test/moduletest/regexp/expect_output.txt +++ b/test/moduletest/regexp/expect_output.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2021 Huawei Device Co., Ltd. +# 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 @@ -18,4 +18,31 @@ true true true true - +false +false +false +false +true +false +SC52BAHL01031234567890123456USD +true +true +true +false +true +true +true +false +true +true +aaaabbBbcccC +aaaabb_Bbccc_C +aaaabb_Bbccc_C +_xy_B_xy_C +aaaabbBbcccC +aaaabbBbcccC +aaaabb_Bbccc_C +aaaabb_Bbccc_C +_xy_B_xy_C +aaaabbBbcccC +(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s,(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(,s,(,s,(,s,(,s,(,s,(,s,(,s,(,s diff --git a/test/moduletest/regexp/regexp.js b/test/moduletest/regexp/regexp.js index 809c58074ec6b4f88f733fe2644bc76e1ee2f385..be835b3a2fe0196ac6bc25a02117a67f86db4a89 100644 --- a/test/moduletest/regexp/regexp.js +++ b/test/moduletest/regexp/regexp.js @@ -22,23 +22,292 @@ var reg = /[\x5d-\x7e]/i; var result = reg.test("a"); print(result); -// true var reg1 = new RegExp("^[-+]?([0-9]+)?(\\٫[0-9]{1,})?$"); var result1 = reg1.test('0٫0000000000001'); print(result1); -// true var reg2 = /^[Α-ώ]+$/i print(reg2.test('άέήίΰϊϋόύώ')); -// true + print(reg2.test('ΆΈΉΊΪΫΎΏ')); -// true + print(reg2.test('αβγδεζηθικλμνξοπρςστυφχψω')); -// true + print(reg2.test('ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ')); -// true let reg3 =/^[A-Z0-9_\-]*$/i print(reg3.test('')) -//true \ No newline at end of file + +let reg4 = new RegExp("^(?.*?)(?https?[::]//[^/]+/svn(?:/[a-z0-9.,;?'*:+&%$#=~_ \\u4E00-\\u9FA5-]*)*).*$", "i") +print(reg4.test('a')); + +let reg5 = new RegExp("^(?.*?)(?(?:(?:ht|f)tps?[::]//)?(?:[a-z0-9-]+\\.)+" + "(?:com|edu|gov|mil|net|org|biz|info|name|museum|us|ca|uk|cn|cc|tw|de|au|sg|hk|ei|fr|me|im)(?![a-z])" + "(?:\\:[0-9][0-9]*)?(?:\\.?/[a-z0-9.,;?'\\|*:\\\\+&%$#=~_-]*)*).*$", "i") +print(reg5.test('a')); + +let reg6 = new RegExp("^(?.*?)(?(?:ht|f)tps?[::]//(?:[a-z0-9-]+\\.)*[a-z0-9-]+(?:/[a-z0-9]+)*[/a-z0-9.,;?'\\|*:\\\\+&%$#=~_-]*).*$", "i") +print(reg6.test('a')); + +let reg7 = new RegExp("^(?.*?)(?(?:https?[::]//)?(?:[a-z0-9-\\\\]+\\.)+" + "(?:com|edu|gov|mil|net|org|biz|info|name|museum|us|ca|uk|cn|cc|tw|de|au|sg|hk|ei|fr|me|im)(?![a-z])" + "(?:\\:\\d{4})?(?:/[a-z0-9.,;?'\\|*:\\\\+&%$#=~_-]*)*\\?(?:[a-z0-9]*=[a-z0-9.,;?'*:+%$#=~_\\u4E00-\\u9FA5-]*&?)*).*$", "i") +print(reg7.test('a')); + +let arr = [] +let temp = true +var quotedEmailUserUtf8 = /^([\s\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|(\\[\x01-\x09\x0b\x0c\x0d-\x7f\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))*$/i; +arr.push(quotedEmailUserUtf8.test(" foo m端ller ")) + +let reg8 = /^[A-ZÃÁÀÂÄÇÉÊËÍÏÕÓÔÖÚÜ]+$/i +arr.push(reg8.test('palíndromo')) +arr.push(reg8.test('órgão')) +arr.push(reg8.test('qwértyúão')) +arr.push(reg8.test('àäãcëüïÄÏÜ')) + +let reg9 = /^[A-ZÀÉÈÌÎÓÒÙ]+$/i +arr.push(reg9.test('àéèìîóòù')) +arr.push(reg9.test('metró')) +arr.push(reg9.test('pèsca')) +arr.push(reg9.test('genî')) + +let reg10 = /^[A-ZÀÁẠẢÃÂẦẤẬẨẪĂẰẮẶẲẴĐÈÉẸẺẼÊỀẾỆỂỄÌÍỊỈĨÒÓỌỎÕÔỒỐỘỔỖƠỜỚỢỞỠÙÚỤỦŨƯỪỨỰỬỮỲÝỴỶỸ]+$/i +arr.push(reg10.test('thiến')) +arr.push(reg10.test('nghiêng')) +arr.push(reg10.test('chào')) +arr.push(reg10.test('thế')) +arr.push(reg10.test('giới')) + +let reg11 = /^[A-ZÅÄÖ]+$/i +arr.push(reg11.test('äiti')) + +let reg12 = /^[A-ZÆØÅ]+$/i +arr.push(reg12.test('aøå')) + +let reg13 = /^[A-ZĄĆĘŚŁŃÓŻŹ]+$/i +arr.push(reg13.test('kreską')) +arr.push(reg13.test('zamknięte')) +arr.push(reg13.test('zwykłe')) +arr.push(reg13.test('kropką')) +arr.push(reg13.test('przyjęły')) +arr.push(reg13.test('święty')) +arr.push(reg13.test('Pozwól')) + +let reg14 = /^[А-ЯЂЈЉЊЋЏ]+$/i +arr.push(reg14.test('ШћжЂљЕ')) + +let reg15 = /^[A-ZČĆŽŠĐ]+$/i +arr.push(reg15.test('ŠAabčšđćž')) +arr.push(reg15.test('ŠATROĆčđš')) + +let reg16 = /^[A-ZÁÉÍÑÓÚÜ]+$/i +arr.push(reg16.test('ábcó')) +arr.push(reg16.test('dormís')) +arr.push(reg16.test('volvés')) +arr.push(reg16.test('español')) + +let reg17 = /^[A-ZÅÄÖ]+$/i +arr.push(reg17.test('religiös')) +arr.push(reg17.test('stjäla')) +arr.push(reg17.test('västgöte')) + +let reg18 = /^[A-ZÇĞİıÖŞÜ]+$/i +arr.push(reg18.test('AİıÖöÇ窺ĞğÜüZ')) + +let reg19 = /^[Α-ώ]+$/i +arr.push(reg19.test('άέήίΰϊϋόύώ')) +arr.push(reg19.test('ΆΈΉΊΪΫΎΏ')) + +let reg20 = /^[0-9A-VXYZÇƏĞİıÖŞÜ]+$/i +arr.push(reg20.test('Azərbaycan')) +arr.push(reg20.test('abcç2')) +arr.push(reg20.test('3kərə4kərə')) + +let reg21 = /^[0-9А-Я]+$/i +arr.push(reg21.test('абв1')) +arr.push(reg21.test('жаба')) +arr.push(reg21.test('яГоДа2')) +arr.push(reg21.test('йЮя')) + +let reg22 = /^[0-9A-ZÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ]+$/i +arr.push(reg22.test('řiť123')) + +let reg23 = /^[0-9A-ZÁČĎÉÍŇÓŠŤÚÝŽĹŔĽÄÔ]+$/i +arr.push(reg23.test('1môj')) +arr.push(reg23.test('2ľúbím')) +arr.push(reg23.test('3mäkčeň')) +arr.push(reg23.test('5vŕba')) +arr.push(reg23.test('6ňorimberk')) +arr.push(reg23.test('7ťava')) +arr.push(reg23.test('8žanéta')) +arr.push(reg23.test('9Ďábelské')) +arr.push(reg23.test('10ódy')) + +let reg24 = /^[0-9A-ZÁÉËÏÓÖÜÚ]+$/i +arr.push(reg24.test('Kán123')) +arr.push(reg24.test('één354')) + +let reg25 = /^[0-9A-ZÅÄÖ]+$/i +arr.push(reg25.test('äiti124')) +arr.push(reg25.test('451åå23')) + +let reg26 = /^[0-9A-ZÄÖÜß]+$/i +arr.push(reg26.test('äbc123')) + +let reg27 = /^[0-9A-ZÁÉÍÓÖŐÚÜŰ]+$/i +arr.push(reg27.test('0árvíztűrőtükörfúrógép123')) + +let reg28 = /^[0-9A-ZÃÁÀÂÄÇÉÊËÍÏÕÓÔÖÚÜ]+$/i +arr.push(reg28.test('palíndromo')) +arr.push(reg28.test('2órgão')) +arr.push(reg28.test('qwértyúão9')) +arr.push(reg28.test('àäãcë4üïÄÏÜ')) + +let reg29 = /^[0-9A-ZÀÉÈÌÎÓÒÙ]+$/i +arr.push(reg29.test('123àéèìîóòù')) +arr.push(reg29.test('met23ró')) +arr.push(reg29.test('pès56ca')) +arr.push(reg29.test('gen45î')) + +let reg30 = /^[0-9A-ZÁÉÍÑÓÚÜ]+$/i +arr.push(reg30.test('ábcó123')) + +let reg31 = /^[0-9A-ZÀÁẠẢÃÂẦẤẬẨẪĂẰẮẶẲẴĐÈÉẸẺẼÊỀẾỆỂỄÌÍỊỈĨÒÓỌỎÕÔỒỐỘỔỖƠỜỚỢỞỠÙÚỤỦŨƯỪỨỰỬỮỲÝỴỶỸ]+$/i +arr.push(reg31.test('Thầy3')) +arr.push(reg31.test('3Gà')) + +let reg32 = /^[0-9A-ZĄĆĘŚŁŃÓŻŹ]+$/i +arr.push(reg32.test('kre123ską')) +arr.push(reg32.test('zam21knięte')) +arr.push(reg32.test('zw23ykłe')) +arr.push(reg32.test('prz23yjęły')) +arr.push(reg32.test('świ23ęty')) +arr.push(reg32.test('Poz1322wól')) + +let reg33 = /^[0-9А-ЯЂЈЉЊЋЏ]+$/i +arr.push(reg33.test('ШћжЂљЕ123')) + +let reg34 = /^[0-9A-ZČĆŽŠĐ]+$/i +arr.push(reg34.test('ŠAabčšđćž123')) +arr.push(reg34.test('ŠATRO11Ćčđš')) + +let reg35 = /^[0-9A-ZÅÄÖ]+$/i +arr.push(reg35.test('religiös13')) +arr.push(reg35.test('st23jäla')) +arr.push(reg35.test('västgöte123')) + +let reg36 = /^[0-9A-ZÇĞİıÖŞÜ]+$/i +arr.push(reg36.test('AİıÖöÇ窺ĞğÜüZ123')) + +let reg37 = new RegExp("^[-+]?([0-9]+)?(\\٫[0-9]{1,})?$") +arr.push(reg37.test('0٫0000000000001')) + +let reg38 = new RegExp("^(?:[-+])?(?:[0-9]+)?(?:\\٫[0-9]*)?(?:[eE][\\+\\-]?(?:[0-9]+))?$") +arr.push(reg38.test('123٫')) +arr.push(reg38.test('123٫123')) +arr.push(reg38.test('-123٫123')) + +let reg39 =/^[A-Z0-9_\-]*$/i +arr.push(reg39.test('')) + +let reg40 = RegExp("^(?!-? )(?=.*\\d)(\\¥)?-?(0|[1-9]\\d|[1-9]\\d{0,2}(\\,\\d{3})*)?(\\.(\\d{2}))?$") +arr.push(reg40.test('¥6,954,231')) +arr.push(reg40.test('¥-6,954,231')) + +var reg41 = /^[A-VXYZÇƏĞİıÖŞÜ]+$/i; +arr.push(reg41.test('Azərbaycan')) +arr.push(reg41.test('üöğıəçş')) +arr.push(reg41.test('sizAzərbaycanlaşdırılmışlardansınızmı')) +arr.push(reg41.test('dahaBirDüzgünString')) +arr.push(reg41.test('abcçdeəfgğhxıijkqlmnoöprsştuüvyz')) + +let reg42 = /^[А-Я]+$/i +arr.push(reg42.test('абв')) +arr.push(reg42.test('жаба')) +arr.push(reg42.test('яГоДа')) + +let reg43 = /^[A-ZÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ]+$/i +arr.push(reg43.test('žluťoučký')) +arr.push(reg43.test('Pěl')) +arr.push(reg43.test('Ďábelské')) +arr.push(reg43.test('ódy')) + +let reg44 = /^[A-ZÁČĎÉÍŇÓŠŤÚÝŽĹŔĽÄÔ]+$/i +arr.push(reg44.test('môj')) +arr.push(reg44.test('ľúbím')) +arr.push(reg44.test('mäkčeň')) +arr.push(reg44.test('vŕba')) +arr.push(reg44.test('ňorimberk')) + +let reg45 = /^[A-ZÆØÅ]+$/i +arr.push(reg45.test('aøå')) + +let reg46 = /^[A-ZÁÉËÏÓÖÜÚ]+$/i +arr.push(reg46.test('Kán')) +arr.push(reg46.test('één')) +arr.push(reg46.test('vóór')) +arr.push(reg46.test('nú')) +arr.push(reg46.test('héél')) + +let reg47 = /^[A-ZÄÖÜß]+$/i +arr.push(reg47.test('äbc')) +arr.push(reg47.test('FöÖbär')) + +let reg48 = /^[A-ZÁÉÍÓÖŐÚÜŰ]+$/i +arr.push(reg48.test('árvíztűrőtükörfúrógép')) + +arr.forEach((item)=>{ + if(!item){ + temp = false + } +}) +print(temp) + +let arr1 = [] +let temp1 = false +let reg49 = /[^A-Z0-9+\/=]/i; +arr1.push(reg49.test("Zg==")); +arr1.push(reg49.test("Zm8=")); +arr1.push(reg49.test("Zm9v")); +arr1.push(reg49.test("Zm9vYg==")); +arr1.push(reg49.test("Zm9vYmE=")); +arr1.push(reg49.test("Zm9vYmFy")); +arr1.push(reg49.test( + "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4=" +)); +arr1.push(reg49.test("Vml2YW11cyBmZXJtZW50dW0gc2VtcGVyIHBvcnRhLg==")); +arr1.push(reg49.test("U3VzcGVuZGlzc2UgbGVjdHVzIGxlbw==")); +arr1.forEach((item)=>{ + if(item){ + temp1 = true + } +}) +print(temp1) +let str1 = 'SC52BAHL01031234567890123456USD' +print(str1.replace(/[^A-Z0-9]+/gi, '')) + +let reg50 = /^[ABCEGHJKLMNPRSTVXY]\d[ABCEGHJ-NPRSTV-Z][\s-]?\d[ABCEGHJ-NPRSTV-Z]\d$/i + +let regabc = /abc/g; +let strabcd = "abcdabcdabcd"; +for (let i = 0; i < 10; i++) { + // cache is used in this case + print(regabc.test(strabcd)); +} + +let str2 = "aaaabbBbcccC"; +for (let i = 0; i < 2; i++) { + print(str2); + let t1 = str2.replace(/([A-Z])/g, function(e) { + return "_" + e; + }); + print(t1); + let t2 = str2.replace(/([A-Z])/g, "_$1"); + print(t2); + print(t1.replace(/([a-z]+)/g, "_xy")); + print(t2.replace(/_/g, "")); +} + +// regexp cache test +let mediaReg = "\([^\)]+\)\([^\)]+\)\([^\)]+\)\([^\)]+\)\([^\)]+\)\([^\)]+\)\([^\)]+\)\([^\)]+\)\([^\)]+\)\([^\)]+\)\([^\)]+\)\([^\)]+\)\([^\)]+\)\([^\)]+\)\([^\)]+\)\([^\)]+\)"; +let string = '(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s'; +const regex1 = new RegExp(mediaReg); +let matchArray = string.match(regex1); +print(matchArray); diff --git a/test/moduletest/regexpflagd/BUILD.gn b/test/moduletest/regexpflagd/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..2e08d96260c036ef3a568cc0c0a914e0e62b5338 --- /dev/null +++ b/test/moduletest/regexpflagd/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("regexpflagd") { + deps = [] +} diff --git a/test/moduletest/regexpflagd/expect_output.txt b/test/moduletest/regexpflagd/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..ea7c46a46b0872a11c7b871faf3ac01a28ded610 --- /dev/null +++ b/test/moduletest/regexpflagd/expect_output.txt @@ -0,0 +1,32 @@ +# Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +true +dgm +0,9 +3,4 +8,9 +3,4 +8,9 +李 +雷 +10,20 +13,14 +18,20 +13,14 +18,20 +韩 +梅梅 +1,2 +1,2 +undefined diff --git a/test/moduletest/regexpflagd/regexpflagd.js b/test/moduletest/regexpflagd/regexpflagd.js new file mode 100644 index 0000000000000000000000000000000000000000..854a99baf159da455598e1f3214d4b3fb44250e3 --- /dev/null +++ b/test/moduletest/regexpflagd/regexpflagd.js @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * @tc.name:regexpflagd + * @tc.desc:test Regexp d flag + * @tc.type: FUNC + * @tc.require: issueI5NO8G + */ +var regexpNames = /姓氏:(?.+),名字:(?.+)/gmd; +var users = `姓氏:李,名字:雷\n姓氏:韩,名字:梅梅`; +var result = regexpNames.exec(users); +print(regexpNames.hasIndices); +print(regexpNames.flags); +print(result.indices[0]); +print(result.indices[1]); +print(result.indices[2]); +print(result.indices.groups.first); +print(result.indices.groups.last); +print(result.groups.first); +print(result.groups.last); + +result = regexpNames.exec(users); +print(result.indices[0]); +print(result.indices[1]); +print(result.indices[2]); +print(result.indices.groups.first); +print(result.indices.groups.last); +print(result.groups.first); +print(result.groups.last); + +var result2 = "bπb".match(/(π)/du).indices; +print(result2[0]); +print(result2[1]); +print(result2.groups); diff --git a/test/moduletest/setobjectwithproto/expect_output.txt b/test/moduletest/setobjectwithproto/expect_output.txt index 0010910b34ff44da75161c93677a6f698620a9a0..42c263d41870499dca83aeed7ef237abdc98971a 100644 --- a/test/moduletest/setobjectwithproto/expect_output.txt +++ b/test/moduletest/setobjectwithproto/expect_output.txt @@ -12,3 +12,7 @@ # limitations under the License. null +true +true +true +true diff --git a/test/moduletest/setobjectwithproto/setobjectwithproto.js b/test/moduletest/setobjectwithproto/setobjectwithproto.js index 6c5c5dc74417df653cbe84be60befd6d1706cb52..907df54a3a06cda22d3571ee3f613e278a472b63 100644 --- a/test/moduletest/setobjectwithproto/setobjectwithproto.js +++ b/test/moduletest/setobjectwithproto/setobjectwithproto.js @@ -22,5 +22,16 @@ var object = { __proto__: null }; - -print(Object.getPrototypeOf(object)); \ No newline at end of file + +print(Object.getPrototypeOf(object)); + +class C32 extends String { + +} +let obj1 = new C32(); +print(obj1.__proto__ == C32.prototype); +print(C32.__proto__ == String); +C32.__proto__ = Array; +let obj2 = new C32(); +print(obj2.__proto__ == C32.prototype); +print(C32.__proto__ == Array); \ No newline at end of file diff --git a/test/moduletest/spreadoperator/expect_output.txt b/test/moduletest/spreadoperator/expect_output.txt index 1d2b373eef3cb77f975fa06b07ec9b509cb6f4dd..f4c4aeb49046ae598ad683f3247215f8df8f9bc9 100644 --- a/test/moduletest/spreadoperator/expect_output.txt +++ b/test/moduletest/spreadoperator/expect_output.txt @@ -34,3 +34,9 @@ foo Apple Banana 1 2 3 1 2 3 +0 +1 +2 +3 +4 +true diff --git a/test/moduletest/spreadoperator/spreadoperator.js b/test/moduletest/spreadoperator/spreadoperator.js index b738f5273a92b1405f40c562a2ca641a73f3da8c..73c58a45c2b81235659d733db341772c1fa4ebfd 100644 --- a/test/moduletest/spreadoperator/spreadoperator.js +++ b/test/moduletest/spreadoperator/spreadoperator.js @@ -110,4 +110,30 @@ Array.prototype[Symbol.iterator] = function* () { yield 3; } print(...fruits1) -print(...fruits2) \ No newline at end of file +print(...fruits2) + +// test spread array when encounter situations like [...arr, elem1, elem2] with arr be StableJSArray +function appendChild(newNode) { + this.childNodes = [...this.childNodes, newNode]; +} +const app = { tageName: 'VIEW', childNodes: [], appendChild }; +for (let i = 0; i < 5; ++i) { + const el = { tageName: 'VIEW', childNodes: [], appendChild }; + const text = { tageName: 'VIEW', childNodes: [], appendChild }; + const content = { tageName: '#text', content: i }; + text.appendChild(content); + el.appendChild(text); + app.appendChild(el); +} +for (let i = 0; i < 5; ++i) { + print(app.childNodes[i].childNodes[0].childNodes[0].content); +} +let result = [] +try { + class C29 {}; + const v66 = undefined; + new C29(...v66); +} catch (err) { + result.push(err.name == "TypeError"); +} +print(result) \ No newline at end of file diff --git a/test/moduletest/storeicbyname/BUILD.gn b/test/moduletest/storeicbyname/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..d4fed14c68e6e3771a163cb43e5383f043f4e23d --- /dev/null +++ b/test/moduletest/storeicbyname/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("storeicbyname") { + deps = [] +} diff --git a/test/moduletest/storeicbyname/expect_output.txt b/test/moduletest/storeicbyname/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..acf7908bc337d484ae9d0cf1f7b4939c9ae886b2 --- /dev/null +++ b/test/moduletest/storeicbyname/expect_output.txt @@ -0,0 +1,14 @@ +# 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. + +test successful !!! diff --git a/test/moduletest/storeicbyname/storeicbyname.js b/test/moduletest/storeicbyname/storeicbyname.js new file mode 100644 index 0000000000000000000000000000000000000000..5e76fd52705dbd5f651fa89854baa03fa8069de7 --- /dev/null +++ b/test/moduletest/storeicbyname/storeicbyname.js @@ -0,0 +1,30 @@ +/* + * 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. + */ + +/* + * @tc.name:storeicbyname + * @tc.desc:test storeicbyname + * @tc.type: FUNC + * @tc.require: issueI7UTOA + */ + +const arr = []; +for (let i = 0; i < 200; i++) { + +} +for (let i = 0; i < 100; i++) { + arr.length = 1025; +} +print("test successful !!!"); diff --git a/test/moduletest/string/BUILD.gn b/test/moduletest/string/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..8346efc11011a88d2c51089c3f19b852922e4981 --- /dev/null +++ b/test/moduletest/string/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("string") { + deps = [] +} diff --git a/test/moduletest/string/expect_output.txt b/test/moduletest/string/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..b43d0818640de069076b785c4946624d5e23d0c5 --- /dev/null +++ b/test/moduletest/string/expect_output.txt @@ -0,0 +1,35 @@ +# 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. + +false +false +true +true +false +Invalid string length +今 +20170 +吃什么: +djdjsajkdfalddgetg +wode +wode1 +true +true + +A +AB +— + +testfiles就发(2) +testfileswodet +true diff --git a/test/moduletest/string/string.js b/test/moduletest/string/string.js new file mode 100644 index 0000000000000000000000000000000000000000..a2bbe04b965d54d40b57ec4f196a92936d31ff07 --- /dev/null +++ b/test/moduletest/string/string.js @@ -0,0 +1,98 @@ +/* + * 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. + */ + +/* + * @tc.name:String + * @tc.desc:test String + * @tc.type: FUNC + * @tc.require: issueI5NO8G + */ +let tmp = '今天吃什么:告诉了会晤今天吃什么促“让今天吃什么下来好起来”今天吃今天吃什么于6月1日于北京今天吃什么面。大杂烩中国都五年来首位访华\ +的今天吃什么王刚说,你吵的菜今天吃什么了赞不绝口,但是今天吃什么吃,他接到一个快递单号为#nsExpress3SF#123456789的包裹'; +let flag = tmp; +let str1 = tmp.substring(0, 111) + ',,,,,,,,,,,,,,,,,,,,,,,' + tmp.substring(111 + 23); +let str2 = tmp.substring(0, 111) + 'nsExpress3SF#123456789的' + tmp.substring(111 + 23,111 + 24) + '需'; +let str3 = tmp.substring(0, 111) + 'nsExpress3SF#123456789的' + tmp.substring(111 + 23); +print((flag === str1).toString()); +print((flag === str2).toString()); +print((flag === str3).toString()); + +let utf81 = '12312463347659634287446568765344234356798653455678899865746435323456709876543679334675235523463756875153764736256\ +235252537585734523759078945623465357867096834523523765868'; +let utf82 = utf81.substring(0, 169) + '8'; +let utf83 = utf81.substring(0, 169) + '0'; +print((utf81 === utf82).toString()); +print((utf81 === utf83).toString()); + +function foo(a) { + return a; +} +try { + for (let i = 0; i < 25; i++) { + foo += foo; + } +} catch (e) { + print(e.message); +} +let string1 = "fdjDJSAjkdfalDDGETG"; +let string2 = string1.substring(1); +print(str1.charAt()); +print(str2.charCodeAt()); +print(str1.substr(2, 4)); +print(string2.toLowerCase(2, 4)); + + +let tmp1 = '今天吃什么:哈哈哈会晤美国phonePlatformphonePlatformphonePlatformphonePlatformphonePlatformphonePlatformphonePlatformphonePlatformphonePlatformphonePlatform国务卿促“让今天吃什么下来好起来”今天吃今天吃什么于6月1日于北京今天吃什么面。大杂烩中国都五年来首位访华\ +的今天吃什么王刚说,你吵的菜今天吃什么了赞不绝口,但是今天吃什么吃,他接到一个快递单号为#nsExpress3SF#123456789的包裹'; +let flag1 = tmp1; +let str = tmp1.substring(13, 143); +let str4 = tmp1.substring(13, 26); +let str5 = "phonePlatformphonePlatformphonePlatformphonePlatformphonePlatformphonePlatformphonePlatformphonePlatformphonePlatformphonePlatform"; +let str6 = "phonePlatform"; +var obj = {phonePlatformphonePlatformphonePlatformphonePlatformphonePlatformphonePlatformphonePlatformphonePlatformphonePlatformphonePlatform: "wode", +phonePlatform: "wode1"}; + +print(obj[str]); +print(obj[str4]); +print((str === str5).toString()); +print((str4 == str6).toString()); + +var s0 = String.fromCharCode(); +var s1 = String.fromCharCode(65); +var s2 = String.fromCharCode(65, 66.678); +var s3 = String.fromCharCode(0x12014); +var s4 = String.fromCharCode(true); +print(s0); +print(s1); +print(s2); +print(s3); +print(s4); + +let name1 = 'testfiles就发(2).png'; +let filename1 = name1.substring(0, 14); +let kk1 = filename1.toLowerCase(); +print(kk1); + +let name2 = 'testfileswodetwwwpng'; +let filename2 = name2.substring(0, 14); +let kk2 = filename2.toLowerCase(); +print(kk2); + + +var strA = name2 + name1; +for (let i = 0; i < 100; i++) { +strA.toLocaleString(); +} +print("true"); diff --git a/test/moduletest/stubbuilder/stubbuilder.js b/test/moduletest/stubbuilder/stubbuilder.js index aa8041d10d505524dbe1bbfb9be046b48f133bf5..0405a7b3693cb648837807c2b17f801a3a6ecbbb 100644 --- a/test/moduletest/stubbuilder/stubbuilder.js +++ b/test/moduletest/stubbuilder/stubbuilder.js @@ -576,7 +576,7 @@ * SetPropertyByName(); SetTypeArrayPropertyByName(); CallSetterHelper(); ShouldCallSetter(); IsWritable(); UpdateValueInDict(); * AddPropertyByName(); SetHasConstructorCondition(); SetHasConstructorToHClass(); SetPropertyInlinedProps(); SetOffsetFieldInPropAttr(); * SetIsInlinePropsFieldInPropAttr(); JSHClassAddProperty(); IsDictionaryMode(); SetPropertiesArray(); SetDictionaryOrderFieldInPropAttr(); -* ComputePropertyCapacityInJSObj(); +* ComputeNonInlinedFastPropsCapacity; */ /**************HandleStobjbynameImm8Id16V8****************/ { diff --git a/test/moduletest/trycatch/expect_output.txt b/test/moduletest/trycatch/expect_output.txt index b9dc18c45eca1b01614899e2cc8105ae00332de5..1a2adee4ff91475537675f1efeda8c4351c4898e 100644 --- a/test/moduletest/trycatch/expect_output.txt +++ b/test/moduletest/trycatch/expect_output.txt @@ -11,6 +11,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +TypeError TypeError RangeError ReferenceError diff --git a/test/moduletest/trycatch/trycatch.js b/test/moduletest/trycatch/trycatch.js index 179c02cabcfe0edb0ea6ab567e1042373399f71e..fafe92d9553b9cfd6d9c5b569e32e1ed9a72cfd6 100644 --- a/test/moduletest/trycatch/trycatch.js +++ b/test/moduletest/trycatch/trycatch.js @@ -19,6 +19,17 @@ * @tc.type: FUNC * @tc.require: issueI5NO8G */ +const o = { +} +o.toString = 'a' +const o11 = { +} +try { + o11[[o]] = [] +} catch (error) { + print("TypeError") +} + try { var a = 1; a(); diff --git a/test/moduletest/typearray/expect_output.txt b/test/moduletest/typearray/expect_output.txt index 8f52daa2db975be16e871d5cf2d4d30cfa16fce1..6e2a0a4f5cb7ce8ef28a95a82fdc10a0f3ade17e 100644 --- a/test/moduletest/typearray/expect_output.txt +++ b/test/moduletest/typearray/expect_output.txt @@ -27,3 +27,16 @@ BigUint64Array test success !!! test successful !!! test successful !!! false +3,2,1 +1,2,3 +test successful !!! +test successful !!! +RangeError: The sum of srcLength and targetOffset is greater than targetLength. +RangeError: The sum of srcLength and targetOffset is greater than targetLength. +RangeError: The newByteLength is out of range. +true +true +true +RangeError +0,0,0,0 +not-equal diff --git a/test/moduletest/typearray/typearray.js b/test/moduletest/typearray/typearray.js index b7aed513bc1bf2c8c1b526ecab1f9576a8585a51..3eeb2e1b600b21b4cb059ff47e90ef82d02495f6 100644 --- a/test/moduletest/typearray/typearray.js +++ b/test/moduletest/typearray/typearray.js @@ -198,4 +198,64 @@ const a7 = new BigInt64Array(4); function foo() {} const f = new foo(); const protoOf = f.isPrototypeOf; -print(protoOf.apply(protoOf, a7)); \ No newline at end of file +print(protoOf.apply(protoOf, a7)); +const uint8 = new Uint8Array([1, 2, 3]); +const reversedUint8 = uint8.toReversed(); +print(reversedUint8); // Uint8Array [3, 2, 1] +print(uint8); // Uint8Array [1, 2, 3] + +try { + const a8 = new Int8Array(new ArrayBuffer(0x40004141, {"maxByteLength": 0x40004141})); + const a9 = new Float64Array(a8); +} catch (e) { + print("test successful !!!"); +} + +try { + const a10 = [1, 2]; + const a11 = new Uint8Array(a10); + const a12 = new Uint32Array(a11); + a12.set(a10, 0x1ffffffff); +} catch (e) { + print("test successful !!!"); +} + +try { + const v17 = new Int16Array(5); + const v20 = new Int16Array(5); + v17.set(v20, 4294967295); +} catch (error) { + print(error) +} + +try { + const v17 = new Int16Array(5); + const v20 = new Int16Array(5); + v17.set(v20, 4294967296); +} catch (error) { + print(error) +} + +try { + new BigUint64Array(new ArrayBuffer(256), 256, 0x1fffffff) +} catch (error) { + print(error) +} + +let arr12 = new Uint8Array(256).fill(255); +print(arr12[0] == 255); +print(arr12[123] == 255); +print(arr12[255] == 255); + +try { + new Uint8Array(2 ** 32 - 1); +} catch (error) { + print(error.name); +} + + +const v21 = new SharedArrayBuffer(32); +const v22 = new BigInt64Array(v21); +Atomics.or(v22, Int16Array, false); +print(v22); +print(Atomics.wait(v22, false, true)); diff --git a/test/moduletest/typedarrayat/BUILD.gn b/test/moduletest/typedarrayat/BUILD.gn new file mode 100755 index 0000000000000000000000000000000000000000..a81c4f34807866fd5cb2862a3937294eaab10699 --- /dev/null +++ b/test/moduletest/typedarrayat/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("typedarrayat") { + deps = [] +} diff --git a/test/moduletest/typedarrayat/expect_output.txt b/test/moduletest/typedarrayat/expect_output.txt new file mode 100755 index 0000000000000000000000000000000000000000..b81a5043903133fee3d46bac0f685bc3b58bbf93 --- /dev/null +++ b/test/moduletest/typedarrayat/expect_output.txt @@ -0,0 +1,24 @@ +# Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Float64Array test success !!! +Float32Array test success !!! +Int32Array test success !!! +Int16Array test success !!! +Int8Array test success !!! +Uint32Array test success !!! +Uint16Array test success !!! +Uint8Array test success !!! +Uint8ClampedArray test success !!! +BigInt64Array test success !!! +BigUint64Array test success !!! diff --git a/test/moduletest/typedarrayat/typedarrayat.js b/test/moduletest/typedarrayat/typedarrayat.js new file mode 100755 index 0000000000000000000000000000000000000000..4700ef37428351351a65a165615ea414e7ab2bf6 --- /dev/null +++ b/test/moduletest/typedarrayat/typedarrayat.js @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * @tc.name:typedarrayat + * @tc.desc:test TypedArray.at + * @tc.type: FUNC + * @tc.require: issueI7F8N1 + */ +[ + Float64Array, + Float32Array, + Int32Array, + Int16Array, + Int8Array, + Uint32Array, + Uint16Array, + Uint8Array, + Uint8ClampedArray +].forEach(function (ctor, i) { + if (testTypeArrayAt1(ctor)) { + print(ctor.name + " test success !!!") + } else { + print(ctor.name + " test fail !!!") + } +}); + +[ + BigInt64Array, + BigUint64Array +].forEach(function (ctor, i) { + if (testTypeArrayAt2(ctor)) { + print(ctor.name + " test success !!!") + } else { + print(ctor.name + " test fail !!!") + } +}); + +function testTypeArrayAt1(ctor) { + let result = [] + let obj = new ctor(5); + obj[0] = 10; + obj[1] = 11; + obj[2] = 12; + obj[3] = 13; + obj[4] = 14; + result.push(obj.at(-1) == 14); + result.push(obj.at(1.5) == 11); + result.push(obj.at(-3) == 12); + result.push(obj.at("3") == 13) + result.push(obj.at(-6) == undefined); + result.push(obj.at(5) == undefined); + result.push(obj.at({}) == 10); + for (let i = 0; i < result.length; i++) { + if (!result[i]) { + return false; + } + } + return true; +} + +function testTypeArrayAt2(ctor) { + let result = [] + let obj = new ctor(5); + obj[0] = 10n; + obj[1] = 11n; + obj[2] = 12n; + obj[3] = 13n; + obj[4] = 9017199254740995n; + result.push(obj.at(-1) == 9017199254740995n); + result.push(obj.at(1.5) == 11n); + result.push(obj.at(-3) == 12n); + result.push(obj.at("3") == 13n) + result.push(obj.at(-6) == undefined); + result.push(obj.at(5) == undefined); + result.push(obj.at({}) == 10n); + for (let i = 0; i < result.length; i++) { + if (!result[i]) { + return false; + } + } + return true; +} \ No newline at end of file diff --git a/test/moduletest/typedarrayfindlast/BUILD.gn b/test/moduletest/typedarrayfindlast/BUILD.gn new file mode 100755 index 0000000000000000000000000000000000000000..9e1c6cb2e9e8c4edd5d4a1bac299a265567b137b --- /dev/null +++ b/test/moduletest/typedarrayfindlast/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("typedarrayfindlast") { + deps = [] +} diff --git a/test/moduletest/typedarrayfindlast/expect_output.txt b/test/moduletest/typedarrayfindlast/expect_output.txt new file mode 100755 index 0000000000000000000000000000000000000000..b81a5043903133fee3d46bac0f685bc3b58bbf93 --- /dev/null +++ b/test/moduletest/typedarrayfindlast/expect_output.txt @@ -0,0 +1,24 @@ +# Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Float64Array test success !!! +Float32Array test success !!! +Int32Array test success !!! +Int16Array test success !!! +Int8Array test success !!! +Uint32Array test success !!! +Uint16Array test success !!! +Uint8Array test success !!! +Uint8ClampedArray test success !!! +BigInt64Array test success !!! +BigUint64Array test success !!! diff --git a/test/moduletest/typedarrayfindlast/typedarrayfindlast.js b/test/moduletest/typedarrayfindlast/typedarrayfindlast.js new file mode 100644 index 0000000000000000000000000000000000000000..8b820c6200b49dd541689d67a35f48c277e1b4f7 --- /dev/null +++ b/test/moduletest/typedarrayfindlast/typedarrayfindlast.js @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * @tc.name:typedarrayfindlast + * @tc.desc:test TypedArray.findLast and TypedArray.findLastIndex + * @tc.type: FUNC + * @tc.require: issueI7LP2E + */ + +[ + Float64Array, + Float32Array, + Int32Array, + Int16Array, + Int8Array, + Uint32Array, + Uint16Array, + Uint8Array, + Uint8ClampedArray +].forEach(function (ctor, i) { + if (testTypeArrayAt1(ctor)) { + print(ctor.name + " test success !!!"); + } else { + print(ctor.name + " test fail !!!"); + } +}); + +[ + BigInt64Array, + BigUint64Array +].forEach(function (ctor, i) { + if (testTypeArrayAt2(ctor)) { + print(ctor.name + " test success !!!"); + } else { + print(ctor.name + " test fail !!!"); + } +}); + +function testTypeArrayAt1(ctor) { + let result = []; + let obj = new ctor([10, 11, 12, 13, 14]); + let result1 = obj.findLast((element, index, array) => { + return (element == 12); + }); + result.push(result1 == 12); + result1 = obj.findLast((element, index, array) => { + return (element < 10); + }); + result.push(result1 == undefined); + result1 = obj.findLastIndex((element, index, array) => { + if (index == 3) { + array[3] = 100; + } + return (element == 100); + }); + result.push(result1 == -1); + result1 = obj.findLastIndex((element, index, array) => { + return (element == 100); + }); + result.push(result1 == 3); + for (let i = 0; i < result.length; i++) { + if (!result[i]) { + return false; + } + } + return true; +} + +function testTypeArrayAt2(ctor) { + let result = [] + let obj = new ctor([10n, 11n, 12n, 13n, 9017199254740995n]); + let result1 = obj.findLast((element, index, array) => { + return (element == 12n); + }); + result.push(result1 == 12n); + result1 = obj.findLast((element, index, array) => { + return (element < 10n); + }); + result.push(result1 == undefined); + result1 = obj.findLastIndex((element, index, array) => { + if (index == 3) { + array[3] = 100n; + } + return (element == 100n); + }); + result.push(result1 == -1); + result1 = obj.findLastIndex((element, index, array) => { + return (element == 100n); + }); + result.push(result1 == 3); + for (let i = 0; i < result.length; i++) { + if (!result[i]) { + return false; + } + } + return true; +} \ No newline at end of file diff --git a/test/moduletest/typedarraynan/BUILD.gn b/test/moduletest/typedarraynan/BUILD.gn new file mode 100755 index 0000000000000000000000000000000000000000..2e2e4c53b24fa265c8c708e4565c5ae46f2e1e96 --- /dev/null +++ b/test/moduletest/typedarraynan/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("typedarraynan") { + deps = [] +} diff --git a/test/moduletest/typedarraynan/expect_output.txt b/test/moduletest/typedarraynan/expect_output.txt new file mode 100755 index 0000000000000000000000000000000000000000..6c6d1f002226bca74d406aec74c4f697f6c9ac77 --- /dev/null +++ b/test/moduletest/typedarraynan/expect_output.txt @@ -0,0 +1,23 @@ +# Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +4294967291 +4294967296 +NaN +NaN +-1 +1128267775 +9007199254740991 +0 +2146959360 +NaN diff --git a/test/moduletest/typedarraynan/typedarraynan.js b/test/moduletest/typedarraynan/typedarraynan.js new file mode 100644 index 0000000000000000000000000000000000000000..0b79d22bce212470ea705867254c6fd2e67889a4 --- /dev/null +++ b/test/moduletest/typedarraynan/typedarraynan.js @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * @tc.name:typedarraynan + * @tc.desc:test TypedArray with NaN + * @tc.type: FUNC + * @tc.require: issueI7X3AY + */ + +-4n ^-4n; +8>>8; +const o17 = { + "maxByteLength":8, +}; +o17.b = 8; +o17.b = o17; +const v18 = new SharedArrayBuffer(8,o17); +const v19 = new Uint32Array(v18,4); +v19[0]=-5; +const v20 = new Float32Array(v18); +try { + v20.toReversed(); +} catch(err) { +} +v20.set(v19); +print(v19[0]); +print(v20[0]); +print(v20[1]); + +var buffer = new ArrayBuffer(8); +var array1 = new Int32Array(buffer); +array1[0] = -5; +array1[1] = -5; +var array2 = new Float64Array(buffer); +print(array2[0]); + +array2[0] = 9007199254740991; +print(array1[0]); +print(array1[1]); +print(array2[0]); + +array2[0] = NaN; +print(array1[0]); +print(array1[1]); +print(array2[0]); \ No newline at end of file diff --git a/test/moduletest/typedarraytosorted/BUILD.gn b/test/moduletest/typedarraytosorted/BUILD.gn new file mode 100755 index 0000000000000000000000000000000000000000..7596ecf8a0ed8130f861f19229d1e7380de54eff --- /dev/null +++ b/test/moduletest/typedarraytosorted/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("typedarraytosorted") { + deps = [] +} diff --git a/test/moduletest/typedarraytosorted/expect_output.txt b/test/moduletest/typedarraytosorted/expect_output.txt new file mode 100755 index 0000000000000000000000000000000000000000..b81a5043903133fee3d46bac0f685bc3b58bbf93 --- /dev/null +++ b/test/moduletest/typedarraytosorted/expect_output.txt @@ -0,0 +1,24 @@ +# Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Float64Array test success !!! +Float32Array test success !!! +Int32Array test success !!! +Int16Array test success !!! +Int8Array test success !!! +Uint32Array test success !!! +Uint16Array test success !!! +Uint8Array test success !!! +Uint8ClampedArray test success !!! +BigInt64Array test success !!! +BigUint64Array test success !!! diff --git a/test/moduletest/typedarraytosorted/typedarraytosorted.js b/test/moduletest/typedarraytosorted/typedarraytosorted.js new file mode 100644 index 0000000000000000000000000000000000000000..aa87025ea2549576c36f8dd450b4e7d88579a625 --- /dev/null +++ b/test/moduletest/typedarraytosorted/typedarraytosorted.js @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * @tc.name:typedarraytosorted + * @tc.desc:test TypedArray.toSorted + * @tc.type: FUNC + * @tc.require: issueI7MUIL + */ + +[ + Float64Array, + Float32Array, + Int32Array, + Int16Array, + Int8Array, + Uint32Array, + Uint16Array, + Uint8Array, + Uint8ClampedArray +].forEach(function (ctor, i) { + if (testTypeArrayToSorted1(ctor)) { + print(ctor.name + " test success !!!"); + } else { + print(ctor.name + " test fail !!!"); + } +}); + +[ + BigInt64Array, + BigUint64Array +].forEach(function (ctor, i) { + if (testTypeArrayToSorted2(ctor)) { + print(ctor.name + " test success !!!"); + } else { + print(ctor.name + " test fail !!!"); + } +}); + +function testTypeArrayToSorted1(ctor) { + let arr = [10, 3, 8, 5, 30, 100, 6, 7, 100, 3]; + let obj = new ctor(arr); + let arr2 = obj.toSorted(); + let result = []; + result.push(arr2.toString() == "3,3,5,6,7,8,10,30,100,100"); + arr2 = obj.toSorted((x, y) => { return x < y }); + result.push(arr2.toString() == "100,100,30,10,8,7,6,5,3,3"); + try { + arr2 = obj.toSorted(true); + } catch (err) { + result.push(err.name == "TypeError"); + } + for (let i = 0; i < result.length; i++) { + if (!result[i]) { + return false; + } + } + return true; +} + +function testTypeArrayToSorted2(ctor) { + let arr = [10n, 3n, 8n, 5n, 30n, 100n, 6n, 7n, 100n, 3n]; + let obj = new ctor(arr); + let arr2 = obj.toSorted(); + let result = []; + result.push(arr2.toString() == "3,3,5,6,7,8,10,30,100,100"); + arr2 = obj.toSorted((x, y) => { return x < y }); + result.push(arr2.toString() == "100,100,30,10,8,7,6,5,3,3"); + try { + arr2 = obj.toSorted(true); + } catch (err) { + result.push(err.name == "TypeError"); + } + for (let i = 0; i < result.length; i++) { + if (!result[i]) { + return false; + } + } + return true; +} \ No newline at end of file diff --git a/test/moduletest/typedarraywith/BUILD.gn b/test/moduletest/typedarraywith/BUILD.gn new file mode 100755 index 0000000000000000000000000000000000000000..1d28ff1ae9cda82f9cca6a536a18100b55c0793a --- /dev/null +++ b/test/moduletest/typedarraywith/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("typedarraywith") { + deps = [] +} diff --git a/test/moduletest/typedarraywith/expect_output.txt b/test/moduletest/typedarraywith/expect_output.txt new file mode 100755 index 0000000000000000000000000000000000000000..b81a5043903133fee3d46bac0f685bc3b58bbf93 --- /dev/null +++ b/test/moduletest/typedarraywith/expect_output.txt @@ -0,0 +1,24 @@ +# Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Float64Array test success !!! +Float32Array test success !!! +Int32Array test success !!! +Int16Array test success !!! +Int8Array test success !!! +Uint32Array test success !!! +Uint16Array test success !!! +Uint8Array test success !!! +Uint8ClampedArray test success !!! +BigInt64Array test success !!! +BigUint64Array test success !!! diff --git a/test/moduletest/typedarraywith/typedarraywith.js b/test/moduletest/typedarraywith/typedarraywith.js new file mode 100644 index 0000000000000000000000000000000000000000..2430b9a982ed932befdeb8e7a54501ac205189d8 --- /dev/null +++ b/test/moduletest/typedarraywith/typedarraywith.js @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * @tc.name:typedarraywith + * @tc.desc:test TypedArray.with + * @tc.type: FUNC + * @tc.require: issueI7MUIL + */ + +[ + Float64Array, + Float32Array, + Int32Array, + Int16Array, + Int8Array, + Uint32Array, + Uint16Array, + Uint8Array, + Uint8ClampedArray +].forEach(function (ctor, i) { + if (testTypeArrayWith1(ctor)) { + print(ctor.name + " test success !!!"); + } else { + print(ctor.name + " test fail !!!"); + } +}); + +[ + BigInt64Array, + BigUint64Array +].forEach(function (ctor, i) { + if (testTypeArrayWith2(ctor)) { + print(ctor.name + " test success !!!"); + } else { + print(ctor.name + " test fail !!!"); + } +}); + +function testTypeArrayWith1(ctor) { + let arr = [1, 2, 3, 4, 5]; + let obj = new ctor(arr); + let result = []; + let arr2 = obj.with(-2, 40); + result.push(arr2[3] == 40); + try { + arr2 = obj.with(10, 10); + } catch (err) { + result.push(err.name == "RangeError"); + } + for (let i = 0; i < result.length; i++) { + if (!result[i]) { + return false; + } + } + return true; +} + +function testTypeArrayWith2(ctor) { + let result = []; + let obj = new ctor(5); + obj[0] = 10n; + obj[1] = 11n; + obj[2] = 12n; + obj[3] = 13n; + obj[4] = 9017199254740995n; + let arr2 = obj.with(-2, 40n); + result.push(arr2[3] == 40n); + try { + arr2 = obj.with(10, 10n); + } catch (err) { + result.push(err.name == "RangeError"); + } + for (let i = 0; i < result.length; i++) { + if (!result[i]) { + return false; + } + } + return true; +} \ No newline at end of file diff --git a/test/moduletest/weakcollectionswithsymbol/BUILD.gn b/test/moduletest/weakcollectionswithsymbol/BUILD.gn new file mode 100755 index 0000000000000000000000000000000000000000..c885103b577f8e9514c54548a6a64fc22269a8de --- /dev/null +++ b/test/moduletest/weakcollectionswithsymbol/BUILD.gn @@ -0,0 +1,45 @@ +# Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("weakmapsymbolkey") { + deps = [] +} + +host_moduletest_action("weaksetsymbolvalue") { + deps = [] +} + +host_moduletest_action("weakrefforsymbol") { + deps = [] +} + +host_moduletest_action("finalizationregistrywithsymbol") { + deps = [] +} + +host_moduletest_action("weakcollectionswithsymbol") { + extra_modules = [ + "weakmapsymbolkey", + "weaksetsymbolvalue", + "weakrefforsymbol", + "finalizationregistrywithsymbol", + ] + deps = [ + ":gen_finalizationregistrywithsymbol_abc", + ":gen_weakmapsymbolkey_abc", + ":gen_weakrefforsymbol_abc", + ":gen_weaksetsymbolvalue_abc", + ] +} diff --git a/test/moduletest/weakcollectionswithsymbol/expect_output.txt b/test/moduletest/weakcollectionswithsymbol/expect_output.txt new file mode 100755 index 0000000000000000000000000000000000000000..321b4cc70158057fd33451644fa20a852a22121a --- /dev/null +++ b/test/moduletest/weakcollectionswithsymbol/expect_output.txt @@ -0,0 +1,54 @@ +# Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +weakmapsymbolkey test start +TypeError +wm.has(s1) true +wm.get(s1) 1 +wm.delete(s1) true +wm.has(s1) false +wm.has(s2) false +wm.get(s2) undefined +wm.delete(s2) false +wm.get(Symbol.match) == 4 true +weakmapsymbolkey test end +weaksetsymbolvalue test start +TypeError +ws.has(sym1) true +ws.delete(sym1) true +ws.has(sym1) false +ws.has(sym2) false +ws.delete(sym2) false +ws.has(Symbol.match) true +weaksetsymbolvalue test end +weakrefforsymbol test start +TypeError +wr1.deref() == target1 true +0 wr.deref() == ctor true +1 wr.deref() == ctor true +2 wr.deref() == ctor true +3 wr.deref() == ctor true +4 wr.deref() == ctor true +5 wr.deref() == ctor true +6 wr.deref() == ctor true +7 wr.deref() == ctor true +8 wr.deref() == ctor true +9 wr.deref() == ctor true +10 wr.deref() == ctor true +11 wr.deref() == ctor true +12 wr.deref() == ctor true +weakrefforsymbol test end +finalizationregistrywithsymbol test start +TypeError +finalizationregistrywithsymbol test end +symbol 2 destroyed diff --git a/test/moduletest/weakcollectionswithsymbol/finalizationregistrywithsymbol.js b/test/moduletest/weakcollectionswithsymbol/finalizationregistrywithsymbol.js new file mode 100755 index 0000000000000000000000000000000000000000..1258736c9d8a01e4dbe11cfbaa8415a321365205 --- /dev/null +++ b/test/moduletest/weakcollectionswithsymbol/finalizationregistrywithsymbol.js @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * @tc.name:finalizationregistrywithsymbol + * @tc.desc:test FinalizationRegistry allow the use of most Symbols + * @tc.type: FUNC + * @tc.require: issueI7J2VN + */ +print("finalizationregistrywithsymbol test start"); + +var fr = new FinalizationRegistry((value) => { + print(value + " destroyed"); +}) + +let s = Symbol("symbol"); +let ss = Symbol("symbol"); +fr.register(s, "symbol 1", Symbol.match); +fr.register(ss, "symbol 2", Symbol.matchAll); + +try { + fr.register(Symbol.for("symbol2"), "symbol 2"); +} catch (err) { + print(err.name); +} + +fr.unregister(Symbol.match); +s = {}; +ss = {}; +ArkTools.forceFullGC(); +print("finalizationregistrywithsymbol test end"); \ No newline at end of file diff --git a/test/moduletest/weakcollectionswithsymbol/weakcollectionswithsymbol.js b/test/moduletest/weakcollectionswithsymbol/weakcollectionswithsymbol.js new file mode 100755 index 0000000000000000000000000000000000000000..c32225961ef6b374a893aa1759c9de4037040fa6 --- /dev/null +++ b/test/moduletest/weakcollectionswithsymbol/weakcollectionswithsymbol.js @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * @tc.name:weakcollectionswithsymbol + * @tc.desc:test weakcollectionswithsymbol + * @tc.type: FUNC + * @tc.require: issueI7J2VN + */ \ No newline at end of file diff --git a/test/moduletest/weakcollectionswithsymbol/weakmapsymbolkey.js b/test/moduletest/weakcollectionswithsymbol/weakmapsymbolkey.js new file mode 100755 index 0000000000000000000000000000000000000000..ebf3db8e30e2fc5d924bde98496aff2095306c91 --- /dev/null +++ b/test/moduletest/weakcollectionswithsymbol/weakmapsymbolkey.js @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * @tc.name:weakmapsymbolkey + * @tc.desc:test WeakMap allow the use of most Symbols as keys + * @tc.type: FUNC + * @tc.require: issueI7J2VN + */ +print("weakmapsymbolkey test start"); +let wm = new WeakMap(); +let o = {}; +wm.set(o, 0); +let s1 = Symbol("symbol1"); +wm.set(s1, 1); + +let s2 = Symbol.for("symbol2"); +try { + wm.set(s2, 2); +} catch (err) { + print(err.name); +} + +print("wm.has(s1) " + wm.has(s1)); +print("wm.get(s1) " + wm.get(s1)); +print("wm.delete(s1) " + wm.delete(s1)); +print("wm.has(s1) " + wm.has(s1)); + +print("wm.has(s2) " + wm.has(s2)); +print("wm.get(s2) " + wm.get(s2)); +print("wm.delete(s2) " + wm.delete(s2)); + +[ + Symbol.asyncIterator, + Symbol.hasInstance, + Symbol.isConcatSpreadable, + Symbol.iterator, + Symbol.match, + Symbol.matchAll, + Symbol.replace, + Symbol.search, + Symbol.species, + Symbol.split, + Symbol.toPrimitive, + Symbol.toStringTag, + Symbol.unscopables +].forEach(function (ctor, i) { + wm.set(ctor, i); +}); + +print("wm.get(Symbol.match) == 4 " + (wm.get(Symbol.match) == 4)); +print("weakmapsymbolkey test end"); \ No newline at end of file diff --git a/test/moduletest/weakcollectionswithsymbol/weakrefforsymbol.js b/test/moduletest/weakcollectionswithsymbol/weakrefforsymbol.js new file mode 100755 index 0000000000000000000000000000000000000000..d5d62859ef8a6185035e372ea92944e97da2dfab --- /dev/null +++ b/test/moduletest/weakcollectionswithsymbol/weakrefforsymbol.js @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * @tc.name:weakrefforsymbol + * @tc.desc:test WeakRef allow the use of most Symbols as targets + * @tc.type: FUNC + * @tc.require: issueI7J2VN + */ +print("weakrefforsymbol test start"); + +let target1 = Symbol("symbol1"); +let wr1 = new WeakRef(target1); + +let target2 = Symbol.for("symbol2"); +try { + let wr2 = new WeakRef(target2); +} catch (err) { + print(err.name); +} + +print("wr1.deref() == target1 " + (wr1.deref() == target1)); + +[ + Symbol.asyncIterator, + Symbol.hasInstance, + Symbol.isConcatSpreadable, + Symbol.iterator, + Symbol.match, + Symbol.matchAll, + Symbol.replace, + Symbol.search, + Symbol.species, + Symbol.split, + Symbol.toPrimitive, + Symbol.toStringTag, + Symbol.unscopables +].forEach(function (ctor, i) { + let wr = new WeakRef(ctor); + print(i + " wr.deref() == ctor " + (wr.deref() == ctor)); +}); + +print("weakrefforsymbol test end"); \ No newline at end of file diff --git a/test/moduletest/weakcollectionswithsymbol/weaksetsymbolvalue.js b/test/moduletest/weakcollectionswithsymbol/weaksetsymbolvalue.js new file mode 100755 index 0000000000000000000000000000000000000000..02a2236ad70b8e01925caa08de3fa8367cce9188 --- /dev/null +++ b/test/moduletest/weakcollectionswithsymbol/weaksetsymbolvalue.js @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * @tc.name:weaksetsymbolvalue + * @tc.desc:test WeakSet allow the use of most Symbols as values + * @tc.type: FUNC + * @tc.require: issueI7J2VN + */ +print("weaksetsymbolvalue test start"); +let ws = new WeakSet(); +let sym1 = Symbol("symbol1"); +ws.add(sym1); + +let sym2 = Symbol.for("symbol2"); +try { + ws.add(sym2); +} catch (err) { + print(err.name); +} + +print("ws.has(sym1) " + ws.has(sym1)); +print("ws.delete(sym1) " + ws.delete(sym1)); +print("ws.has(sym1) " + ws.has(sym1)); + +print("ws.has(sym2) " + ws.has(sym2)); +print("ws.delete(sym2) " + ws.delete(sym2)); + +[ + Symbol.asyncIterator, + Symbol.hasInstance, + Symbol.isConcatSpreadable, + Symbol.iterator, + Symbol.match, + Symbol.matchAll, + Symbol.replace, + Symbol.search, + Symbol.species, + Symbol.split, + Symbol.toPrimitive, + Symbol.toStringTag, + Symbol.unscopables +].forEach(function (ctor, i) { + ws.add(ctor, i); +}); + +print("ws.has(Symbol.match) " + ws.has(Symbol.match)); +print("weaksetsymbolvalue test end"); \ No newline at end of file diff --git a/test/perform/map/map.ts b/test/perform/map/map.ts new file mode 100644 index 0000000000000000000000000000000000000000..b4f7c4e5700222263628ebfe076f807131352ee2 --- /dev/null +++ b/test/perform/map/map.ts @@ -0,0 +1,136 @@ +# 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. + +declare function print(arg:any) : string; +declare interface ArkTools { + timeInUs(arg:any):number +} + +function testSet() { + let key = "key"; + let myMap = new Map(); + let start = ArkTools.timeInUs(); + for (let i = 0; i < 1_000_000; i++) { + myMap.set(key, i); + } + let end = ArkTools.timeInUs(); + let time = (end - start) / 1000 + print("Map Set:\t" + String(time) + "\tms"); +} + +function testClear() { + let myMap = new Map(); + for (let i = 0; i < 100_000; i++) { + myMap.set(i, i); + } + let start = ArkTools.timeInUs(); + for (let i = 0; i < 100_000; i++) { + myMap.clear(); + } + let end = ArkTools.timeInUs(); + let time = (end - start) / 1000 + print("Map Clear:\t" + String(time) + "\tms"); +} + +function testDelete() { + let myMap = new Map(); + let key = "key"; + myMap.set(key, key); + let start = ArkTools.timeInUs(); + for (let i = 0; i < 1_000_000; i++) { + myMap.delete(key); + } + let end = ArkTools.timeInUs(); + let time = (end - start) / 1000 + print("Map Delete:\t" + String(time) + "\tms"); +} + +function testHas() { + let myMap = new Map(); + for (let i = 0; i < 100_000; i++) { + myMap.set(i, i); + } + let start = ArkTools.timeInUs(); + for (let i = 0; i < 1_000_000; i++) { + myMap.has(i); + } + let end = ArkTools.timeInUs(); + let time = (end - start) / 1000 + print("Map Has:\t" + String(time) + "\tms"); +} + +function setElements(value, key, map) {} +function testForEach() { + let myMap = new Map(); + for (let i = 0; i < 10; i++) { + myMap.set(i, i); + } + let start = ArkTools.timeInUs(); + for (let i = 0; i < 1_000_000; i++) { + myMap.forEach(setElements); + } + let end = ArkTools.timeInUs(); + let time = (end - start) / 1000 + print("Map ForEach:\t" + String(time) + "\tms"); +} + +function testKeys() { + let myMap = new Map(); + for (let i = 0; i < 10; i++) { + myMap.set(i, i); + } + let start = ArkTools.timeInUs(); + for (let i = 0; i < 1_000_000; i++) { + myMap.keys(); + } + let end = ArkTools.timeInUs(); + let time = (end - start) / 1000 + print("Map Keys:\t" + String(time) + "\tms"); +} + +function testValues() { + let myMap = new Map(); + for (let i = 0; i < 10; i++) { + myMap.set(i, i); + } + let start = ArkTools.timeInUs(); + for (let i = 0; i < 1_000_000; i++) { + myMap.values(); + } + let end = ArkTools.timeInUs(); + let time = (end - start) / 1000 + print("Map Values:\t" + String(time) + "\tms"); +} + +function testEntries() { + let myMap = new Map(); + for (let i = 0; i < 10; i++) { + myMap.set(i, i); + } + let start = ArkTools.timeInUs(); + for (let i = 0; i < 1_000_000; i++) { + myMap.entries(); + } + let end = ArkTools.timeInUs(); + let time = (end - start) / 1000 + print("Map Entries:\t" + String(time) + "\tms"); +} + +testSet(); +testClear(); +testDelete(); +testHas(); +testForEach(); +testKeys(); +testValues(); +testEntries(); diff --git a/test/perform/number/BUILD.gn b/test/perform/number/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..633fd526107ab016ee586e115ebadf69a636dd30 --- /dev/null +++ b/test/perform/number/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +import("//arkcompiler/ets_runtime/test/test_helper.gni") + +host_moduletest_action("number") { + deps = [] +} diff --git a/test/perform/number/expect_output.txt b/test/perform/number/expect_output.txt new file mode 100644 index 0000000000000000000000000000000000000000..4d0f814fda98c789dedae02afbeb4f129ab83674 --- /dev/null +++ b/test/perform/number/expect_output.txt @@ -0,0 +1,12 @@ +# 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. diff --git a/test/perform/number/number.js b/test/perform/number/number.js new file mode 100644 index 0000000000000000000000000000000000000000..2466d304dc7891bb5573e74000b9982cb68f78bd --- /dev/null +++ b/test/perform/number/number.js @@ -0,0 +1,25 @@ +/* + * 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. + */ + +{ + var numObj = "1234"; + const time1 = Date.now() + for(var i = 0; i < 100000; ++i) { + res = Number.parseInt(numObj); + } + const time2 = Date.now() + const time3 = time2 - time1; + print("Number parseInt 1234 : " + time3); +} diff --git a/test/perform/number/stringNodeTests.sh b/test/perform/number/stringNodeTests.sh new file mode 100644 index 0000000000000000000000000000000000000000..de3aae0a3650db2a0e48f4d1435ae37fc7c3b55c --- /dev/null +++ b/test/perform/number/stringNodeTests.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# 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. + +sed -i 's/print/console.log/g' ./number.js +node number.js +sed -i 's/console.log/print/g' ./number.js diff --git a/test/perform/object/object.ts b/test/perform/object/object.ts new file mode 100644 index 0000000000000000000000000000000000000000..18452cde2bfcfa14bd274c4058c91efc110dd5b7 --- /dev/null +++ b/test/perform/object/object.ts @@ -0,0 +1,46 @@ +# 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. + +declare function print(arg:any) : string; +declare interface ArkTools { + timeInUs(arg:any):number +} + +function testToString() { + let theDog = new Dog("Gabby", "Lab"); + let res; + let start = ArkTools.timeInUs(); + for (let i = 0; i < 1_000_000; i++) { + res = theDog.toString(); + } + let end = ArkTools.timeInUs(); + let time = (end - start) / 1000 + print(res); + print("Object ToString:\t" + String(time) + "\tms"); +} + +testToString(); + +function testHasOwnProperty() { + const example = {}; + example.prop = "exists"; + let start = ArkTools.timeInUs(); + for (let i = 0; i < 1_000_000; i++) { + example.hasOwnProperty("prop"); + } + let end = ArkTools.timeInUs(); + let time = (end - start) / 1000 + print("Object HasOwnProperty:\t"+String(time)+"\tms"); +} + +testHasOwnProperty(); diff --git a/test/perform/set/set.ts b/test/perform/set/set.ts new file mode 100644 index 0000000000000000000000000000000000000000..703ab94266f2d71f7bd76a41247d8eb415efbb54 --- /dev/null +++ b/test/perform/set/set.ts @@ -0,0 +1,122 @@ +# 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. + +declare function print(arg:any) : string; +declare interface ArkTools{ + timeInUs(arg:any):number +} + +function testAdd() { + let key = "key"; + let mySet = new Set(); + let start = ArkTools.timeInUs(); + for (let i = 0; i < 1_000_000; i++) { + mySet.add(key); + } + let end = ArkTools.timeInUs(); + let time = (end - start) / 1000 + print("Set Add:\t" + String(time) + "\tms"); +} + +function testDelete() { + let key = "key"; + const mySet = new Set(); + mySet.add(key); + + let start = ArkTools.timeInUs(); + for (let i = 0; i < 1_000_000; i++) { + mySet.delete(key); + } + let end = ArkTools.timeInUs(); + let time = (end - start) / 1000 + print("Set Delete:\t" + String(time) + "\tms"); +} + +function testHas() { + const mySet = new Set(); + for (let i = 0; i < 100_000; i++) { + mySet.add(i); + } + let start = ArkTools.timeInUs(); + for (let i = 0; i < 1_000_000; i++) { + mySet.has(i); + } + let end = ArkTools.timeInUs(); + let time = (end - start) / 1000 + print("Set Has:\t" + String(time) + "\tms"); +} + +function setElements(value1, value2, set) {} +function testForEach() { + const mySet = new Set(); + for (let i = 0; i < 10; i++) { + mySet.add(i); + } + let start = ArkTools.timeInUs(); + for (let i = 0; i < 1_000_000; i++) { + mySet.forEach(setElements); + } + let end = ArkTools.timeInUs(); + let time = (end - start) / 1000 + print("Set ForEach:\t" + String(time) + "\tms"); +} + +function testClear() { + const mySet = new Set(); + for (let i = 0; i < 10; i++) { + mySet.add(i); + } + let start = ArkTools.timeInUs(); + for (let i = 0; i < 1_000_000; i++) { + mySet.clear(); + } + let end = ArkTools.timeInUs(); + let time = (end - start) / 1000 + print("Set Clear:\t" + String(time) + "\tms"); +} + +function testValues() { + const mySet = new Set(); + for (let i = 0; i < 10; i++) { + mySet.add(i); + } + let start = ArkTools.timeInUs(); + for (let i = 0; i < 1_000_000; i++) { + mySet.values(); + } + let end = ArkTools.timeInUs(); + let time = (end - start) / 1000 + print("Set Values:\t" + String(time) + "\tms"); +} + +function testEntries() { + const mySet = new Set(); + for (let i = 0; i < 10; i++) { + mySet.add(i); + } + let start = ArkTools.timeInUs(); + for (let i = 0; i < 1_000_000; i++) { + mySet.entries(); + } + let end = ArkTools.timeInUs(); + let time = (end - start) / 1000 + print("Set Entries:\t" + String(time) + "\tms"); +} + +testAdd(); +testDelete(); +testHas(); +testForEach(); +testClear(); +testValues(); +testEntries(); diff --git a/test/resource/js_runtime/ohos_test.xml b/test/resource/js_runtime/ohos_test.xml index 8d57159ce43ce1f2d78373074221b29ef1e2f078..22fef88a19070a224452e38292211c4ecbc9224c 100755 --- a/test/resource/js_runtime/ohos_test.xml +++ b/test/resource/js_runtime/ohos_test.xml @@ -128,7 +128,7 @@

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